llm-usage-metrics 0.6.0 → 0.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.
- package/README.md +20 -14
- package/dist/index.js +452 -151
- package/dist/index.js.map +1 -1
- package/package.json +29 -11
package/dist/index.js
CHANGED
|
@@ -106,9 +106,6 @@ function getParsingRuntimeConfig(env = process.env) {
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// src/update/update-notifier.ts
|
|
110
|
-
import { spawn as spawn2 } from "child_process";
|
|
111
|
-
|
|
112
109
|
// src/update/update-cache-repository.ts
|
|
113
110
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
114
111
|
import path2 from "path";
|
|
@@ -529,7 +526,6 @@ async function runInteractiveInstallAndRestart(options) {
|
|
|
529
526
|
|
|
530
527
|
// src/update/update-notifier.ts
|
|
531
528
|
var UPDATE_CHECK_SKIP_ENV_VAR = "LLM_USAGE_SKIP_UPDATE_CHECK";
|
|
532
|
-
var UPDATE_CHECK_REFRESH_ENV_VAR = "LLM_USAGE_REFRESH_UPDATE_CHECK";
|
|
533
529
|
function isTruthyEnvFlag(value) {
|
|
534
530
|
if (value === void 0) {
|
|
535
531
|
return false;
|
|
@@ -588,32 +584,6 @@ function toResolveLatestVersionOptions(options, env) {
|
|
|
588
584
|
now: options.now
|
|
589
585
|
};
|
|
590
586
|
}
|
|
591
|
-
function runDetachedCommandWithSpawn(command, args, options = {}) {
|
|
592
|
-
const child = spawn2(command, args, {
|
|
593
|
-
env: options.env,
|
|
594
|
-
stdio: options.stdio ?? "ignore",
|
|
595
|
-
detached: true
|
|
596
|
-
});
|
|
597
|
-
child.on("error", () => void 0);
|
|
598
|
-
child.unref();
|
|
599
|
-
}
|
|
600
|
-
function scheduleBackgroundUpdateRefresh(options, env, argv) {
|
|
601
|
-
const spawnDetachedCommand = options.spawnDetachedCommand ?? runDetachedCommandWithSpawn;
|
|
602
|
-
spawnDetachedCommand(options.execPath ?? process.execPath, argv.slice(1), {
|
|
603
|
-
env: {
|
|
604
|
-
...env,
|
|
605
|
-
[UPDATE_CHECK_REFRESH_ENV_VAR]: "1"
|
|
606
|
-
},
|
|
607
|
-
stdio: "ignore"
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
async function refreshUpdateCheckCache(options) {
|
|
611
|
-
try {
|
|
612
|
-
const env = options.env ?? process.env;
|
|
613
|
-
await resolveLatestVersion(toResolveLatestVersionOptions(options, env));
|
|
614
|
-
} catch {
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
587
|
async function checkForUpdatesAndMaybeRestart(options) {
|
|
618
588
|
const env = options.env ?? process.env;
|
|
619
589
|
const argv = options.argv ?? process.argv;
|
|
@@ -630,19 +600,7 @@ async function checkForUpdatesAndMaybeRestart(options) {
|
|
|
630
600
|
return { continueExecution: true };
|
|
631
601
|
}
|
|
632
602
|
try {
|
|
633
|
-
const
|
|
634
|
-
const cacheFilePath = resolveOptions.cacheFilePath ?? getDefaultUpdateCheckCachePath();
|
|
635
|
-
const cachePayload = await readUpdateCheckCachePayload(cacheFilePath);
|
|
636
|
-
const cacheTtlMs = resolveOptions.cacheTtlMs ?? DEFAULT_UPDATE_CHECK_CACHE_TTL_MS;
|
|
637
|
-
const now = resolveOptions.now ?? Date.now;
|
|
638
|
-
if (!cachePayload || !isCacheFresh(cachePayload, cacheTtlMs, now)) {
|
|
639
|
-
try {
|
|
640
|
-
scheduleBackgroundUpdateRefresh(options, env, argv);
|
|
641
|
-
} catch {
|
|
642
|
-
}
|
|
643
|
-
return { continueExecution: true };
|
|
644
|
-
}
|
|
645
|
-
const latestVersion = cachePayload.latestVersion;
|
|
603
|
+
const latestVersion = await resolveLatestVersion(toResolveLatestVersionOptions(options, env));
|
|
646
604
|
if (!latestVersion || !shouldOfferUpdate(options.currentVersion, latestVersion)) {
|
|
647
605
|
return { continueExecution: true };
|
|
648
606
|
}
|
|
@@ -694,7 +652,8 @@ var knownSourceColors = {
|
|
|
694
652
|
codex: "#22c55e",
|
|
695
653
|
gemini: "#eab308",
|
|
696
654
|
droid: "#3b82f6",
|
|
697
|
-
opencode: "#a855f7"
|
|
655
|
+
opencode: "#a855f7",
|
|
656
|
+
claude: "#d97757"
|
|
698
657
|
};
|
|
699
658
|
var fallbackColors = ["#f97316", "#06b6d4", "#ef4444", "#84cc16", "#f43f5e"];
|
|
700
659
|
function getSourceColor(source, index) {
|
|
@@ -1076,8 +1035,14 @@ function escapeBareAutolinks(value) {
|
|
|
1076
1035
|
(_, prefix, email) => `${prefix}${email.replace("@", "\\@")}`
|
|
1077
1036
|
);
|
|
1078
1037
|
}
|
|
1038
|
+
function escapeHtmlText(value) {
|
|
1039
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
1040
|
+
}
|
|
1079
1041
|
function escapeMarkdownText(value) {
|
|
1080
|
-
const escapedMarkdownText = value
|
|
1042
|
+
const escapedMarkdownText = escapeHtmlText(value).replace(
|
|
1043
|
+
markdownSpecialCharacterPattern,
|
|
1044
|
+
"\\$&"
|
|
1045
|
+
);
|
|
1081
1046
|
return escapeBareAutolinks(escapedMarkdownText);
|
|
1082
1047
|
}
|
|
1083
1048
|
function toMarkdownSafeCell(value) {
|
|
@@ -1270,7 +1235,8 @@ var sourceStylePolicies = /* @__PURE__ */ new Map([
|
|
|
1270
1235
|
["codex", (palette) => palette.magenta],
|
|
1271
1236
|
["gemini", (palette) => palette.yellow],
|
|
1272
1237
|
["droid", (palette) => palette.green],
|
|
1273
|
-
["opencode", (palette) => palette.blue]
|
|
1238
|
+
["opencode", (palette) => palette.blue],
|
|
1239
|
+
["claude", (palette) => palette.cyan]
|
|
1274
1240
|
]);
|
|
1275
1241
|
function resolveSourceStyler(source, palette = defaultTerminalStylePalette) {
|
|
1276
1242
|
const stylePolicy = sourceStylePolicies.get(source);
|
|
@@ -2651,7 +2617,7 @@ function aggregateEfficiency(options) {
|
|
|
2651
2617
|
}
|
|
2652
2618
|
|
|
2653
2619
|
// src/efficiency/git-outcome-collector.ts
|
|
2654
|
-
import { spawn as
|
|
2620
|
+
import { spawn as spawn2 } from "child_process";
|
|
2655
2621
|
import { createInterface as createInterface2 } from "readline";
|
|
2656
2622
|
import path3 from "path";
|
|
2657
2623
|
import { stat } from "fs/promises";
|
|
@@ -2850,7 +2816,7 @@ function parseGitLogShortstatLines(lines, authorEmail) {
|
|
|
2850
2816
|
}
|
|
2851
2817
|
async function runGitCommand(repoDir, args) {
|
|
2852
2818
|
return await new Promise((resolve, reject) => {
|
|
2853
|
-
const child =
|
|
2819
|
+
const child = spawn2("git", args, {
|
|
2854
2820
|
cwd: repoDir,
|
|
2855
2821
|
env: {
|
|
2856
2822
|
...process.env,
|
|
@@ -3431,7 +3397,7 @@ function formatEnvVarOverrides(overrides) {
|
|
|
3431
3397
|
return lines;
|
|
3432
3398
|
}
|
|
3433
3399
|
|
|
3434
|
-
// src/sources/
|
|
3400
|
+
// src/sources/claude/claude-source-adapter.ts
|
|
3435
3401
|
import os2 from "os";
|
|
3436
3402
|
import path6 from "path";
|
|
3437
3403
|
|
|
@@ -3566,11 +3532,6 @@ async function discoverFiles(rootDir, options) {
|
|
|
3566
3532
|
return toCanonicalFiles(files, resolvedOptions);
|
|
3567
3533
|
}
|
|
3568
3534
|
|
|
3569
|
-
// src/utils/discover-jsonl-files.ts
|
|
3570
|
-
async function discoverJsonlFiles(rootDir) {
|
|
3571
|
-
return discoverFiles(rootDir, { extension: ".jsonl" });
|
|
3572
|
-
}
|
|
3573
|
-
|
|
3574
3535
|
// src/utils/fs-helpers.ts
|
|
3575
3536
|
import { access as access2, constants as constants2, stat as stat3 } from "fs/promises";
|
|
3576
3537
|
async function pathExists(filePath) {
|
|
@@ -3652,6 +3613,22 @@ async function* readJsonlObjects(filePath, options = {}) {
|
|
|
3652
3613
|
}
|
|
3653
3614
|
}
|
|
3654
3615
|
|
|
3616
|
+
// src/sources/parse-diagnostics.ts
|
|
3617
|
+
function incrementSkippedReason(reasons, reason) {
|
|
3618
|
+
const current = reasons.get(reason) ?? 0;
|
|
3619
|
+
reasons.set(reason, current + 1);
|
|
3620
|
+
}
|
|
3621
|
+
function toSkippedRowReasonStats(reasons) {
|
|
3622
|
+
return [...reasons.entries()].map(([reason, count]) => ({ reason, count })).sort((left, right) => compareByCodePoint(left.reason, right.reason));
|
|
3623
|
+
}
|
|
3624
|
+
function toParseDiagnostics(events, skippedRows, skippedRowReasons) {
|
|
3625
|
+
return {
|
|
3626
|
+
events,
|
|
3627
|
+
skippedRows,
|
|
3628
|
+
skippedRowReasons: toSkippedRowReasonStats(skippedRowReasons)
|
|
3629
|
+
};
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3655
3632
|
// src/sources/parsing-utils.ts
|
|
3656
3633
|
var MIN_PLAUSIBLE_UNIX_SECONDS_ABS = 1e8;
|
|
3657
3634
|
var UNIX_SECONDS_ABS_CUTOFF = 1e10;
|
|
@@ -3704,8 +3681,178 @@ function normalizeTimestampCandidate(candidate) {
|
|
|
3704
3681
|
return date.toISOString();
|
|
3705
3682
|
}
|
|
3706
3683
|
|
|
3684
|
+
// src/sources/claude/claude-source-adapter.ts
|
|
3685
|
+
var defaultClaudeProjectsDir = path6.join(os2.homedir(), ".claude", "projects");
|
|
3686
|
+
var CLAUDE_ASSISTANT_LINE_PATTERN = /"type"\s*:\s*"assistant"/u;
|
|
3687
|
+
var CLAUDE_USAGE_LINE_PATTERN = /"usage"\s*:/u;
|
|
3688
|
+
function shouldParseClaudeJsonlLine(lineText) {
|
|
3689
|
+
return CLAUDE_ASSISTANT_LINE_PATTERN.test(lineText) && CLAUDE_USAGE_LINE_PATTERN.test(lineText);
|
|
3690
|
+
}
|
|
3691
|
+
function getFallbackSessionId(filePath) {
|
|
3692
|
+
return path6.basename(filePath, ".jsonl");
|
|
3693
|
+
}
|
|
3694
|
+
function resolveProvider(message, model) {
|
|
3695
|
+
const explicitProvider = asTrimmedText(message.provider);
|
|
3696
|
+
if (explicitProvider) {
|
|
3697
|
+
return explicitProvider;
|
|
3698
|
+
}
|
|
3699
|
+
if (model?.toLowerCase().startsWith("claude-")) {
|
|
3700
|
+
return "anthropic";
|
|
3701
|
+
}
|
|
3702
|
+
return void 0;
|
|
3703
|
+
}
|
|
3704
|
+
function parseUsage(usage) {
|
|
3705
|
+
const inputTokens = normalizeNonNegativeInteger(toNumberLike(usage.input_tokens));
|
|
3706
|
+
const outputTokens = normalizeNonNegativeInteger(toNumberLike(usage.output_tokens));
|
|
3707
|
+
const cacheReadTokens = normalizeNonNegativeInteger(toNumberLike(usage.cache_read_input_tokens));
|
|
3708
|
+
const cacheWriteTokens = normalizeNonNegativeInteger(
|
|
3709
|
+
toNumberLike(usage.cache_creation_input_tokens)
|
|
3710
|
+
);
|
|
3711
|
+
const totalTokens = inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens;
|
|
3712
|
+
if (totalTokens === 0) {
|
|
3713
|
+
return void 0;
|
|
3714
|
+
}
|
|
3715
|
+
return {
|
|
3716
|
+
inputTokens,
|
|
3717
|
+
outputTokens,
|
|
3718
|
+
reasoningTokens: 0,
|
|
3719
|
+
cacheReadTokens,
|
|
3720
|
+
cacheWriteTokens,
|
|
3721
|
+
totalTokens
|
|
3722
|
+
};
|
|
3723
|
+
}
|
|
3724
|
+
function createDedupKey(filePath, line, message, timestamp, model) {
|
|
3725
|
+
const messageId = asTrimmedText(message.id);
|
|
3726
|
+
if (messageId) {
|
|
3727
|
+
return `${filePath}\0${messageId}`;
|
|
3728
|
+
}
|
|
3729
|
+
const uuid = asTrimmedText(line.uuid);
|
|
3730
|
+
if (uuid) {
|
|
3731
|
+
return `${filePath}\0${uuid}`;
|
|
3732
|
+
}
|
|
3733
|
+
return `${filePath}\0${timestamp}\0${model ?? ""}`;
|
|
3734
|
+
}
|
|
3735
|
+
function comparePendingEvents(left, right) {
|
|
3736
|
+
if (left.timestamp !== right.timestamp) {
|
|
3737
|
+
return compareByCodePoint(left.timestamp, right.timestamp);
|
|
3738
|
+
}
|
|
3739
|
+
return left.sequence - right.sequence;
|
|
3740
|
+
}
|
|
3741
|
+
var ClaudeSourceAdapter = class {
|
|
3742
|
+
id = "claude";
|
|
3743
|
+
projectsDir;
|
|
3744
|
+
requireProjectsDir;
|
|
3745
|
+
constructor(options = {}) {
|
|
3746
|
+
this.projectsDir = options.projectsDir ?? defaultClaudeProjectsDir;
|
|
3747
|
+
this.requireProjectsDir = options.requireProjectsDir ?? false;
|
|
3748
|
+
}
|
|
3749
|
+
getNormalizedProjectsDir() {
|
|
3750
|
+
if (isBlankText(this.projectsDir)) {
|
|
3751
|
+
throw new Error("Claude projects directory must be a non-empty path");
|
|
3752
|
+
}
|
|
3753
|
+
return this.projectsDir.trim();
|
|
3754
|
+
}
|
|
3755
|
+
async discoverFiles() {
|
|
3756
|
+
const normalizedProjectsDir = this.getNormalizedProjectsDir();
|
|
3757
|
+
if (this.requireProjectsDir && !await pathReadable(normalizedProjectsDir)) {
|
|
3758
|
+
throw new Error(
|
|
3759
|
+
`Claude projects directory is missing or unreadable: ${normalizedProjectsDir}`
|
|
3760
|
+
);
|
|
3761
|
+
}
|
|
3762
|
+
if (this.requireProjectsDir && !await pathIsDirectory(normalizedProjectsDir)) {
|
|
3763
|
+
throw new Error(`Claude projects directory is not a directory: ${normalizedProjectsDir}`);
|
|
3764
|
+
}
|
|
3765
|
+
return discoverFiles(normalizedProjectsDir, { extension: ".jsonl" });
|
|
3766
|
+
}
|
|
3767
|
+
async parseFile(filePath) {
|
|
3768
|
+
const { events } = await this.parseFileWithDiagnostics(filePath);
|
|
3769
|
+
return events;
|
|
3770
|
+
}
|
|
3771
|
+
async parseFileWithDiagnostics(filePath) {
|
|
3772
|
+
const eventsByDedupKey = /* @__PURE__ */ new Map();
|
|
3773
|
+
let skippedRows = 0;
|
|
3774
|
+
let sequence = 0;
|
|
3775
|
+
const skippedRowReasons = /* @__PURE__ */ new Map();
|
|
3776
|
+
for await (const line of readJsonlObjects(filePath, {
|
|
3777
|
+
shouldParseLine: shouldParseClaudeJsonlLine
|
|
3778
|
+
})) {
|
|
3779
|
+
if (asTrimmedText(line.type) !== "assistant") {
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
const message = asRecord(line.message);
|
|
3783
|
+
if (!message || asTrimmedText(message.role) !== "assistant") {
|
|
3784
|
+
skippedRows++;
|
|
3785
|
+
incrementSkippedReason(skippedRowReasons, "invalid_assistant_message");
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
const model = asTrimmedText(message.model);
|
|
3789
|
+
if (model === "<synthetic>") {
|
|
3790
|
+
skippedRows++;
|
|
3791
|
+
incrementSkippedReason(skippedRowReasons, "synthetic_message");
|
|
3792
|
+
continue;
|
|
3793
|
+
}
|
|
3794
|
+
const usage = parseUsage(asRecord(message.usage) ?? {});
|
|
3795
|
+
if (!usage) {
|
|
3796
|
+
skippedRows++;
|
|
3797
|
+
incrementSkippedReason(skippedRowReasons, "no_token_usage");
|
|
3798
|
+
continue;
|
|
3799
|
+
}
|
|
3800
|
+
const timestamp = normalizeTimestampCandidate(line.timestamp);
|
|
3801
|
+
if (!timestamp) {
|
|
3802
|
+
skippedRows++;
|
|
3803
|
+
incrementSkippedReason(skippedRowReasons, "invalid_timestamp");
|
|
3804
|
+
continue;
|
|
3805
|
+
}
|
|
3806
|
+
const dedupKey = createDedupKey(filePath, line, message, timestamp, model);
|
|
3807
|
+
const sessionId = asTrimmedText(line.sessionId) ?? getFallbackSessionId(filePath);
|
|
3808
|
+
const repoRoot = asTrimmedText(line.cwd);
|
|
3809
|
+
const provider = resolveProvider(message, model);
|
|
3810
|
+
sequence += 1;
|
|
3811
|
+
eventsByDedupKey.set(dedupKey, {
|
|
3812
|
+
sessionId,
|
|
3813
|
+
timestamp,
|
|
3814
|
+
repoRoot,
|
|
3815
|
+
provider,
|
|
3816
|
+
model,
|
|
3817
|
+
usage,
|
|
3818
|
+
sequence
|
|
3819
|
+
});
|
|
3820
|
+
}
|
|
3821
|
+
const events = [];
|
|
3822
|
+
for (const pendingEvent of [...eventsByDedupKey.values()].sort(comparePendingEvents)) {
|
|
3823
|
+
try {
|
|
3824
|
+
events.push(
|
|
3825
|
+
createUsageEvent({
|
|
3826
|
+
source: this.id,
|
|
3827
|
+
sessionId: pendingEvent.sessionId,
|
|
3828
|
+
timestamp: pendingEvent.timestamp,
|
|
3829
|
+
repoRoot: pendingEvent.repoRoot,
|
|
3830
|
+
provider: pendingEvent.provider,
|
|
3831
|
+
model: pendingEvent.model,
|
|
3832
|
+
...pendingEvent.usage,
|
|
3833
|
+
costMode: "estimated"
|
|
3834
|
+
})
|
|
3835
|
+
);
|
|
3836
|
+
} catch {
|
|
3837
|
+
skippedRows++;
|
|
3838
|
+
incrementSkippedReason(skippedRowReasons, "event_creation_failed");
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
return toParseDiagnostics(events, skippedRows, skippedRowReasons);
|
|
3842
|
+
}
|
|
3843
|
+
};
|
|
3844
|
+
|
|
3707
3845
|
// src/sources/codex/codex-source-adapter.ts
|
|
3708
|
-
|
|
3846
|
+
import os3 from "os";
|
|
3847
|
+
import path7 from "path";
|
|
3848
|
+
|
|
3849
|
+
// src/utils/discover-jsonl-files.ts
|
|
3850
|
+
async function discoverJsonlFiles(rootDir) {
|
|
3851
|
+
return discoverFiles(rootDir, { extension: ".jsonl" });
|
|
3852
|
+
}
|
|
3853
|
+
|
|
3854
|
+
// src/sources/codex/codex-source-adapter.ts
|
|
3855
|
+
var defaultSessionsDir = path7.join(os3.homedir(), ".codex", "sessions");
|
|
3709
3856
|
var LEGACY_CODEX_MODEL_FALLBACK = "legacy-codex-unknown";
|
|
3710
3857
|
var SESSION_META_LINE_PATTERN = /"type"\s*:\s*"session_meta"/u;
|
|
3711
3858
|
var TURN_CONTEXT_LINE_PATTERN = /"type"\s*:\s*"turn_context"/u;
|
|
@@ -3799,8 +3946,8 @@ function createLastUsageOnlyKey(timestamp, usage) {
|
|
|
3799
3946
|
usage.totalTokens
|
|
3800
3947
|
].join(":");
|
|
3801
3948
|
}
|
|
3802
|
-
function
|
|
3803
|
-
return
|
|
3949
|
+
function getFallbackSessionId2(filePath) {
|
|
3950
|
+
return path7.basename(filePath, ".jsonl");
|
|
3804
3951
|
}
|
|
3805
3952
|
function resolveRepoRootFromPayload(payload) {
|
|
3806
3953
|
if (!payload) {
|
|
@@ -3840,7 +3987,7 @@ var CodexSourceAdapter = class {
|
|
|
3840
3987
|
async parseFile(filePath) {
|
|
3841
3988
|
const events = [];
|
|
3842
3989
|
const state = {
|
|
3843
|
-
sessionId:
|
|
3990
|
+
sessionId: getFallbackSessionId2(filePath),
|
|
3844
3991
|
provider: "openai"
|
|
3845
3992
|
};
|
|
3846
3993
|
for await (const line of readJsonlObjects(filePath, {
|
|
@@ -3932,37 +4079,19 @@ var CodexSourceAdapter = class {
|
|
|
3932
4079
|
|
|
3933
4080
|
// src/sources/droid/droid-source-adapter.ts
|
|
3934
4081
|
import { readFile as readFile2 } from "fs/promises";
|
|
3935
|
-
import
|
|
3936
|
-
import
|
|
3937
|
-
|
|
3938
|
-
// src/sources/parse-diagnostics.ts
|
|
3939
|
-
function incrementSkippedReason(reasons, reason) {
|
|
3940
|
-
const current = reasons.get(reason) ?? 0;
|
|
3941
|
-
reasons.set(reason, current + 1);
|
|
3942
|
-
}
|
|
3943
|
-
function toSkippedRowReasonStats(reasons) {
|
|
3944
|
-
return [...reasons.entries()].map(([reason, count]) => ({ reason, count })).sort((left, right) => compareByCodePoint(left.reason, right.reason));
|
|
3945
|
-
}
|
|
3946
|
-
function toParseDiagnostics(events, skippedRows, skippedRowReasons) {
|
|
3947
|
-
return {
|
|
3948
|
-
events,
|
|
3949
|
-
skippedRows,
|
|
3950
|
-
skippedRowReasons: toSkippedRowReasonStats(skippedRowReasons)
|
|
3951
|
-
};
|
|
3952
|
-
}
|
|
3953
|
-
|
|
3954
|
-
// src/sources/droid/droid-source-adapter.ts
|
|
3955
|
-
var defaultSessionsDir2 = path7.join(os3.homedir(), ".factory", "sessions");
|
|
4082
|
+
import os4 from "os";
|
|
4083
|
+
import path8 from "path";
|
|
4084
|
+
var defaultSessionsDir2 = path8.join(os4.homedir(), ".factory", "sessions");
|
|
3956
4085
|
var DROID_SESSION_START_LINE_PATTERN = /"type"\s*:\s*"session_start"/u;
|
|
3957
4086
|
var DROID_MESSAGE_LINE_PATTERN = /"type"\s*:\s*"message"/u;
|
|
3958
4087
|
function shouldParseDroidJsonlLine(lineText) {
|
|
3959
4088
|
return DROID_SESSION_START_LINE_PATTERN.test(lineText) || DROID_MESSAGE_LINE_PATTERN.test(lineText);
|
|
3960
4089
|
}
|
|
3961
4090
|
function getSettingsSessionId(filePath) {
|
|
3962
|
-
return
|
|
4091
|
+
return path8.basename(filePath, ".settings.json");
|
|
3963
4092
|
}
|
|
3964
4093
|
function getSiblingJsonlPath(settingsPath) {
|
|
3965
|
-
return
|
|
4094
|
+
return path8.join(path8.dirname(settingsPath), `${getSettingsSessionId(settingsPath)}.jsonl`);
|
|
3966
4095
|
}
|
|
3967
4096
|
function isSessionStartRecord(line) {
|
|
3968
4097
|
return asTrimmedText(line.type) === "session_start";
|
|
@@ -4106,11 +4235,11 @@ var DroidSourceAdapter = class {
|
|
|
4106
4235
|
|
|
4107
4236
|
// src/sources/gemini/gemini-source-adapter.ts
|
|
4108
4237
|
import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
|
|
4109
|
-
import
|
|
4110
|
-
import
|
|
4111
|
-
var defaultGeminiDir =
|
|
4238
|
+
import os5 from "os";
|
|
4239
|
+
import path9 from "path";
|
|
4240
|
+
var defaultGeminiDir = path9.join(os5.homedir(), ".gemini");
|
|
4112
4241
|
function getProjectsJsonPath(geminiDir) {
|
|
4113
|
-
return
|
|
4242
|
+
return path9.join(geminiDir, "projects.json");
|
|
4114
4243
|
}
|
|
4115
4244
|
function parseProjectsJson(data) {
|
|
4116
4245
|
const mapping = /* @__PURE__ */ new Map();
|
|
@@ -4145,7 +4274,7 @@ async function loadProjectsJson(geminiDir) {
|
|
|
4145
4274
|
}
|
|
4146
4275
|
}
|
|
4147
4276
|
async function discoverSessionFiles(geminiDir) {
|
|
4148
|
-
const tmpDir =
|
|
4277
|
+
const tmpDir = path9.join(geminiDir, "tmp");
|
|
4149
4278
|
let projectEntries;
|
|
4150
4279
|
try {
|
|
4151
4280
|
projectEntries = await readdir2(tmpDir, { withFileTypes: true, encoding: "utf8" });
|
|
@@ -4162,7 +4291,7 @@ async function discoverSessionFiles(geminiDir) {
|
|
|
4162
4291
|
if (!projectEntry.isDirectory() && !projectEntry.isSymbolicLink()) {
|
|
4163
4292
|
continue;
|
|
4164
4293
|
}
|
|
4165
|
-
const chatsDir =
|
|
4294
|
+
const chatsDir = path9.join(tmpDir, projectEntry.name, "chats");
|
|
4166
4295
|
const discoveredChatFiles = await discoverFiles(chatsDir, { extension: ".json" });
|
|
4167
4296
|
allSessionFiles.push(...discoveredChatFiles);
|
|
4168
4297
|
}
|
|
@@ -4176,9 +4305,9 @@ function resolveRepoRoot(filePath, sessionData, projectMapping) {
|
|
|
4176
4305
|
return mappedRoot;
|
|
4177
4306
|
}
|
|
4178
4307
|
}
|
|
4179
|
-
const chatsDir =
|
|
4180
|
-
const projectDir =
|
|
4181
|
-
const projectIdentifier =
|
|
4308
|
+
const chatsDir = path9.dirname(filePath);
|
|
4309
|
+
const projectDir = path9.dirname(chatsDir);
|
|
4310
|
+
const projectIdentifier = path9.basename(projectDir);
|
|
4182
4311
|
return projectMapping.get(projectIdentifier);
|
|
4183
4312
|
}
|
|
4184
4313
|
function toFiniteNumber(value) {
|
|
@@ -4287,7 +4416,7 @@ var GeminiSourceAdapter = class {
|
|
|
4287
4416
|
incrementSkippedReason(skippedRowReasons, "invalid_session_data");
|
|
4288
4417
|
return toParseDiagnostics(events, skippedRows, skippedRowReasons);
|
|
4289
4418
|
}
|
|
4290
|
-
const sessionId = asTrimmedText(sessionDataRecord.sessionId) ??
|
|
4419
|
+
const sessionId = asTrimmedText(sessionDataRecord.sessionId) ?? path9.basename(filePath, ".json");
|
|
4291
4420
|
const projectMapping = await this.getProjectMappingForParse();
|
|
4292
4421
|
const repoRoot = resolveRepoRoot(filePath, sessionDataRecord, projectMapping);
|
|
4293
4422
|
if (!Array.isArray(sessionDataRecord.messages)) {
|
|
@@ -4344,8 +4473,8 @@ var GeminiSourceAdapter = class {
|
|
|
4344
4473
|
};
|
|
4345
4474
|
|
|
4346
4475
|
// src/sources/opencode/opencode-db-path-resolver.ts
|
|
4347
|
-
import
|
|
4348
|
-
import
|
|
4476
|
+
import os6 from "os";
|
|
4477
|
+
import path10 from "path";
|
|
4349
4478
|
function deduplicate(paths) {
|
|
4350
4479
|
return [...new Set(paths)];
|
|
4351
4480
|
}
|
|
@@ -4357,39 +4486,39 @@ function normalizeEnvPath(value) {
|
|
|
4357
4486
|
return normalized || void 0;
|
|
4358
4487
|
}
|
|
4359
4488
|
function getLinuxLikeCandidates(homeDir, env) {
|
|
4360
|
-
const xdgDataHome = normalizeEnvPath(env.XDG_DATA_HOME) ??
|
|
4489
|
+
const xdgDataHome = normalizeEnvPath(env.XDG_DATA_HOME) ?? path10.join(homeDir, ".local", "share");
|
|
4361
4490
|
return [
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4491
|
+
path10.join(xdgDataHome, "opencode", "opencode.db"),
|
|
4492
|
+
path10.join(xdgDataHome, "opencode", "db.sqlite"),
|
|
4493
|
+
path10.join(homeDir, ".opencode", "opencode.db"),
|
|
4494
|
+
path10.join(homeDir, ".opencode", "db.sqlite")
|
|
4366
4495
|
];
|
|
4367
4496
|
}
|
|
4368
4497
|
function getMacOsCandidates(homeDir) {
|
|
4369
|
-
const appSupportDir =
|
|
4498
|
+
const appSupportDir = path10.join(homeDir, "Library", "Application Support");
|
|
4370
4499
|
return [
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4500
|
+
path10.join(appSupportDir, "opencode", "opencode.db"),
|
|
4501
|
+
path10.join(appSupportDir, "opencode", "db.sqlite"),
|
|
4502
|
+
path10.join(homeDir, ".opencode", "opencode.db"),
|
|
4503
|
+
path10.join(homeDir, ".opencode", "db.sqlite")
|
|
4375
4504
|
];
|
|
4376
4505
|
}
|
|
4377
4506
|
function getWindowsCandidates(homeDir, env) {
|
|
4378
4507
|
const userProfile = normalizeEnvPath(env.USERPROFILE);
|
|
4379
|
-
const roamingBase = normalizeEnvPath(env.APPDATA) ?? normalizeEnvPath(env.LOCALAPPDATA) ?? (userProfile ?
|
|
4508
|
+
const roamingBase = normalizeEnvPath(env.APPDATA) ?? normalizeEnvPath(env.LOCALAPPDATA) ?? (userProfile ? path10.join(userProfile, "AppData", "Roaming") : void 0);
|
|
4380
4509
|
const roamingCandidates = roamingBase ? [
|
|
4381
|
-
|
|
4382
|
-
|
|
4510
|
+
path10.join(roamingBase, "opencode", "opencode.db"),
|
|
4511
|
+
path10.join(roamingBase, "opencode", "db.sqlite")
|
|
4383
4512
|
] : [];
|
|
4384
4513
|
return [
|
|
4385
4514
|
...roamingCandidates,
|
|
4386
|
-
|
|
4387
|
-
|
|
4515
|
+
path10.join(homeDir, ".opencode", "opencode.db"),
|
|
4516
|
+
path10.join(homeDir, ".opencode", "db.sqlite")
|
|
4388
4517
|
];
|
|
4389
4518
|
}
|
|
4390
4519
|
function getDefaultOpenCodeDbPathCandidates(options = {}) {
|
|
4391
4520
|
const platform = options.platform ?? process.platform;
|
|
4392
|
-
const homeDir = options.homeDir ??
|
|
4521
|
+
const homeDir = options.homeDir ?? os6.homedir();
|
|
4393
4522
|
const env = options.env ?? process.env;
|
|
4394
4523
|
switch (platform) {
|
|
4395
4524
|
case "win32":
|
|
@@ -4878,9 +5007,9 @@ var OpenCodeSourceAdapter = class {
|
|
|
4878
5007
|
};
|
|
4879
5008
|
|
|
4880
5009
|
// src/sources/pi/pi-source-adapter.ts
|
|
4881
|
-
import
|
|
4882
|
-
import
|
|
4883
|
-
var defaultSessionsDir3 =
|
|
5010
|
+
import os7 from "os";
|
|
5011
|
+
import path11 from "path";
|
|
5012
|
+
var defaultSessionsDir3 = path11.join(os7.homedir(), ".pi", "agent", "sessions");
|
|
4884
5013
|
var PI_MESSAGE_LINE_PATTERN = /"type"\s*:\s*"message"/u;
|
|
4885
5014
|
var PI_SESSION_LINE_PATTERN = /"type"\s*:\s*"session"/u;
|
|
4886
5015
|
var PI_MODEL_CHANGE_LINE_PATTERN = /"type"\s*:\s*"model_change"/u;
|
|
@@ -4953,8 +5082,8 @@ function extractUsage(line, message) {
|
|
|
4953
5082
|
}
|
|
4954
5083
|
return extractUsageFromRecord(messageUsage);
|
|
4955
5084
|
}
|
|
4956
|
-
function
|
|
4957
|
-
return
|
|
5085
|
+
function getFallbackSessionId3(filePath) {
|
|
5086
|
+
return path11.basename(filePath, ".jsonl");
|
|
4958
5087
|
}
|
|
4959
5088
|
function resolveRepoRootFromRecord(record) {
|
|
4960
5089
|
if (!record) {
|
|
@@ -4986,7 +5115,7 @@ var PiSourceAdapter = class {
|
|
|
4986
5115
|
}
|
|
4987
5116
|
async parseFile(filePath) {
|
|
4988
5117
|
const events = [];
|
|
4989
|
-
const state = { sessionId:
|
|
5118
|
+
const state = { sessionId: getFallbackSessionId3(filePath) };
|
|
4990
5119
|
for await (const line of readJsonlObjects(filePath, {
|
|
4991
5120
|
shouldParseLine: shouldParsePiJsonlLine
|
|
4992
5121
|
})) {
|
|
@@ -5125,6 +5254,21 @@ var sourceRegistrations = [
|
|
|
5125
5254
|
create: (options) => new OpenCodeSourceAdapter({
|
|
5126
5255
|
dbPath: options.opencodeDb
|
|
5127
5256
|
})
|
|
5257
|
+
},
|
|
5258
|
+
{
|
|
5259
|
+
id: "claude",
|
|
5260
|
+
sourceDirOverride: { kind: "directory" },
|
|
5261
|
+
create: (options, sourceDirectoryOverrides) => {
|
|
5262
|
+
const directoryConfig = resolveDirectoryConfig(
|
|
5263
|
+
"claude",
|
|
5264
|
+
options.claudeDir,
|
|
5265
|
+
sourceDirectoryOverrides
|
|
5266
|
+
);
|
|
5267
|
+
return new ClaudeSourceAdapter({
|
|
5268
|
+
projectsDir: directoryConfig.path,
|
|
5269
|
+
requireProjectsDir: directoryConfig.requireExistingPath
|
|
5270
|
+
});
|
|
5271
|
+
}
|
|
5128
5272
|
}
|
|
5129
5273
|
];
|
|
5130
5274
|
var sourceDirUnsupportedFlags = new Map(
|
|
@@ -5201,6 +5345,7 @@ function createDefaultAdapters(options) {
|
|
|
5201
5345
|
validateDirectoryOverride("--codex-dir", options.codexDir);
|
|
5202
5346
|
validateDirectoryOverride("--gemini-dir", options.geminiDir);
|
|
5203
5347
|
validateDirectoryOverride("--droid-dir", options.droidDir);
|
|
5348
|
+
validateDirectoryOverride("--claude-dir", options.claudeDir);
|
|
5204
5349
|
const sourceDirectoryOverrides = parseSourceDirectoryOverrides(options.sourceDir);
|
|
5205
5350
|
validateSourceDirectoryOverrideIds(sourceDirectoryOverrides);
|
|
5206
5351
|
return sourceRegistrations.map((source) => source.create(options, sourceDirectoryOverrides));
|
|
@@ -5316,6 +5461,9 @@ function resolveExplicitSourceIds(options, sourceFilter) {
|
|
|
5316
5461
|
if (options.droidDir) {
|
|
5317
5462
|
explicitSourceIds.add("droid");
|
|
5318
5463
|
}
|
|
5464
|
+
if (options.claudeDir) {
|
|
5465
|
+
explicitSourceIds.add("claude");
|
|
5466
|
+
}
|
|
5319
5467
|
if (options.opencodeDb) {
|
|
5320
5468
|
explicitSourceIds.add("opencode");
|
|
5321
5469
|
}
|
|
@@ -5444,7 +5592,7 @@ function normalizeSkippedRowReasons(value) {
|
|
|
5444
5592
|
|
|
5445
5593
|
// src/cli/parse-file-cache.ts
|
|
5446
5594
|
import { mkdir as mkdir2, readFile as readFile4, rename, rm, stat as stat4, writeFile as writeFile2 } from "fs/promises";
|
|
5447
|
-
import
|
|
5595
|
+
import path12 from "path";
|
|
5448
5596
|
var PARSE_FILE_CACHE_VERSION = 5;
|
|
5449
5597
|
var CACHE_KEY_SEPARATOR = "\0";
|
|
5450
5598
|
function createCacheKey(source, filePath) {
|
|
@@ -5671,7 +5819,7 @@ function normalizeCacheEntry(value) {
|
|
|
5671
5819
|
};
|
|
5672
5820
|
}
|
|
5673
5821
|
function getDefaultParseFileCachePath() {
|
|
5674
|
-
return
|
|
5822
|
+
return path12.join(getUserCacheRootDir(), "llm-usage-metrics", "parse-file-cache.json");
|
|
5675
5823
|
}
|
|
5676
5824
|
function normalizeCacheShardSource(source) {
|
|
5677
5825
|
const normalizedSource = normalizeCacheSource(source);
|
|
@@ -5681,12 +5829,12 @@ function normalizeCacheShardSource(source) {
|
|
|
5681
5829
|
return normalizedSource.replace(/[^a-z0-9._-]/gu, "_");
|
|
5682
5830
|
}
|
|
5683
5831
|
function getSourceShardedParseFileCachePath(cacheFilePath, source) {
|
|
5684
|
-
const parsedPath =
|
|
5832
|
+
const parsedPath = path12.parse(cacheFilePath);
|
|
5685
5833
|
const sourceShard = normalizeCacheShardSource(source);
|
|
5686
5834
|
if (parsedPath.ext.length > 0) {
|
|
5687
|
-
return
|
|
5835
|
+
return path12.join(parsedPath.dir, `${parsedPath.name}.${sourceShard}${parsedPath.ext}`);
|
|
5688
5836
|
}
|
|
5689
|
-
return
|
|
5837
|
+
return path12.join(parsedPath.dir, `${parsedPath.base}.${sourceShard}`);
|
|
5690
5838
|
}
|
|
5691
5839
|
var ParseFileCache = class _ParseFileCache {
|
|
5692
5840
|
constructor(cacheFilePath, limits, now) {
|
|
@@ -5694,6 +5842,9 @@ var ParseFileCache = class _ParseFileCache {
|
|
|
5694
5842
|
this.limits = limits;
|
|
5695
5843
|
this.now = now;
|
|
5696
5844
|
}
|
|
5845
|
+
cacheFilePath;
|
|
5846
|
+
limits;
|
|
5847
|
+
now;
|
|
5697
5848
|
entriesByKey = /* @__PURE__ */ new Map();
|
|
5698
5849
|
dirty = false;
|
|
5699
5850
|
static async load(options) {
|
|
@@ -5780,7 +5931,7 @@ var ParseFileCache = class _ParseFileCache {
|
|
|
5780
5931
|
keptEntries.length = bestCount;
|
|
5781
5932
|
payloadText = bestPayloadText;
|
|
5782
5933
|
}
|
|
5783
|
-
await mkdir2(
|
|
5934
|
+
await mkdir2(path12.dirname(this.cacheFilePath), { recursive: true });
|
|
5784
5935
|
const temporaryPath = `${this.cacheFilePath}.${process.pid}.${this.now()}.tmp`;
|
|
5785
5936
|
try {
|
|
5786
5937
|
await writeFile2(temporaryPath, payloadText, "utf8");
|
|
@@ -6267,9 +6418,92 @@ function applyPricingToEvents(events, pricingSource) {
|
|
|
6267
6418
|
});
|
|
6268
6419
|
}
|
|
6269
6420
|
|
|
6421
|
+
// src/pricing/pricing-override-source.ts
|
|
6422
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
6423
|
+
function toFiniteUsdRate(value) {
|
|
6424
|
+
if (value === null || value === void 0) {
|
|
6425
|
+
return void 0;
|
|
6426
|
+
}
|
|
6427
|
+
if (typeof value === "string" && value.trim() === "") {
|
|
6428
|
+
return void 0;
|
|
6429
|
+
}
|
|
6430
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
6431
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
6432
|
+
}
|
|
6433
|
+
function normalizeReasoningBilling(value) {
|
|
6434
|
+
if (value === "included-in-output" || value === "separate") {
|
|
6435
|
+
return value;
|
|
6436
|
+
}
|
|
6437
|
+
return void 0;
|
|
6438
|
+
}
|
|
6439
|
+
function normalizePricingOverride(raw) {
|
|
6440
|
+
const inputPer1MUsd = toFiniteUsdRate(toNumberLike(raw.inputPer1MUsd));
|
|
6441
|
+
const outputPer1MUsd = toFiniteUsdRate(toNumberLike(raw.outputPer1MUsd));
|
|
6442
|
+
if (inputPer1MUsd === void 0 || outputPer1MUsd === void 0) {
|
|
6443
|
+
return void 0;
|
|
6444
|
+
}
|
|
6445
|
+
const cacheReadPer1MUsd = toFiniteUsdRate(toNumberLike(raw.cacheReadPer1MUsd));
|
|
6446
|
+
const cacheWritePer1MUsd = toFiniteUsdRate(toNumberLike(raw.cacheWritePer1MUsd));
|
|
6447
|
+
const reasoningPer1MUsd = toFiniteUsdRate(toNumberLike(raw.reasoningPer1MUsd));
|
|
6448
|
+
const reasoningBilling = normalizeReasoningBilling(raw.reasoningBilling);
|
|
6449
|
+
return {
|
|
6450
|
+
inputPer1MUsd,
|
|
6451
|
+
outputPer1MUsd,
|
|
6452
|
+
...cacheReadPer1MUsd !== void 0 ? { cacheReadPer1MUsd } : {},
|
|
6453
|
+
...cacheWritePer1MUsd !== void 0 ? { cacheWritePer1MUsd } : {},
|
|
6454
|
+
...reasoningPer1MUsd !== void 0 ? { reasoningPer1MUsd } : {},
|
|
6455
|
+
...reasoningBilling !== void 0 ? { reasoningBilling } : {}
|
|
6456
|
+
};
|
|
6457
|
+
}
|
|
6458
|
+
function normalizeOverrideFile(payload) {
|
|
6459
|
+
const root = asRecord(payload);
|
|
6460
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
6461
|
+
const modelsRecord = asRecord(root?.models);
|
|
6462
|
+
if (!modelsRecord) {
|
|
6463
|
+
return overrides;
|
|
6464
|
+
}
|
|
6465
|
+
for (const [modelName, rawPricing] of Object.entries(modelsRecord)) {
|
|
6466
|
+
const normalizedModelName = asTrimmedText(modelName)?.toLowerCase();
|
|
6467
|
+
if (!normalizedModelName) {
|
|
6468
|
+
continue;
|
|
6469
|
+
}
|
|
6470
|
+
const pricing = normalizePricingOverride(asRecord(rawPricing) ?? {});
|
|
6471
|
+
if (pricing) {
|
|
6472
|
+
overrides.set(normalizedModelName, pricing);
|
|
6473
|
+
}
|
|
6474
|
+
}
|
|
6475
|
+
return overrides;
|
|
6476
|
+
}
|
|
6477
|
+
async function loadPricingOverrides(filePath) {
|
|
6478
|
+
const fileContents = await readFile5(filePath, "utf8");
|
|
6479
|
+
const parsed = JSON.parse(fileContents);
|
|
6480
|
+
return normalizeOverrideFile(parsed);
|
|
6481
|
+
}
|
|
6482
|
+
var PricingOverrideSource = class {
|
|
6483
|
+
overrides;
|
|
6484
|
+
delegate;
|
|
6485
|
+
constructor(overrides, delegate) {
|
|
6486
|
+
this.overrides = overrides;
|
|
6487
|
+
this.delegate = delegate;
|
|
6488
|
+
}
|
|
6489
|
+
resolveModelAlias(model) {
|
|
6490
|
+
if (this.overrides.has(model.toLowerCase())) {
|
|
6491
|
+
return model;
|
|
6492
|
+
}
|
|
6493
|
+
return this.delegate.resolveModelAlias(model);
|
|
6494
|
+
}
|
|
6495
|
+
getPricing(model) {
|
|
6496
|
+
const override = this.overrides.get(model.toLowerCase());
|
|
6497
|
+
if (override) {
|
|
6498
|
+
return override;
|
|
6499
|
+
}
|
|
6500
|
+
return this.delegate.getPricing(model);
|
|
6501
|
+
}
|
|
6502
|
+
};
|
|
6503
|
+
|
|
6270
6504
|
// src/pricing/litellm-pricing-fetcher.ts
|
|
6271
|
-
import { mkdir as mkdir3, readFile as
|
|
6272
|
-
import
|
|
6505
|
+
import { mkdir as mkdir3, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
|
|
6506
|
+
import path13 from "path";
|
|
6273
6507
|
|
|
6274
6508
|
// src/pricing/litellm-model-map.json
|
|
6275
6509
|
var litellm_model_map_default = {
|
|
@@ -6453,7 +6687,7 @@ function normalizeLitellmPricingPayload(payload) {
|
|
|
6453
6687
|
return normalizedPricing;
|
|
6454
6688
|
}
|
|
6455
6689
|
function getDefaultLiteLLMPricingCachePath() {
|
|
6456
|
-
return
|
|
6690
|
+
return path13.join(getUserCacheRootDir(), "llm-usage-metrics", "litellm-pricing-cache.json");
|
|
6457
6691
|
}
|
|
6458
6692
|
function stripProviderPrefix(model) {
|
|
6459
6693
|
const slashIndex = model.lastIndexOf("/");
|
|
@@ -6812,7 +7046,7 @@ var LiteLLMPricingFetcher = class {
|
|
|
6812
7046
|
async readCachePayload() {
|
|
6813
7047
|
let content;
|
|
6814
7048
|
try {
|
|
6815
|
-
content = await
|
|
7049
|
+
content = await readFile6(this.cacheFilePath, "utf8");
|
|
6816
7050
|
} catch {
|
|
6817
7051
|
return void 0;
|
|
6818
7052
|
}
|
|
@@ -6847,7 +7081,7 @@ var LiteLLMPricingFetcher = class {
|
|
|
6847
7081
|
};
|
|
6848
7082
|
}
|
|
6849
7083
|
async writeCache() {
|
|
6850
|
-
const directoryPath =
|
|
7084
|
+
const directoryPath = path13.dirname(this.cacheFilePath);
|
|
6851
7085
|
await mkdir3(directoryPath, { recursive: true });
|
|
6852
7086
|
const payload = {
|
|
6853
7087
|
fetchedAt: this.now(),
|
|
@@ -6859,7 +7093,27 @@ var LiteLLMPricingFetcher = class {
|
|
|
6859
7093
|
};
|
|
6860
7094
|
|
|
6861
7095
|
// src/cli/build-usage-data-pricing.ts
|
|
7096
|
+
function wrapWithPricingOverrides(overrides, delegate) {
|
|
7097
|
+
if (!overrides || overrides.size === 0) {
|
|
7098
|
+
return delegate;
|
|
7099
|
+
}
|
|
7100
|
+
return new PricingOverrideSource(overrides, delegate);
|
|
7101
|
+
}
|
|
6862
7102
|
async function resolvePricingSource(options, runtimeConfig) {
|
|
7103
|
+
let pricingOverrides;
|
|
7104
|
+
if (options.pricingOverrides) {
|
|
7105
|
+
try {
|
|
7106
|
+
pricingOverrides = await loadPricingOverrides(options.pricingOverrides);
|
|
7107
|
+
} catch (error) {
|
|
7108
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
7109
|
+
throw new Error(
|
|
7110
|
+
`Could not load --pricing-overrides from ${options.pricingOverrides}: ${reason}`,
|
|
7111
|
+
{
|
|
7112
|
+
cause: error
|
|
7113
|
+
}
|
|
7114
|
+
);
|
|
7115
|
+
}
|
|
7116
|
+
}
|
|
6863
7117
|
const litellmPricingFetcher = new LiteLLMPricingFetcher({
|
|
6864
7118
|
sourceUrl: options.pricingUrl,
|
|
6865
7119
|
offline: options.pricingOffline,
|
|
@@ -6868,10 +7122,11 @@ async function resolvePricingSource(options, runtimeConfig) {
|
|
|
6868
7122
|
});
|
|
6869
7123
|
try {
|
|
6870
7124
|
const fromCache = await litellmPricingFetcher.load();
|
|
7125
|
+
const source = wrapWithPricingOverrides(pricingOverrides, litellmPricingFetcher);
|
|
6871
7126
|
if (options.pricingOffline) {
|
|
6872
|
-
return { source
|
|
7127
|
+
return { source, origin: "offline-cache" };
|
|
6873
7128
|
}
|
|
6874
|
-
return { source
|
|
7129
|
+
return { source, origin: fromCache ? "cache" : "network" };
|
|
6875
7130
|
} catch (error) {
|
|
6876
7131
|
if (options.pricingOffline) {
|
|
6877
7132
|
throw new Error("Offline pricing mode enabled but cached pricing is unavailable", {
|
|
@@ -6958,6 +7213,7 @@ var RuntimeProfileCollector = class {
|
|
|
6958
7213
|
constructor(now = () => performance.now()) {
|
|
6959
7214
|
this.now = now;
|
|
6960
7215
|
}
|
|
7216
|
+
now;
|
|
6961
7217
|
sourceSelection;
|
|
6962
7218
|
sourceStats = /* @__PURE__ */ new Map();
|
|
6963
7219
|
stageDurations = /* @__PURE__ */ new Map();
|
|
@@ -7251,6 +7507,9 @@ function resolveScopeNote(options) {
|
|
|
7251
7507
|
if (hasActiveTextOption(options.droidDir)) {
|
|
7252
7508
|
activeFilters.push("--droid-dir");
|
|
7253
7509
|
}
|
|
7510
|
+
if (hasActiveTextOption(options.claudeDir)) {
|
|
7511
|
+
activeFilters.push("--claude-dir");
|
|
7512
|
+
}
|
|
7254
7513
|
if (hasActiveTextOption(options.opencodeDb)) {
|
|
7255
7514
|
activeFilters.push("--opencode-db");
|
|
7256
7515
|
}
|
|
@@ -7435,11 +7694,12 @@ function emitEnvVarOverrides(activeEnvOverrides, diagnosticsLogger) {
|
|
|
7435
7694
|
}
|
|
7436
7695
|
|
|
7437
7696
|
// src/cli/share-artifact.ts
|
|
7438
|
-
import { spawn as
|
|
7439
|
-
import {
|
|
7440
|
-
import
|
|
7697
|
+
import { spawn as spawn3 } from "child_process";
|
|
7698
|
+
import { constants as constants3 } from "fs";
|
|
7699
|
+
import { access as access3, writeFile as writeFile4 } from "fs/promises";
|
|
7700
|
+
import path14 from "path";
|
|
7441
7701
|
async function writeShareSvgFile(fileName, svgContent) {
|
|
7442
|
-
const outputPath =
|
|
7702
|
+
const outputPath = path14.resolve(process.cwd(), fileName);
|
|
7443
7703
|
await writeFile4(outputPath, svgContent, "utf8");
|
|
7444
7704
|
return outputPath;
|
|
7445
7705
|
}
|
|
@@ -7449,27 +7709,74 @@ function stringifyError(error) {
|
|
|
7449
7709
|
}
|
|
7450
7710
|
return String(error);
|
|
7451
7711
|
}
|
|
7452
|
-
|
|
7712
|
+
var WINDOWS_OPEN_COMMAND = "C:\\Windows\\System32\\rundll32.exe";
|
|
7713
|
+
var DARWIN_OPEN_COMMAND = "/usr/bin/open";
|
|
7714
|
+
var UNIX_OPEN_COMMAND = "/usr/bin/xdg-open";
|
|
7715
|
+
var WINDOWS_FALLBACK_COMMANDS = ["rundll32.exe"];
|
|
7716
|
+
var DARWIN_FALLBACK_COMMANDS = ["open"];
|
|
7717
|
+
var UNIX_FALLBACK_COMMANDS = ["xdg-open"];
|
|
7718
|
+
async function fileExists(filePath) {
|
|
7719
|
+
try {
|
|
7720
|
+
await access3(filePath, constants3.X_OK);
|
|
7721
|
+
return true;
|
|
7722
|
+
} catch {
|
|
7723
|
+
return false;
|
|
7724
|
+
}
|
|
7725
|
+
}
|
|
7726
|
+
async function resolveBinaryPath(primaryPath, fallbackNames) {
|
|
7727
|
+
if (await fileExists(primaryPath)) {
|
|
7728
|
+
return primaryPath;
|
|
7729
|
+
}
|
|
7730
|
+
const pathDirs = (process.env.PATH ?? "").split(path14.delimiter).filter(Boolean);
|
|
7731
|
+
for (const fallbackName of fallbackNames) {
|
|
7732
|
+
for (const dir of pathDirs) {
|
|
7733
|
+
const candidate = path14.join(dir, fallbackName);
|
|
7734
|
+
if (await fileExists(candidate)) {
|
|
7735
|
+
return candidate;
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
}
|
|
7739
|
+
return void 0;
|
|
7740
|
+
}
|
|
7741
|
+
async function resolveOpenCommand(filePath, platform) {
|
|
7453
7742
|
if (platform === "win32") {
|
|
7743
|
+
const resolvedPath2 = await resolveBinaryPath(WINDOWS_OPEN_COMMAND, WINDOWS_FALLBACK_COMMANDS);
|
|
7744
|
+
if (!resolvedPath2) {
|
|
7745
|
+
throw new Error(
|
|
7746
|
+
"Could not find rundll32.exe. Please ensure Windows System32 is accessible or rundll32.exe is available on PATH."
|
|
7747
|
+
);
|
|
7748
|
+
}
|
|
7454
7749
|
return {
|
|
7455
|
-
command:
|
|
7456
|
-
args: ["
|
|
7750
|
+
command: resolvedPath2,
|
|
7751
|
+
args: ["shell32.dll,ShellExec_RunDLL", filePath]
|
|
7457
7752
|
};
|
|
7458
7753
|
}
|
|
7459
7754
|
if (platform === "darwin") {
|
|
7755
|
+
const resolvedPath2 = await resolveBinaryPath(DARWIN_OPEN_COMMAND, DARWIN_FALLBACK_COMMANDS);
|
|
7756
|
+
if (!resolvedPath2) {
|
|
7757
|
+
throw new Error(
|
|
7758
|
+
"Could not find open command. Please ensure macOS is properly configured or open is available on PATH."
|
|
7759
|
+
);
|
|
7760
|
+
}
|
|
7460
7761
|
return {
|
|
7461
|
-
command:
|
|
7762
|
+
command: resolvedPath2,
|
|
7462
7763
|
args: [filePath]
|
|
7463
7764
|
};
|
|
7464
7765
|
}
|
|
7766
|
+
const resolvedPath = await resolveBinaryPath(UNIX_OPEN_COMMAND, UNIX_FALLBACK_COMMANDS);
|
|
7767
|
+
if (!resolvedPath) {
|
|
7768
|
+
throw new Error(
|
|
7769
|
+
"Could not find xdg-open. Please install xdg-utils or ensure it is in your PATH."
|
|
7770
|
+
);
|
|
7771
|
+
}
|
|
7465
7772
|
return {
|
|
7466
|
-
command:
|
|
7773
|
+
command: resolvedPath,
|
|
7467
7774
|
args: [filePath]
|
|
7468
7775
|
};
|
|
7469
7776
|
}
|
|
7470
7777
|
async function spawnDetached(command, args) {
|
|
7471
7778
|
await new Promise((resolve, reject) => {
|
|
7472
|
-
const child =
|
|
7779
|
+
const child = spawn3(command, args, {
|
|
7473
7780
|
detached: true,
|
|
7474
7781
|
stdio: "ignore",
|
|
7475
7782
|
windowsHide: true
|
|
@@ -7494,7 +7801,7 @@ async function spawnDetached(command, args) {
|
|
|
7494
7801
|
async function openShareSvgFile(filePath, deps = {}) {
|
|
7495
7802
|
const platform = deps.platform ?? process.platform;
|
|
7496
7803
|
const runDetached = deps.spawnDetached ?? spawnDetached;
|
|
7497
|
-
const { command, args } = resolveOpenCommand(filePath, platform);
|
|
7804
|
+
const { command, args } = await resolveOpenCommand(filePath, platform);
|
|
7498
7805
|
await runDetached(command, args);
|
|
7499
7806
|
}
|
|
7500
7807
|
async function writeAndOpenShareSvgFile(fileName, svgContent, deps = {}) {
|
|
@@ -7782,7 +8089,7 @@ function resolveTerminalContextLines(optimizeData, options) {
|
|
|
7782
8089
|
const bestRow = rowsWithSavings.length > 0 ? rowsWithSavings.reduce(
|
|
7783
8090
|
(best, current) => (current.savingsUsd ?? Number.NEGATIVE_INFINITY) > (best.savingsUsd ?? Number.NEGATIVE_INFINITY) ? current : best
|
|
7784
8091
|
) : void 0;
|
|
7785
|
-
if (
|
|
8092
|
+
if (bestRow?.savingsUsd === void 0) {
|
|
7786
8093
|
lines.push("ALL best candidate: unavailable (missing baseline or candidate pricing)");
|
|
7787
8094
|
} else if (bestRow.savingsUsd > 0) {
|
|
7788
8095
|
lines.push(
|
|
@@ -9654,7 +9961,7 @@ function registerSharedReportOptions(command, profile) {
|
|
|
9654
9961
|
const allowedSourcesLabel = getAllowedSourcesLabel(supportedSourceIds);
|
|
9655
9962
|
const supportedSourcesSummary = `(${supportedSourceIds.length}): ${allowedSourcesLabel}`;
|
|
9656
9963
|
const profileConfig = sharedOptionProfileConfig[profile];
|
|
9657
|
-
const configuredCommand = command.option("--pi-dir <path>", "Path to .pi sessions directory").option("--codex-dir <path>", "Path to .codex sessions directory").option("--gemini-dir <path>", "Path to .gemini directory").option("--droid-dir <path>", "Path to Droid sessions directory").option("--opencode-db <path>", "Path to OpenCode SQLite DB").option(
|
|
9964
|
+
const configuredCommand = command.option("--pi-dir <path>", "Path to .pi sessions directory").option("--codex-dir <path>", "Path to .codex sessions directory").option("--gemini-dir <path>", "Path to .gemini directory").option("--droid-dir <path>", "Path to Droid sessions directory").option("--claude-dir <path>", "Path to Claude projects directory").option("--opencode-db <path>", "Path to OpenCode SQLite DB").option(
|
|
9658
9965
|
"--source-dir <source-id=path>",
|
|
9659
9966
|
"Override source directory for directory-backed sources (repeatable)",
|
|
9660
9967
|
collectRepeatedOption
|
|
@@ -9669,7 +9976,10 @@ function registerSharedReportOptions(command, profile) {
|
|
|
9669
9976
|
"--model <name>",
|
|
9670
9977
|
"Filter by model (repeatable/comma-separated; exact when exact match exists after source/provider/date filters, otherwise substring)",
|
|
9671
9978
|
collectRepeatedOption
|
|
9672
|
-
).option("--pricing-url <url>", "Override LiteLLM pricing source URL").option(
|
|
9979
|
+
).option("--pricing-url <url>", "Override LiteLLM pricing source URL").option(
|
|
9980
|
+
"--pricing-overrides <path>",
|
|
9981
|
+
"Path to a JSON file of per-model pricing overrides (takes precedence over LiteLLM)"
|
|
9982
|
+
).option("--pricing-offline", "Use cached LiteLLM pricing only (no network fetch)").option(
|
|
9673
9983
|
"--ignore-pricing-failures",
|
|
9674
9984
|
"Continue without estimated costs when pricing cannot be loaded"
|
|
9675
9985
|
).option("--json", "Render output as JSON");
|
|
@@ -9983,15 +10293,6 @@ var { packageName, packageVersion } = loadPackageMetadataFromRuntime();
|
|
|
9983
10293
|
var updateRuntimeConfig = getUpdateNotifierRuntimeConfig();
|
|
9984
10294
|
var cli = createCli({ version: packageVersion });
|
|
9985
10295
|
async function main() {
|
|
9986
|
-
if (process.env[UPDATE_CHECK_REFRESH_ENV_VAR] === "1") {
|
|
9987
|
-
await refreshUpdateCheckCache({
|
|
9988
|
-
packageName,
|
|
9989
|
-
currentVersion: packageVersion,
|
|
9990
|
-
cacheTtlMs: updateRuntimeConfig.cacheTtlMs,
|
|
9991
|
-
fetchTimeoutMs: updateRuntimeConfig.fetchTimeoutMs
|
|
9992
|
-
});
|
|
9993
|
-
return;
|
|
9994
|
-
}
|
|
9995
10296
|
const updateResult = await checkForUpdatesAndMaybeRestart({
|
|
9996
10297
|
packageName,
|
|
9997
10298
|
currentVersion: packageVersion,
|