@node9/proxy 1.33.0 → 1.34.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 +1116 -1099
- package/dist/cli.mjs +1114 -1097
- package/dist/dashboard.mjs +335 -174
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -547,9 +547,9 @@ function matchesPattern(text, patterns) {
|
|
|
547
547
|
const withoutDotSlash = text.replace(/^\.\//, "");
|
|
548
548
|
return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
|
|
549
549
|
}
|
|
550
|
-
function getNestedValue(obj,
|
|
550
|
+
function getNestedValue(obj, path10) {
|
|
551
551
|
if (!obj || typeof obj !== "object") return null;
|
|
552
|
-
const segments =
|
|
552
|
+
const segments = path10.split(".");
|
|
553
553
|
for (const seg of segments) {
|
|
554
554
|
if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
|
|
555
555
|
}
|
|
@@ -2630,13 +2630,116 @@ var init_audit = __esm({
|
|
|
2630
2630
|
});
|
|
2631
2631
|
|
|
2632
2632
|
// src/pricing/litellm.ts
|
|
2633
|
+
import fs4 from "fs";
|
|
2634
|
+
import path5 from "path";
|
|
2635
|
+
import os5 from "os";
|
|
2633
2636
|
function normalizeModel(raw) {
|
|
2634
2637
|
return raw.replace(/-\d{8}$/, "").toLowerCase();
|
|
2635
2638
|
}
|
|
2639
|
+
function readCache() {
|
|
2640
|
+
try {
|
|
2641
|
+
const raw = JSON.parse(fs4.readFileSync(CACHE_FILE(), "utf-8"));
|
|
2642
|
+
if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
|
|
2643
|
+
return null;
|
|
2644
|
+
}
|
|
2645
|
+
const ageMs = Date.now() - new Date(raw.fetchedAt).getTime();
|
|
2646
|
+
if (ageMs < 0 || ageMs > TTL_MS) return null;
|
|
2647
|
+
return raw.prices;
|
|
2648
|
+
} catch {
|
|
2649
|
+
return null;
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
function writeCache(prices) {
|
|
2653
|
+
try {
|
|
2654
|
+
const target = CACHE_FILE();
|
|
2655
|
+
const dir = path5.dirname(target);
|
|
2656
|
+
if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
|
|
2657
|
+
const tmp = target + ".tmp";
|
|
2658
|
+
const body = {
|
|
2659
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2660
|
+
prices
|
|
2661
|
+
};
|
|
2662
|
+
fs4.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
|
|
2663
|
+
fs4.renameSync(tmp, target);
|
|
2664
|
+
} catch (err) {
|
|
2665
|
+
try {
|
|
2666
|
+
fs4.appendFileSync(
|
|
2667
|
+
HOOK_DEBUG_LOG,
|
|
2668
|
+
`[pricing] cache write failed: ${err.message}
|
|
2669
|
+
`
|
|
2670
|
+
);
|
|
2671
|
+
} catch {
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
function tupleFromLiteLLM(entry) {
|
|
2676
|
+
if (!entry || typeof entry !== "object") return null;
|
|
2677
|
+
const e = entry;
|
|
2678
|
+
const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
2679
|
+
const inCost = num2(e.input_cost_per_token);
|
|
2680
|
+
const outCost = num2(e.output_cost_per_token);
|
|
2681
|
+
if (inCost === 0 && outCost === 0) return null;
|
|
2682
|
+
return [
|
|
2683
|
+
inCost,
|
|
2684
|
+
outCost,
|
|
2685
|
+
num2(e.cache_creation_input_token_cost),
|
|
2686
|
+
num2(e.cache_read_input_token_cost)
|
|
2687
|
+
];
|
|
2688
|
+
}
|
|
2689
|
+
async function fetchLiteLLMPricing() {
|
|
2690
|
+
try {
|
|
2691
|
+
const res = await fetch(LITELLM_URL, {
|
|
2692
|
+
signal: AbortSignal.timeout(15e3)
|
|
2693
|
+
});
|
|
2694
|
+
if (!res.ok) return null;
|
|
2695
|
+
const json = await res.json();
|
|
2696
|
+
if (!json || typeof json !== "object") return null;
|
|
2697
|
+
const out = {};
|
|
2698
|
+
for (const [key, value] of Object.entries(json)) {
|
|
2699
|
+
const tuple = tupleFromLiteLLM(value);
|
|
2700
|
+
if (tuple) out[key.toLowerCase()] = tuple;
|
|
2701
|
+
}
|
|
2702
|
+
if (Object.keys(out).length < 10) {
|
|
2703
|
+
return null;
|
|
2704
|
+
}
|
|
2705
|
+
return out;
|
|
2706
|
+
} catch {
|
|
2707
|
+
return null;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
async function ensurePricingLoaded() {
|
|
2711
|
+
if (memCache !== null && Date.now() - memCacheAt < TTL_MS) return;
|
|
2712
|
+
const fromDisk = readCache();
|
|
2713
|
+
if (fromDisk && Object.keys(fromDisk).length > 0) {
|
|
2714
|
+
memCache = fromDisk;
|
|
2715
|
+
memCacheAt = Date.now();
|
|
2716
|
+
lookupCache.clear();
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
const fetched = await fetchLiteLLMPricing();
|
|
2720
|
+
if (fetched && Object.keys(fetched).length > 0) {
|
|
2721
|
+
memCache = fetched;
|
|
2722
|
+
memCacheAt = Date.now();
|
|
2723
|
+
writeCache(fetched);
|
|
2724
|
+
lookupCache.clear();
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
memCache = { ...BUNDLED_PRICING };
|
|
2728
|
+
memCacheAt = Date.now();
|
|
2729
|
+
lookupCache.clear();
|
|
2730
|
+
}
|
|
2636
2731
|
function pricingFor(model) {
|
|
2637
2732
|
const norm = normalizeModel(model);
|
|
2638
2733
|
const cached = lookupCache.get(norm);
|
|
2639
2734
|
if (cached !== void 0) return cached;
|
|
2735
|
+
if (memCache === null && !diskChecked) {
|
|
2736
|
+
diskChecked = true;
|
|
2737
|
+
const disk = readCache();
|
|
2738
|
+
if (disk && Object.keys(disk).length > 0) {
|
|
2739
|
+
memCache = disk;
|
|
2740
|
+
memCacheAt = Date.now();
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2640
2743
|
const sources = [];
|
|
2641
2744
|
if (memCache) sources.push(memCache);
|
|
2642
2745
|
sources.push(BUNDLED_PRICING);
|
|
@@ -2661,11 +2764,12 @@ function pricingFor(model) {
|
|
|
2661
2764
|
lookupCache.set(norm, resolved);
|
|
2662
2765
|
return resolved;
|
|
2663
2766
|
}
|
|
2664
|
-
var BUNDLED_PRICING, TTL_MS, memCache, lookupCache;
|
|
2767
|
+
var LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, diskChecked, lookupCache;
|
|
2665
2768
|
var init_litellm = __esm({
|
|
2666
2769
|
"src/pricing/litellm.ts"() {
|
|
2667
2770
|
"use strict";
|
|
2668
2771
|
init_audit();
|
|
2772
|
+
LITELLM_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
2669
2773
|
BUNDLED_PRICING = {
|
|
2670
2774
|
// Anthropic
|
|
2671
2775
|
"claude-opus-4": [5e-6, 25e-6, 625e-8, 5e-7],
|
|
@@ -2682,33 +2786,72 @@ var init_litellm = __esm({
|
|
|
2682
2786
|
"claude-3-5-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
2683
2787
|
"claude-3-5-haiku": [8e-7, 4e-6, 1e-6, 8e-8],
|
|
2684
2788
|
"claude-3-haiku": [25e-8, 125e-8, 3e-7, 3e-8],
|
|
2685
|
-
// OpenAI
|
|
2789
|
+
// OpenAI. gpt-5 family + o-series copied from the live LiteLLM table
|
|
2790
|
+
// (verified 2026-06-14) — the bundled gpt-5 was stale at $10/$30 vs the real
|
|
2791
|
+
// $1.25/$10, and Codex models (gpt-5-codex etc.) were absent, so the offline
|
|
2792
|
+
// fallback mispriced every Codex session. See cost-codex.codexPriceFor.
|
|
2686
2793
|
"gpt-4o": [5e-6, 15e-6, 0, 25e-7],
|
|
2687
2794
|
"gpt-4o-mini": [15e-8, 6e-7, 0, 75e-9],
|
|
2688
|
-
"gpt-5": [
|
|
2689
|
-
|
|
2795
|
+
"gpt-5": [125e-8, 1e-5, 0, 125e-9],
|
|
2796
|
+
"gpt-5-codex": [125e-8, 1e-5, 0, 125e-9],
|
|
2797
|
+
"gpt-5-mini": [25e-8, 2e-6, 0, 25e-9],
|
|
2798
|
+
o3: [2e-6, 8e-6, 0, 5e-7],
|
|
2799
|
+
"o4-mini": [11e-7, 44e-7, 0, 275e-9],
|
|
2800
|
+
// Google. Values copied from the live LiteLLM table (verified 2026-06-14)
|
|
2801
|
+
// so the bundled fallback prices the current Gemini tiers correctly offline
|
|
2802
|
+
// — the local cost readers were carrying a stale hardcoded copy where
|
|
2803
|
+
// gemini-2.5-flash read $0.15/$0.60 vs the real $0.30/$2.50 (~4× under on
|
|
2804
|
+
// output). See cost-gemini.geminiPriceFor (the single Gemini price source).
|
|
2805
|
+
"gemini-2.5-pro": [125e-8, 1e-5, 0, 125e-9],
|
|
2806
|
+
"gemini-2.5-flash": [3e-7, 25e-7, 0, 3e-8],
|
|
2690
2807
|
"gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
|
|
2691
2808
|
"gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
|
|
2692
2809
|
};
|
|
2810
|
+
CACHE_FILE = () => path5.join(os5.homedir(), ".node9", "model-pricing.json");
|
|
2693
2811
|
TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2694
2812
|
memCache = null;
|
|
2813
|
+
memCacheAt = 0;
|
|
2814
|
+
diskChecked = false;
|
|
2695
2815
|
lookupCache = /* @__PURE__ */ new Map();
|
|
2696
2816
|
}
|
|
2697
2817
|
});
|
|
2698
2818
|
|
|
2699
2819
|
// src/cost-codex.ts
|
|
2820
|
+
function codexPriceFor(model) {
|
|
2821
|
+
return pricingFor(model) ?? CODEX_FALLBACK;
|
|
2822
|
+
}
|
|
2823
|
+
function codexSessionCost(model, tokens) {
|
|
2824
|
+
const nonCached = Math.max(0, tokens.input - tokens.cached);
|
|
2825
|
+
const [pin, pout, , pcr] = codexPriceFor(model || "gpt-5");
|
|
2826
|
+
return nonCached * pin + tokens.cached * pcr + tokens.output * pout;
|
|
2827
|
+
}
|
|
2828
|
+
var CODEX_FALLBACK;
|
|
2700
2829
|
var init_cost_codex = __esm({
|
|
2701
2830
|
"src/cost-codex.ts"() {
|
|
2702
2831
|
"use strict";
|
|
2703
2832
|
init_litellm();
|
|
2833
|
+
CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
|
|
2704
2834
|
}
|
|
2705
2835
|
});
|
|
2706
2836
|
|
|
2707
2837
|
// src/cost-gemini.ts
|
|
2838
|
+
function geminiPriceFor(model) {
|
|
2839
|
+
let tuple = pricingFor(model);
|
|
2840
|
+
if (!tuple && /^gemini-/i.test(model)) {
|
|
2841
|
+
for (const proxy of GEMINI_FALLBACK_MODELS) {
|
|
2842
|
+
tuple = pricingFor(proxy);
|
|
2843
|
+
if (tuple) break;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
if (!tuple) return null;
|
|
2847
|
+
return { input: tuple[0], output: tuple[1], cacheRead: tuple[3] || tuple[0] };
|
|
2848
|
+
}
|
|
2849
|
+
var GEMINI_FALLBACK_MODELS;
|
|
2708
2850
|
var init_cost_gemini = __esm({
|
|
2709
2851
|
"src/cost-gemini.ts"() {
|
|
2710
2852
|
"use strict";
|
|
2711
2853
|
init_litellm();
|
|
2854
|
+
GEMINI_FALLBACK_MODELS = ["gemini-2.5-flash", "gemini-2.0-flash"];
|
|
2712
2855
|
}
|
|
2713
2856
|
});
|
|
2714
2857
|
|
|
@@ -2750,9 +2893,9 @@ var init_scan_watermark = __esm({
|
|
|
2750
2893
|
});
|
|
2751
2894
|
|
|
2752
2895
|
// src/cli/aggregate/report-audit.ts
|
|
2753
|
-
import
|
|
2754
|
-
import
|
|
2755
|
-
import
|
|
2896
|
+
import fs5 from "fs";
|
|
2897
|
+
import os6 from "os";
|
|
2898
|
+
import path6 from "path";
|
|
2756
2899
|
function buildTestTimestamps(allEntries) {
|
|
2757
2900
|
const testTs = /* @__PURE__ */ new Set();
|
|
2758
2901
|
for (const e of allEntries) {
|
|
@@ -2833,8 +2976,8 @@ function getDateRange(period, now) {
|
|
|
2833
2976
|
}
|
|
2834
2977
|
}
|
|
2835
2978
|
function parseAuditLog(logPath) {
|
|
2836
|
-
if (!
|
|
2837
|
-
const raw =
|
|
2979
|
+
if (!fs5.existsSync(logPath)) return [];
|
|
2980
|
+
const raw = fs5.readFileSync(logPath, "utf-8");
|
|
2838
2981
|
return raw.split("\n").flatMap((line) => {
|
|
2839
2982
|
if (!line.trim()) return [];
|
|
2840
2983
|
try {
|
|
@@ -2851,11 +2994,10 @@ function isDlp(checkedBy) {
|
|
|
2851
2994
|
return !!checkedBy?.includes("dlp");
|
|
2852
2995
|
}
|
|
2853
2996
|
function claudeModelPrice(model) {
|
|
2854
|
-
const
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
}
|
|
2858
|
-
return null;
|
|
2997
|
+
const t = pricingFor(model);
|
|
2998
|
+
if (!t) return null;
|
|
2999
|
+
const [i, o, cw, cr] = t;
|
|
3000
|
+
return { i, o, cw, cr };
|
|
2859
3001
|
}
|
|
2860
3002
|
function emptyClaudeCostAccumulator() {
|
|
2861
3003
|
return {
|
|
@@ -2882,25 +3024,25 @@ function freezeClaudeCost(acc) {
|
|
|
2882
3024
|
};
|
|
2883
3025
|
}
|
|
2884
3026
|
function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
2885
|
-
const projPath =
|
|
3027
|
+
const projPath = path6.join(projectsDir, proj);
|
|
2886
3028
|
let files;
|
|
2887
3029
|
try {
|
|
2888
|
-
const stat =
|
|
3030
|
+
const stat = fs5.statSync(projPath);
|
|
2889
3031
|
if (!stat.isDirectory()) return;
|
|
2890
|
-
files =
|
|
3032
|
+
files = fs5.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
2891
3033
|
} catch {
|
|
2892
3034
|
return;
|
|
2893
3035
|
}
|
|
2894
3036
|
const startMs = start.getTime();
|
|
2895
3037
|
for (const file of files) {
|
|
2896
|
-
const filePath =
|
|
3038
|
+
const filePath = path6.join(projPath, file);
|
|
2897
3039
|
try {
|
|
2898
|
-
if (
|
|
3040
|
+
if (fs5.statSync(filePath).mtimeMs < startMs) continue;
|
|
2899
3041
|
} catch {
|
|
2900
3042
|
continue;
|
|
2901
3043
|
}
|
|
2902
3044
|
try {
|
|
2903
|
-
const raw =
|
|
3045
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
2904
3046
|
for (const line of raw.split("\n")) {
|
|
2905
3047
|
if (!line.trim()) continue;
|
|
2906
3048
|
let entry;
|
|
@@ -2950,10 +3092,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
|
|
|
2950
3092
|
}
|
|
2951
3093
|
function loadClaudeCost(start, end, projectsDir) {
|
|
2952
3094
|
const acc = emptyClaudeCostAccumulator();
|
|
2953
|
-
if (!
|
|
3095
|
+
if (!fs5.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
2954
3096
|
let dirs;
|
|
2955
3097
|
try {
|
|
2956
|
-
dirs =
|
|
3098
|
+
dirs = fs5.readdirSync(projectsDir);
|
|
2957
3099
|
} catch {
|
|
2958
3100
|
return freezeClaudeCost(acc);
|
|
2959
3101
|
}
|
|
@@ -2964,10 +3106,10 @@ function loadClaudeCost(start, end, projectsDir) {
|
|
|
2964
3106
|
}
|
|
2965
3107
|
async function loadClaudeCostAsync(start, end, projectsDir) {
|
|
2966
3108
|
const acc = emptyClaudeCostAccumulator();
|
|
2967
|
-
if (!
|
|
3109
|
+
if (!fs5.existsSync(projectsDir)) return freezeClaudeCost(acc);
|
|
2968
3110
|
let dirs;
|
|
2969
3111
|
try {
|
|
2970
|
-
dirs =
|
|
3112
|
+
dirs = fs5.readdirSync(projectsDir);
|
|
2971
3113
|
} catch {
|
|
2972
3114
|
return freezeClaudeCost(acc);
|
|
2973
3115
|
}
|
|
@@ -2980,11 +3122,12 @@ async function loadClaudeCostAsync(start, end, projectsDir) {
|
|
|
2980
3122
|
function processCodexCostFile(filePath, start, end, acc) {
|
|
2981
3123
|
let lines;
|
|
2982
3124
|
try {
|
|
2983
|
-
lines =
|
|
3125
|
+
lines = fs5.readFileSync(filePath, "utf-8").split("\n");
|
|
2984
3126
|
} catch {
|
|
2985
3127
|
return;
|
|
2986
3128
|
}
|
|
2987
3129
|
let sessionStart = "";
|
|
3130
|
+
let model = "";
|
|
2988
3131
|
let lastTotalInput = 0;
|
|
2989
3132
|
let lastTotalCached = 0;
|
|
2990
3133
|
let lastTotalOutput = 0;
|
|
@@ -3002,6 +3145,10 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
3002
3145
|
sessionStart = String(p["timestamp"] ?? "");
|
|
3003
3146
|
continue;
|
|
3004
3147
|
}
|
|
3148
|
+
if (entry.type === "turn_context" && typeof p["model"] === "string") {
|
|
3149
|
+
model = p["model"];
|
|
3150
|
+
continue;
|
|
3151
|
+
}
|
|
3005
3152
|
if (entry.type === "event_msg" && p["type"] === "token_count") {
|
|
3006
3153
|
const info = p["info"] ?? {};
|
|
3007
3154
|
const usage = info["total_token_usage"] ?? {};
|
|
@@ -3016,40 +3163,45 @@ function processCodexCostFile(filePath, start, end, acc) {
|
|
|
3016
3163
|
if (!sessionStart) return;
|
|
3017
3164
|
const ts = new Date(sessionStart);
|
|
3018
3165
|
if (ts < start || ts > end) return;
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3166
|
+
const cost = codexSessionCost(model, {
|
|
3167
|
+
input: lastTotalInput,
|
|
3168
|
+
cached: lastTotalCached,
|
|
3169
|
+
output: lastTotalOutput
|
|
3170
|
+
});
|
|
3021
3171
|
acc.total += cost;
|
|
3022
3172
|
acc.toolCalls += sessionToolCalls;
|
|
3023
3173
|
const dateKey = sessionStart.slice(0, 10);
|
|
3024
3174
|
acc.byDay.set(dateKey, (acc.byDay.get(dateKey) ?? 0) + cost);
|
|
3175
|
+
const normModel = normalizeModel(model || "gpt-5");
|
|
3176
|
+
acc.byModel.set(normModel, (acc.byModel.get(normModel) ?? 0) + cost);
|
|
3025
3177
|
}
|
|
3026
3178
|
function listCodexSessionFiles(sessionsBase) {
|
|
3027
3179
|
const jsonlFiles = [];
|
|
3028
|
-
if (!
|
|
3180
|
+
if (!fs5.existsSync(sessionsBase)) return jsonlFiles;
|
|
3029
3181
|
try {
|
|
3030
|
-
for (const year of
|
|
3031
|
-
const yearPath =
|
|
3182
|
+
for (const year of fs5.readdirSync(sessionsBase)) {
|
|
3183
|
+
const yearPath = path6.join(sessionsBase, year);
|
|
3032
3184
|
try {
|
|
3033
|
-
if (!
|
|
3185
|
+
if (!fs5.statSync(yearPath).isDirectory()) continue;
|
|
3034
3186
|
} catch {
|
|
3035
3187
|
continue;
|
|
3036
3188
|
}
|
|
3037
|
-
for (const month of
|
|
3038
|
-
const monthPath =
|
|
3189
|
+
for (const month of fs5.readdirSync(yearPath)) {
|
|
3190
|
+
const monthPath = path6.join(yearPath, month);
|
|
3039
3191
|
try {
|
|
3040
|
-
if (!
|
|
3192
|
+
if (!fs5.statSync(monthPath).isDirectory()) continue;
|
|
3041
3193
|
} catch {
|
|
3042
3194
|
continue;
|
|
3043
3195
|
}
|
|
3044
|
-
for (const day of
|
|
3045
|
-
const dayPath =
|
|
3196
|
+
for (const day of fs5.readdirSync(monthPath)) {
|
|
3197
|
+
const dayPath = path6.join(monthPath, day);
|
|
3046
3198
|
try {
|
|
3047
|
-
if (!
|
|
3199
|
+
if (!fs5.statSync(dayPath).isDirectory()) continue;
|
|
3048
3200
|
} catch {
|
|
3049
3201
|
continue;
|
|
3050
3202
|
}
|
|
3051
|
-
for (const file of
|
|
3052
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
3203
|
+
for (const file of fs5.readdirSync(dayPath)) {
|
|
3204
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path6.join(dayPath, file));
|
|
3053
3205
|
}
|
|
3054
3206
|
}
|
|
3055
3207
|
}
|
|
@@ -3059,16 +3211,33 @@ function listCodexSessionFiles(sessionsBase) {
|
|
|
3059
3211
|
}
|
|
3060
3212
|
return jsonlFiles;
|
|
3061
3213
|
}
|
|
3214
|
+
function mergeByModel(...maps) {
|
|
3215
|
+
const out = /* @__PURE__ */ new Map();
|
|
3216
|
+
for (const m of maps) {
|
|
3217
|
+
for (const [k, v] of m) out.set(k, (out.get(k) ?? 0) + v);
|
|
3218
|
+
}
|
|
3219
|
+
return out;
|
|
3220
|
+
}
|
|
3062
3221
|
function loadCodexCost(start, end, sessionsBase) {
|
|
3063
|
-
const acc = {
|
|
3222
|
+
const acc = {
|
|
3223
|
+
total: 0,
|
|
3224
|
+
toolCalls: 0,
|
|
3225
|
+
byDay: /* @__PURE__ */ new Map(),
|
|
3226
|
+
byModel: /* @__PURE__ */ new Map()
|
|
3227
|
+
};
|
|
3064
3228
|
const files = listCodexSessionFiles(sessionsBase);
|
|
3065
3229
|
for (const filePath of files) {
|
|
3066
3230
|
processCodexCostFile(filePath, start, end, acc);
|
|
3067
3231
|
}
|
|
3068
|
-
return { total: acc.total, byDay: acc.byDay, toolCalls: acc.toolCalls };
|
|
3232
|
+
return { total: acc.total, byDay: acc.byDay, byModel: acc.byModel, toolCalls: acc.toolCalls };
|
|
3069
3233
|
}
|
|
3070
3234
|
async function loadCodexCostAsync(start, end, sessionsBase) {
|
|
3071
|
-
const acc = {
|
|
3235
|
+
const acc = {
|
|
3236
|
+
total: 0,
|
|
3237
|
+
toolCalls: 0,
|
|
3238
|
+
byDay: /* @__PURE__ */ new Map(),
|
|
3239
|
+
byModel: /* @__PURE__ */ new Map()
|
|
3240
|
+
};
|
|
3072
3241
|
const files = listCodexSessionFiles(sessionsBase);
|
|
3073
3242
|
const CHUNK_SIZE = 5;
|
|
3074
3243
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -3077,12 +3246,12 @@ async function loadCodexCostAsync(start, end, sessionsBase) {
|
|
|
3077
3246
|
await new Promise((resolve) => setImmediate(resolve));
|
|
3078
3247
|
}
|
|
3079
3248
|
}
|
|
3080
|
-
return { total: acc.total, byDay: acc.byDay, toolCalls: acc.toolCalls };
|
|
3249
|
+
return { total: acc.total, byDay: acc.byDay, byModel: acc.byModel, toolCalls: acc.toolCalls };
|
|
3081
3250
|
}
|
|
3082
|
-
function
|
|
3251
|
+
function geminiPriceFor2(model) {
|
|
3083
3252
|
let tuple = pricingFor(model);
|
|
3084
3253
|
if (!tuple && /^gemini-/i.test(model)) {
|
|
3085
|
-
for (const proxy of
|
|
3254
|
+
for (const proxy of GEMINI_FALLBACK_MODELS2) {
|
|
3086
3255
|
tuple = pricingFor(proxy);
|
|
3087
3256
|
if (tuple) break;
|
|
3088
3257
|
}
|
|
@@ -3113,13 +3282,13 @@ function freezeGeminiCost(acc) {
|
|
|
3113
3282
|
function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
3114
3283
|
const startMs = start.getTime();
|
|
3115
3284
|
try {
|
|
3116
|
-
if (
|
|
3285
|
+
if (fs5.statSync(filePath).mtimeMs < startMs) return;
|
|
3117
3286
|
} catch {
|
|
3118
3287
|
return;
|
|
3119
3288
|
}
|
|
3120
3289
|
let raw;
|
|
3121
3290
|
try {
|
|
3122
|
-
raw =
|
|
3291
|
+
raw = fs5.readFileSync(filePath, "utf-8");
|
|
3123
3292
|
} catch {
|
|
3124
3293
|
return;
|
|
3125
3294
|
}
|
|
@@ -3140,7 +3309,7 @@ function processGeminiCostFile(filePath, projectKey, start, end, acc) {
|
|
|
3140
3309
|
}
|
|
3141
3310
|
const ts = new Date(entry.timestamp);
|
|
3142
3311
|
if (ts < start || ts > end) continue;
|
|
3143
|
-
const price =
|
|
3312
|
+
const price = geminiPriceFor2(entry.model);
|
|
3144
3313
|
if (!price) continue;
|
|
3145
3314
|
const inp = entry.tokens.input ?? 0;
|
|
3146
3315
|
const out = entry.tokens.output ?? 0;
|
|
@@ -3168,30 +3337,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
|
|
|
3168
3337
|
const out = [];
|
|
3169
3338
|
let dirs;
|
|
3170
3339
|
try {
|
|
3171
|
-
if (!
|
|
3172
|
-
dirs =
|
|
3340
|
+
if (!fs5.statSync(geminiTmpDir).isDirectory()) return out;
|
|
3341
|
+
dirs = fs5.readdirSync(geminiTmpDir);
|
|
3173
3342
|
} catch {
|
|
3174
3343
|
return out;
|
|
3175
3344
|
}
|
|
3176
3345
|
for (const proj of dirs) {
|
|
3177
|
-
const chatsDir =
|
|
3346
|
+
const chatsDir = path6.join(geminiTmpDir, proj, "chats");
|
|
3178
3347
|
let files;
|
|
3179
3348
|
try {
|
|
3180
|
-
if (!
|
|
3181
|
-
files =
|
|
3349
|
+
if (!fs5.statSync(chatsDir).isDirectory()) continue;
|
|
3350
|
+
files = fs5.readdirSync(chatsDir);
|
|
3182
3351
|
} catch {
|
|
3183
3352
|
continue;
|
|
3184
3353
|
}
|
|
3185
3354
|
for (const f of files) {
|
|
3186
3355
|
if (!f.endsWith(".jsonl")) continue;
|
|
3187
|
-
out.push({ projectKey: proj, file:
|
|
3356
|
+
out.push({ projectKey: proj, file: path6.join(chatsDir, f) });
|
|
3188
3357
|
}
|
|
3189
3358
|
}
|
|
3190
3359
|
return out;
|
|
3191
3360
|
}
|
|
3192
3361
|
function loadGeminiCost(start, end, geminiTmpDir) {
|
|
3193
3362
|
const acc = emptyGeminiAccumulator();
|
|
3194
|
-
if (!
|
|
3363
|
+
if (!fs5.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
|
|
3195
3364
|
for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
|
|
3196
3365
|
processGeminiCostFile(file, projectKey, start, end, acc);
|
|
3197
3366
|
}
|
|
@@ -3199,7 +3368,7 @@ function loadGeminiCost(start, end, geminiTmpDir) {
|
|
|
3199
3368
|
}
|
|
3200
3369
|
async function loadGeminiCostAsync(start, end, geminiTmpDir) {
|
|
3201
3370
|
const acc = emptyGeminiAccumulator();
|
|
3202
|
-
if (!
|
|
3371
|
+
if (!fs5.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
|
|
3203
3372
|
const files = listGeminiSessionFiles(geminiTmpDir);
|
|
3204
3373
|
const CHUNK_SIZE = 5;
|
|
3205
3374
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -3212,11 +3381,11 @@ async function loadGeminiCostAsync(start, end, geminiTmpDir) {
|
|
|
3212
3381
|
}
|
|
3213
3382
|
function aggregateReportFromAudit(period, opts = {}) {
|
|
3214
3383
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
3215
|
-
const auditLogPath2 = opts.auditLogPath ??
|
|
3216
|
-
const claudeProjectsDir = opts.claudeProjectsDir ??
|
|
3217
|
-
const codexSessionsDir = opts.codexSessionsDir ??
|
|
3218
|
-
const geminiTmpDir = opts.geminiTmpDir ??
|
|
3219
|
-
const hasAuditFile =
|
|
3384
|
+
const auditLogPath2 = opts.auditLogPath ?? path6.join(os6.homedir(), ".node9", "audit.log");
|
|
3385
|
+
const claudeProjectsDir = opts.claudeProjectsDir ?? path6.join(os6.homedir(), ".claude", "projects");
|
|
3386
|
+
const codexSessionsDir = opts.codexSessionsDir ?? path6.join(os6.homedir(), ".codex", "sessions");
|
|
3387
|
+
const geminiTmpDir = opts.geminiTmpDir ?? path6.join(os6.homedir(), ".gemini", "tmp");
|
|
3388
|
+
const hasAuditFile = fs5.existsSync(auditLogPath2);
|
|
3220
3389
|
const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath2);
|
|
3221
3390
|
const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
|
|
3222
3391
|
const { start, end } = getDateRange(period, now);
|
|
@@ -3378,7 +3547,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
3378
3547
|
cacheWriteTokens: claudeCost.cacheWriteTokens,
|
|
3379
3548
|
cacheReadTokens: claudeCost.cacheReadTokens + geminiCost.cacheReadTokens,
|
|
3380
3549
|
byDay: claudeCost.byDay,
|
|
3381
|
-
byModel: claudeCost.byModel,
|
|
3550
|
+
byModel: mergeByModel(claudeCost.byModel, codexCost.byModel),
|
|
3382
3551
|
byProject: claudeCost.byProject
|
|
3383
3552
|
},
|
|
3384
3553
|
toolMap,
|
|
@@ -3392,43 +3561,32 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
3392
3561
|
};
|
|
3393
3562
|
return { data, hasAuditFile, responseDlpEntries };
|
|
3394
3563
|
}
|
|
3395
|
-
var TEST_COMMAND_RE, SUPERSEDE_WINDOW_MS,
|
|
3564
|
+
var TEST_COMMAND_RE, SUPERSEDE_WINDOW_MS, GEMINI_FALLBACK_MODELS2;
|
|
3396
3565
|
var init_report_audit = __esm({
|
|
3397
3566
|
"src/cli/aggregate/report-audit.ts"() {
|
|
3398
3567
|
"use strict";
|
|
3399
3568
|
init_costSync();
|
|
3400
3569
|
init_litellm();
|
|
3570
|
+
init_cost_codex();
|
|
3401
3571
|
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;
|
|
3402
3572
|
SUPERSEDE_WINDOW_MS = 6e4;
|
|
3403
|
-
|
|
3404
|
-
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
3405
|
-
"claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
3406
|
-
"claude-opus-4": { i: 15e-6, o: 75e-6, cw: 1875e-8, cr: 15e-7 },
|
|
3407
|
-
"claude-sonnet-4-6": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
3408
|
-
"claude-sonnet-4-5": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
3409
|
-
"claude-sonnet-4": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
3410
|
-
"claude-3-7-sonnet": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
3411
|
-
"claude-3-5-sonnet": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
3412
|
-
"claude-haiku-4-5": { i: 1e-6, o: 5e-6, cw: 125e-8, cr: 1e-7 },
|
|
3413
|
-
"claude-3-5-haiku": { i: 8e-7, o: 4e-6, cw: 1e-6, cr: 8e-8 }
|
|
3414
|
-
};
|
|
3415
|
-
GEMINI_FALLBACK_MODELS = ["gemini-2.5-flash", "gemini-2.0-flash"];
|
|
3573
|
+
GEMINI_FALLBACK_MODELS2 = ["gemini-2.5-flash", "gemini-2.0-flash"];
|
|
3416
3574
|
}
|
|
3417
3575
|
});
|
|
3418
3576
|
|
|
3419
3577
|
// src/utils/provenance.ts
|
|
3420
|
-
import
|
|
3421
|
-
import
|
|
3578
|
+
import path7 from "path";
|
|
3579
|
+
import os7 from "os";
|
|
3422
3580
|
var USER_PREFIXES;
|
|
3423
3581
|
var init_provenance = __esm({
|
|
3424
3582
|
"src/utils/provenance.ts"() {
|
|
3425
3583
|
"use strict";
|
|
3426
3584
|
USER_PREFIXES = [
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3585
|
+
path7.join(os7.homedir(), "bin"),
|
|
3586
|
+
path7.join(os7.homedir(), ".local", "bin"),
|
|
3587
|
+
path7.join(os7.homedir(), ".cargo", "bin"),
|
|
3588
|
+
path7.join(os7.homedir(), ".npm-global", "bin"),
|
|
3589
|
+
path7.join(os7.homedir(), ".volta", "bin")
|
|
3432
3590
|
];
|
|
3433
3591
|
}
|
|
3434
3592
|
});
|
|
@@ -3532,24 +3690,20 @@ var init_scan_history = __esm({
|
|
|
3532
3690
|
|
|
3533
3691
|
// src/cli/commands/scan.ts
|
|
3534
3692
|
import chalk4 from "chalk";
|
|
3535
|
-
import
|
|
3536
|
-
import
|
|
3537
|
-
import
|
|
3693
|
+
import fs6 from "fs";
|
|
3694
|
+
import path8 from "path";
|
|
3695
|
+
import os8 from "os";
|
|
3538
3696
|
import stringWidth2 from "string-width";
|
|
3539
3697
|
function claudeModelPrice2(model) {
|
|
3540
|
-
const
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
}
|
|
3544
|
-
return null;
|
|
3698
|
+
const t = pricingFor(model);
|
|
3699
|
+
if (!t) return null;
|
|
3700
|
+
const [i, o, cw, cr] = t;
|
|
3701
|
+
return { i, o, cw, cr };
|
|
3545
3702
|
}
|
|
3546
3703
|
function geminiModelPrice(model) {
|
|
3547
|
-
const
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
}
|
|
3551
|
-
if (base.includes("flash")) return GEMINI_PRICING["gemini-2.0-flash"];
|
|
3552
|
-
return null;
|
|
3704
|
+
const p = geminiPriceFor(model);
|
|
3705
|
+
if (!p) return null;
|
|
3706
|
+
return { i: p.input, o: p.output, cr: p.cacheRead };
|
|
3553
3707
|
}
|
|
3554
3708
|
function isNode9SelfOutput(text) {
|
|
3555
3709
|
let hits = 0;
|
|
@@ -3687,7 +3841,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
3687
3841
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
3688
3842
|
let raw;
|
|
3689
3843
|
try {
|
|
3690
|
-
raw =
|
|
3844
|
+
raw = fs6.readFileSync(path8.join(projPath, file), "utf-8");
|
|
3691
3845
|
} catch {
|
|
3692
3846
|
return;
|
|
3693
3847
|
}
|
|
@@ -3739,7 +3893,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
3739
3893
|
if (block.type !== "tool_result") continue;
|
|
3740
3894
|
const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
|
|
3741
3895
|
if (filePath) {
|
|
3742
|
-
const ext =
|
|
3896
|
+
const ext = path8.extname(filePath).toLowerCase();
|
|
3743
3897
|
if (CODE_EXTENSIONS.has(ext)) continue;
|
|
3744
3898
|
}
|
|
3745
3899
|
const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
|
|
@@ -3796,7 +3950,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
3796
3950
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
3797
3951
|
if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
|
|
3798
3952
|
const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
3799
|
-
const inputFileExt = inputFilePath ?
|
|
3953
|
+
const inputFileExt = inputFilePath ? path8.extname(inputFilePath).toLowerCase() : "";
|
|
3800
3954
|
if (CODE_EXTENSIONS.has(inputFileExt)) continue;
|
|
3801
3955
|
const dlpMatch = scanArgs(input);
|
|
3802
3956
|
if (dlpMatch) {
|
|
@@ -3893,19 +4047,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
3893
4047
|
}
|
|
3894
4048
|
}
|
|
3895
4049
|
async function processClaudeProjectAsync(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
3896
|
-
const projPath =
|
|
4050
|
+
const projPath = path8.join(projectsDir, proj);
|
|
3897
4051
|
try {
|
|
3898
|
-
if (!
|
|
4052
|
+
if (!fs6.statSync(projPath).isDirectory()) return;
|
|
3899
4053
|
} catch {
|
|
3900
4054
|
return;
|
|
3901
4055
|
}
|
|
3902
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
4056
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os8.homedir(), "~")).slice(
|
|
3903
4057
|
0,
|
|
3904
4058
|
40
|
|
3905
4059
|
);
|
|
3906
4060
|
let files;
|
|
3907
4061
|
try {
|
|
3908
|
-
files =
|
|
4062
|
+
files = fs6.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
|
|
3909
4063
|
} catch {
|
|
3910
4064
|
return;
|
|
3911
4065
|
}
|
|
@@ -3943,12 +4097,12 @@ function emptyClaudeScan() {
|
|
|
3943
4097
|
};
|
|
3944
4098
|
}
|
|
3945
4099
|
async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
|
|
3946
|
-
const projectsDir =
|
|
4100
|
+
const projectsDir = path8.join(os8.homedir(), ".claude", "projects");
|
|
3947
4101
|
const result = emptyClaudeScan();
|
|
3948
|
-
if (!
|
|
4102
|
+
if (!fs6.existsSync(projectsDir)) return result;
|
|
3949
4103
|
let projDirs;
|
|
3950
4104
|
try {
|
|
3951
|
-
projDirs =
|
|
4105
|
+
projDirs = fs6.readdirSync(projectsDir);
|
|
3952
4106
|
} catch {
|
|
3953
4107
|
return result;
|
|
3954
4108
|
}
|
|
@@ -3969,7 +4123,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
|
|
|
3969
4123
|
return result;
|
|
3970
4124
|
}
|
|
3971
4125
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
3972
|
-
const tmpDir =
|
|
4126
|
+
const tmpDir = path8.join(os8.homedir(), ".gemini", "tmp");
|
|
3973
4127
|
const result = {
|
|
3974
4128
|
filesScanned: 0,
|
|
3975
4129
|
sessions: 0,
|
|
@@ -3984,50 +4138,64 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
3984
4138
|
sessionsWithEarlySecrets: 0
|
|
3985
4139
|
};
|
|
3986
4140
|
const dedup = emptyScanDedup();
|
|
3987
|
-
if (!
|
|
4141
|
+
if (!fs6.existsSync(tmpDir)) return result;
|
|
3988
4142
|
let slugDirs;
|
|
3989
4143
|
try {
|
|
3990
|
-
slugDirs =
|
|
4144
|
+
slugDirs = fs6.readdirSync(tmpDir);
|
|
3991
4145
|
} catch {
|
|
3992
4146
|
return result;
|
|
3993
4147
|
}
|
|
3994
4148
|
const ruleSources = buildRuleSources();
|
|
3995
4149
|
for (const slug of slugDirs) {
|
|
3996
|
-
const slugPath =
|
|
4150
|
+
const slugPath = path8.join(tmpDir, slug);
|
|
3997
4151
|
try {
|
|
3998
|
-
if (!
|
|
4152
|
+
if (!fs6.statSync(slugPath).isDirectory()) continue;
|
|
3999
4153
|
} catch {
|
|
4000
4154
|
continue;
|
|
4001
4155
|
}
|
|
4002
4156
|
let projLabel = stripTerminalEscapes(slug).slice(0, 40);
|
|
4003
4157
|
try {
|
|
4004
4158
|
projLabel = stripTerminalEscapes(
|
|
4005
|
-
|
|
4006
|
-
).replace(
|
|
4159
|
+
fs6.readFileSync(path8.join(slugPath, ".project_root"), "utf-8").trim()
|
|
4160
|
+
).replace(os8.homedir(), "~").slice(0, 40);
|
|
4007
4161
|
} catch {
|
|
4008
4162
|
}
|
|
4009
|
-
const chatsDir =
|
|
4010
|
-
if (!
|
|
4163
|
+
const chatsDir = path8.join(slugPath, "chats");
|
|
4164
|
+
if (!fs6.existsSync(chatsDir)) continue;
|
|
4011
4165
|
let chatFiles;
|
|
4012
4166
|
try {
|
|
4013
|
-
chatFiles =
|
|
4167
|
+
chatFiles = fs6.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
|
|
4014
4168
|
} catch {
|
|
4015
4169
|
continue;
|
|
4016
4170
|
}
|
|
4171
|
+
const seenSessions = /* @__PURE__ */ new Set();
|
|
4017
4172
|
for (const chatFile of chatFiles) {
|
|
4173
|
+
const sessionId = chatFile.replace(/\.jsonl?$/, "");
|
|
4174
|
+
if (seenSessions.has(sessionId)) continue;
|
|
4175
|
+
seenSessions.add(sessionId);
|
|
4018
4176
|
result.filesScanned++;
|
|
4019
4177
|
onProgress?.(result.filesScanned);
|
|
4020
|
-
const sessionId = chatFile.replace(/\.json$/, "");
|
|
4021
4178
|
let raw;
|
|
4022
4179
|
try {
|
|
4023
|
-
raw =
|
|
4180
|
+
raw = fs6.readFileSync(path8.join(chatsDir, chatFile), "utf-8");
|
|
4024
4181
|
} catch {
|
|
4025
4182
|
continue;
|
|
4026
4183
|
}
|
|
4027
4184
|
const sessionCalls = [];
|
|
4028
4185
|
let session;
|
|
4029
4186
|
try {
|
|
4030
|
-
|
|
4187
|
+
if (chatFile.endsWith(".jsonl")) {
|
|
4188
|
+
const messages = raw.split("\n").filter((l) => l.trim()).map((l) => {
|
|
4189
|
+
try {
|
|
4190
|
+
return JSON.parse(l);
|
|
4191
|
+
} catch {
|
|
4192
|
+
return null;
|
|
4193
|
+
}
|
|
4194
|
+
}).filter((m) => m !== null);
|
|
4195
|
+
session = { messages };
|
|
4196
|
+
} else {
|
|
4197
|
+
session = JSON.parse(raw);
|
|
4198
|
+
}
|
|
4031
4199
|
} catch {
|
|
4032
4200
|
continue;
|
|
4033
4201
|
}
|
|
@@ -4182,7 +4350,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
4182
4350
|
return result;
|
|
4183
4351
|
}
|
|
4184
4352
|
function scanCodexHistory(startDate, onProgress, onLine) {
|
|
4185
|
-
const sessionsBase =
|
|
4353
|
+
const sessionsBase = path8.join(os8.homedir(), ".codex", "sessions");
|
|
4186
4354
|
const result = {
|
|
4187
4355
|
filesScanned: 0,
|
|
4188
4356
|
sessions: 0,
|
|
@@ -4197,32 +4365,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
4197
4365
|
sessionsWithEarlySecrets: 0
|
|
4198
4366
|
};
|
|
4199
4367
|
const dedup = emptyScanDedup();
|
|
4200
|
-
if (!
|
|
4368
|
+
if (!fs6.existsSync(sessionsBase)) return result;
|
|
4201
4369
|
const jsonlFiles = [];
|
|
4202
4370
|
try {
|
|
4203
|
-
for (const year of
|
|
4204
|
-
const yearPath =
|
|
4371
|
+
for (const year of fs6.readdirSync(sessionsBase)) {
|
|
4372
|
+
const yearPath = path8.join(sessionsBase, year);
|
|
4205
4373
|
try {
|
|
4206
|
-
if (!
|
|
4374
|
+
if (!fs6.statSync(yearPath).isDirectory()) continue;
|
|
4207
4375
|
} catch {
|
|
4208
4376
|
continue;
|
|
4209
4377
|
}
|
|
4210
|
-
for (const month of
|
|
4211
|
-
const monthPath =
|
|
4378
|
+
for (const month of fs6.readdirSync(yearPath)) {
|
|
4379
|
+
const monthPath = path8.join(yearPath, month);
|
|
4212
4380
|
try {
|
|
4213
|
-
if (!
|
|
4381
|
+
if (!fs6.statSync(monthPath).isDirectory()) continue;
|
|
4214
4382
|
} catch {
|
|
4215
4383
|
continue;
|
|
4216
4384
|
}
|
|
4217
|
-
for (const day of
|
|
4218
|
-
const dayPath =
|
|
4385
|
+
for (const day of fs6.readdirSync(monthPath)) {
|
|
4386
|
+
const dayPath = path8.join(monthPath, day);
|
|
4219
4387
|
try {
|
|
4220
|
-
if (!
|
|
4388
|
+
if (!fs6.statSync(dayPath).isDirectory()) continue;
|
|
4221
4389
|
} catch {
|
|
4222
4390
|
continue;
|
|
4223
4391
|
}
|
|
4224
|
-
for (const file of
|
|
4225
|
-
if (file.endsWith(".jsonl")) jsonlFiles.push(
|
|
4392
|
+
for (const file of fs6.readdirSync(dayPath)) {
|
|
4393
|
+
if (file.endsWith(".jsonl")) jsonlFiles.push(path8.join(dayPath, file));
|
|
4226
4394
|
}
|
|
4227
4395
|
}
|
|
4228
4396
|
}
|
|
@@ -4236,7 +4404,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
4236
4404
|
onProgress?.(result.filesScanned);
|
|
4237
4405
|
let lines;
|
|
4238
4406
|
try {
|
|
4239
|
-
lines =
|
|
4407
|
+
lines = fs6.readFileSync(filePath, "utf-8").split("\n");
|
|
4240
4408
|
} catch {
|
|
4241
4409
|
continue;
|
|
4242
4410
|
}
|
|
@@ -4248,6 +4416,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
4248
4416
|
let lastTotalInput = 0;
|
|
4249
4417
|
let lastTotalCached = 0;
|
|
4250
4418
|
let lastTotalOutput = 0;
|
|
4419
|
+
let model = "";
|
|
4251
4420
|
for (const line of lines) {
|
|
4252
4421
|
if (!line.trim()) continue;
|
|
4253
4422
|
onLine?.();
|
|
@@ -4262,7 +4431,11 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
4262
4431
|
sessionId = String(payload["id"] ?? filePath);
|
|
4263
4432
|
startTime = String(payload["timestamp"] ?? "");
|
|
4264
4433
|
const cwd = String(payload["cwd"] ?? "");
|
|
4265
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
4434
|
+
projLabel = stripTerminalEscapes(cwd.replace(os8.homedir(), "~")).slice(0, 40);
|
|
4435
|
+
continue;
|
|
4436
|
+
}
|
|
4437
|
+
if (entry.type === "turn_context" && typeof payload["model"] === "string") {
|
|
4438
|
+
model = payload["model"];
|
|
4266
4439
|
continue;
|
|
4267
4440
|
}
|
|
4268
4441
|
if (entry.type === "event_msg" && payload["type"] === "token_count") {
|
|
@@ -4408,13 +4581,16 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
4408
4581
|
}
|
|
4409
4582
|
}
|
|
4410
4583
|
}
|
|
4411
|
-
|
|
4412
|
-
|
|
4584
|
+
result.totalCostUSD += codexSessionCost(model, {
|
|
4585
|
+
input: lastTotalInput,
|
|
4586
|
+
cached: lastTotalCached,
|
|
4587
|
+
output: lastTotalOutput
|
|
4588
|
+
});
|
|
4413
4589
|
result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "codex"));
|
|
4414
4590
|
}
|
|
4415
4591
|
return result;
|
|
4416
4592
|
}
|
|
4417
|
-
var
|
|
4593
|
+
var CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, FIXTURE_TOKEN_PATTERNS, TERMINAL_ESCAPE_RE, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS;
|
|
4418
4594
|
var init_scan = __esm({
|
|
4419
4595
|
"src/cli/commands/scan.ts"() {
|
|
4420
4596
|
"use strict";
|
|
@@ -4423,6 +4599,9 @@ var init_scan = __esm({
|
|
|
4423
4599
|
init_policy();
|
|
4424
4600
|
init_dist();
|
|
4425
4601
|
init_dlp();
|
|
4602
|
+
init_litellm();
|
|
4603
|
+
init_cost_gemini();
|
|
4604
|
+
init_cost_codex();
|
|
4426
4605
|
init_hook_payload();
|
|
4427
4606
|
init_dist();
|
|
4428
4607
|
init_scan_summary();
|
|
@@ -4432,26 +4611,6 @@ var init_scan = __esm({
|
|
|
4432
4611
|
init_protection();
|
|
4433
4612
|
init_scan_json();
|
|
4434
4613
|
init_scan_history();
|
|
4435
|
-
CLAUDE_PRICING2 = {
|
|
4436
|
-
"claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
4437
|
-
"claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
|
|
4438
|
-
"claude-opus-4": { i: 15e-6, o: 75e-6, cw: 1875e-8, cr: 15e-7 },
|
|
4439
|
-
"claude-sonnet-4-6": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
4440
|
-
"claude-sonnet-4-5": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
4441
|
-
"claude-sonnet-4": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
4442
|
-
"claude-3-7-sonnet": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
4443
|
-
"claude-3-5-sonnet": { i: 3e-6, o: 15e-6, cw: 375e-8, cr: 3e-7 },
|
|
4444
|
-
"claude-haiku-4-5": { i: 1e-6, o: 5e-6, cw: 125e-8, cr: 1e-7 },
|
|
4445
|
-
"claude-3-5-haiku": { i: 8e-7, o: 4e-6, cw: 1e-6, cr: 8e-8 }
|
|
4446
|
-
};
|
|
4447
|
-
GEMINI_PRICING = {
|
|
4448
|
-
"gemini-2.5-pro": { i: 125e-8, o: 1e-5, cr: 31e-8 },
|
|
4449
|
-
"gemini-2.5-flash": { i: 15e-8, o: 6e-7, cr: 375e-10 },
|
|
4450
|
-
"gemini-2.0-flash": { i: 1e-7, o: 4e-7, cr: 25e-9 },
|
|
4451
|
-
"gemini-1.5-pro": { i: 125e-8, o: 5e-6, cr: 3125e-10 },
|
|
4452
|
-
"gemini-1.5-flash": { i: 75e-9, o: 3e-7, cr: 1875e-11 },
|
|
4453
|
-
"gemini-3-flash": { i: 1e-7, o: 4e-7, cr: 25e-9 }
|
|
4454
|
-
};
|
|
4455
4614
|
CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4456
4615
|
".ts",
|
|
4457
4616
|
".tsx",
|
|
@@ -4514,23 +4673,23 @@ var init_scan = __esm({
|
|
|
4514
4673
|
});
|
|
4515
4674
|
|
|
4516
4675
|
// src/tui/dashboard/data.ts
|
|
4517
|
-
import
|
|
4518
|
-
import
|
|
4519
|
-
import
|
|
4676
|
+
import fs7 from "fs";
|
|
4677
|
+
import os9 from "os";
|
|
4678
|
+
import path9 from "path";
|
|
4520
4679
|
import http from "http";
|
|
4521
4680
|
function auditLogPath() {
|
|
4522
|
-
return
|
|
4681
|
+
return path9.join(os9.homedir(), ".node9", "audit.log");
|
|
4523
4682
|
}
|
|
4524
4683
|
function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
|
|
4525
4684
|
return new Promise((resolve) => {
|
|
4526
4685
|
const p = customPath ?? auditLogPath();
|
|
4527
|
-
if (!
|
|
4686
|
+
if (!fs7.existsSync(p)) {
|
|
4528
4687
|
resolve([]);
|
|
4529
4688
|
return;
|
|
4530
4689
|
}
|
|
4531
4690
|
let raw;
|
|
4532
4691
|
try {
|
|
4533
|
-
raw =
|
|
4692
|
+
raw = fs7.readFileSync(p, "utf8");
|
|
4534
4693
|
} catch {
|
|
4535
4694
|
resolve([]);
|
|
4536
4695
|
return;
|
|
@@ -4659,15 +4818,16 @@ function loadBlast() {
|
|
|
4659
4818
|
}
|
|
4660
4819
|
}
|
|
4661
4820
|
function shortenPath(p) {
|
|
4662
|
-
const home =
|
|
4821
|
+
const home = os9.homedir();
|
|
4663
4822
|
return p.startsWith(home) ? p.replace(home, "~") : p;
|
|
4664
4823
|
}
|
|
4665
4824
|
async function loadReportAuditAsync(period) {
|
|
4666
|
-
const claudeProjectsDir =
|
|
4667
|
-
const codexSessionsDir =
|
|
4668
|
-
const geminiTmpDir =
|
|
4825
|
+
const claudeProjectsDir = path9.join(os9.homedir(), ".claude", "projects");
|
|
4826
|
+
const codexSessionsDir = path9.join(os9.homedir(), ".codex", "sessions");
|
|
4827
|
+
const geminiTmpDir = path9.join(os9.homedir(), ".gemini", "tmp");
|
|
4669
4828
|
const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
|
|
4670
4829
|
const entries = await readAuditEntriesAsync();
|
|
4830
|
+
void ensurePricingLoaded();
|
|
4671
4831
|
const claudeCost = await loadClaudeCostAsync(start, end, claudeProjectsDir);
|
|
4672
4832
|
const codexCost = await loadCodexCostAsync(start, end, codexSessionsDir);
|
|
4673
4833
|
const geminiCost = await loadGeminiCostAsync(start, end, geminiTmpDir);
|
|
@@ -5008,6 +5168,7 @@ var init_data = __esm({
|
|
|
5008
5168
|
init_shields();
|
|
5009
5169
|
init_scan_watermark();
|
|
5010
5170
|
init_report_audit();
|
|
5171
|
+
init_litellm();
|
|
5011
5172
|
init_scan();
|
|
5012
5173
|
init_protection();
|
|
5013
5174
|
TEST_TOOLS = /* @__PURE__ */ new Set(["Bash", "bash"]);
|
|
@@ -5769,8 +5930,8 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-run
|
|
|
5769
5930
|
function TopToolsProjects({ audit }) {
|
|
5770
5931
|
const data = audit?.data;
|
|
5771
5932
|
const tools = data ? [...data.toolMap.entries()].sort(([, a], [, b]) => b.calls - a.calls).slice(0, ROW_LIMIT) : [];
|
|
5772
|
-
const projects = data ? [...data.cost.byProject.entries()].map(([
|
|
5773
|
-
name: basenameOf(
|
|
5933
|
+
const projects = data ? [...data.cost.byProject.entries()].map(([path10, r]) => ({
|
|
5934
|
+
name: basenameOf(path10),
|
|
5774
5935
|
cost: r.cost,
|
|
5775
5936
|
tokens: r.inputTokens + r.outputTokens
|
|
5776
5937
|
})).sort((a, b) => b.cost - a.cost).slice(0, ROW_LIMIT) : [];
|
|
@@ -6207,8 +6368,8 @@ function pickTopLoopFile(loops) {
|
|
|
6207
6368
|
map.set(k, (map.get(k) ?? 0) + (l.count ?? 0));
|
|
6208
6369
|
}
|
|
6209
6370
|
if (map.size === 0) return void 0;
|
|
6210
|
-
const [
|
|
6211
|
-
return { path:
|
|
6371
|
+
const [path10, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
6372
|
+
return { path: path10, count };
|
|
6212
6373
|
}
|
|
6213
6374
|
var EMPTY_FILTERED_SCAN;
|
|
6214
6375
|
var init_derive = __esm({
|