tokentracker-cli 0.93.7 → 0.94.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.de.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dashboard/dist/assets/{AchievementBadge-DqvblIQY.js → AchievementBadge-DnmgEa4q.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-C-xm27pB.js → AchievementsPage-B7YTbAuK.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-D8Oe2FGJ.js → AchievementsSection-CwgT5sqH.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-BJOcOnHo.js → BadgeDetailModal-Dets6jzW.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-6fB046ld.js → BotAnimated-5KwUvC9F.js} +1 -1
- package/dashboard/dist/assets/{Card-BKbWtBaL.js → Card-jf9w-4-N.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-DbpeAldp.js → ClawdAnimated-CGxDVQ0c.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-Jte7TBon.js → CommandPalette-B1bUUf32.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-DDm7tCXO.js → CommunityStatsModal-DRDpZycV.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-CrIjcrpZ.js → ConfirmModal-BR-aIRH9.js} +1 -1
- package/dashboard/dist/assets/{Controls-BWJtMkIy.js → Controls-CbHsJAqV.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-Bcq4EwG5.js → DashboardPage-B5XMQoxy.js} +1 -1
- package/dashboard/dist/assets/{DevicePage-C9NcPZGX.js → DevicePage-DDlicLLn.js} +1 -1
- package/dashboard/dist/assets/{DialogClose-CY-LguUG.js → DialogClose-B89uLXqp.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-DiVbwvTR.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-BkAmJrQ_.js → DialogTitle-jOyycYKF.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-U-bxcM-y.js → FadeIn-BEND42ze.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-4HUCxVhg.js → HeaderGithubStar-Dacl1WGS.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-BXcXOCs8.js → HoverTooltip-B2Mh2LTi.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-Cppkc-gE.js → IpCheckPage-CPEuRhiU.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-D3KXfYKE.js → LandingPage-BYWNR_dx.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-CdsHg6L0.js → LeaderboardAvatar-B9_5LDVj.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-niat0TZW.js → LeaderboardPage-_Hl1NaIa.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-DgjhhEbB.js → LeaderboardProfileModal-Cydp7CyA.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-CZej8gpc.js → LeaderboardProfilePage-Dz0ecujs.js} +1 -1
- package/dashboard/dist/assets/LimitsPage-CmdT2qOZ.js +5 -0
- package/dashboard/dist/assets/{LocalOnlyNotice-DQswDMSD.js → LocalOnlyNotice-DhuPGUkw.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-glPyVqYh.js → LoginCard-DQtHaX_5.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-slsdEXC7.js → LoginModal-DvpPRnYV.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-DX5QT917.js → LoginPage-DOjfmdwb.js} +1 -1
- package/dashboard/dist/assets/{PetPage-B8U3CJwt.js → PetPage-Dd6U9fh2.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-BVPcQAio.js → PopoverPopup-B1V01u10.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-CljIacVj.js → ProviderIcon-BLRe55c7.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-DTBmQk2y.js → ResetPasswordPage-BJ95vy1S.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-C6npelCT.js → SegmentedControl-DacS_m9M.js} +1 -1
- package/dashboard/dist/assets/{Select-D1elkRBG.js → Select-bQDO5BEA.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-3N-hvYuD.js → SelectItemText-CUGrs9CY.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-C5ZK9X2o.js → ServiceStatusPage-CsERvmVC.js} +1 -1
- package/dashboard/dist/assets/SessionsPage-0VvqEfPx.js +2 -0
- package/dashboard/dist/assets/SettingsPage-9EHh2HfD.js +2 -0
- package/dashboard/dist/assets/{SkillsPage-Cwkevv29.js → SkillsPage-B1AcLX41.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-a1-E6_jp.js → TokenGalaxy-DSKTHQSs.js} +1 -1
- package/dashboard/dist/assets/{ToolbarRootContext-Cm5cQb9Z.js → ToolbarRootContext-DP4mQLbq.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-DoNDyarF.js → TrendMonitor-rabtcE5w.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-QMTmKnRl.js → WidgetsPage-Cr6Wg9QX.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-C9rJcY2o.js → WrappedPage-DqQvRcnA.js} +1 -1
- package/dashboard/dist/assets/__vite-browser-external-BHz2N7Ih.css +1 -0
- package/dashboard/dist/assets/{__vite-browser-external-CsbVaBbw.js → __vite-browser-external-C8KwSyTz.js} +12 -12
- package/dashboard/dist/assets/{agent-logos-B39DKy3X.js → agent-logos-BjIh_mqf.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-Cw5mMBik.js → arrow-up-right-BIhbN0NO.js} +1 -1
- package/dashboard/dist/assets/{check-DrQztnq1.js → check-U-wNzPvd.js} +1 -1
- package/dashboard/dist/assets/{chevron-right-CEelysET.js → chevron-right-Du-QVNj4.js} +1 -1
- package/dashboard/dist/assets/{composite-MhgOkYfv.js → composite-BR91CxNl.js} +1 -1
- package/dashboard/dist/assets/{copy-Bouz-qQR.js → copy-Bw3hXBCu.js} +1 -1
- package/dashboard/dist/assets/{download-BugS2cl5.js → download-WOqcQzvK.js} +1 -1
- package/dashboard/dist/assets/{external-link-Ceqto-f4.js → external-link-BZtf6WfS.js} +1 -1
- package/dashboard/dist/assets/{flame-B67y0kLz.js → flame-B5kp2VI9.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-CR8njNJs.js → format-tokens-BOCnuMuJ.js} +1 -1
- package/dashboard/dist/assets/{icons-DFj5ctTx.js → icons-CA3FiIa_.js} +1 -1
- package/dashboard/dist/assets/{index-ZGfRrxuq.js → index-DQaanK52.js} +1 -1
- package/dashboard/dist/assets/{index-CrymIhtC.js → index-DjWC63Bs.js} +1 -1
- package/dashboard/dist/assets/{index-Bx6EP36K.js → index-mEG0HZ-a.js} +1 -1
- package/dashboard/dist/assets/{info-C6XoktLa.js → info-Djer9i3-.js} +1 -1
- package/dashboard/dist/assets/{limits-providers-B9L6hIZT.js → limits-providers-F-RnNFAb.js} +1 -1
- package/dashboard/dist/assets/{link-2-DKG2j43e.js → link-2-DV4ocD0l.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-DB2txq05.js → loader-circle-DJK72c-o.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-3-dz0brp.js → maximize-2-BwBA_lMm.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-D0KjKi1o.js → modifiers.esm-B3sl4qyZ.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-Ds8H_QW9.js → monitor-smartphone-BUTpM4ng.js} +1 -1
- package/dashboard/dist/assets/{plus-BDsmAe4Z.js → plus-BA9RP5L6.js} +1 -1
- package/dashboard/dist/assets/{provider-display-DRz643iD.js → provider-display-DqsJzGSr.js} +1 -1
- package/dashboard/dist/assets/{react-D3Afmy9b.js → react-BYw5AVSz.js} +1 -1
- package/dashboard/dist/assets/{search-CO2Xf-Kg.js → search-BzcnFWVD.js} +1 -1
- package/dashboard/dist/assets/{skills-api-C1BZuUIq.js → skills-api-vAME1P7G.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-DutNEc4F.js → sortable.esm-BpEiRqqI.js} +1 -1
- package/dashboard/dist/assets/{store-a-oUpY-g.js → store-GeT5fhRX.js} +1 -1
- package/dashboard/dist/assets/{terminal-DAePk67T.js → terminal-CZCMYQ8K.js} +1 -1
- package/dashboard/dist/assets/{trash-2-bnFmtRSG.js → trash-2-DzlWEwKO.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-PJBeXyek.js → use-community-stats-DOvJfj-i.js} +1 -1
- package/dashboard/dist/assets/use-limits-display-prefs-BmLs4HyN.js +1 -0
- package/dashboard/dist/assets/{use-ordered-list-BaVvCzZ9.js → use-ordered-list-BgozDSSV.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-Bc3RFJdg.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-COTG-zJs.js → use-session-efficiency-pref-HP1ckVkS.js} +1 -1
- package/dashboard/dist/assets/{use-transform-BQEPyChL.js → use-transform-B4VSogOx.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-LR2gHJJn.js → use-usage-limits-DNNhD2H6.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-CeqKrzFz.js → useCurrency-CSuzCBAL.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-D0IMlZlO.js → useTokenFormat-ClnL9OFe.js} +1 -1
- package/dashboard/dist/assets/{zap-BBn6G5_E.js → zap-C_T9CCiS.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/ip-check.html +2 -2
- package/dashboard/dist/leaderboard.html +2 -2
- package/dashboard/dist/share.html +2 -2
- package/package.json +1 -1
- package/src/commands/status.js +3 -1
- package/src/commands/sync.js +8 -3
- package/src/lib/codex-rollout-parser.js +35 -4
- package/src/lib/grok-context-breakdown.js +24 -33
- package/src/lib/grok-usage.js +95 -0
- package/src/lib/local-api.js +2 -0
- package/src/lib/pricing/index.js +10 -0
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +817 -239
- package/src/lib/session-analytics.js +305 -50
- package/src/lib/telemetry.js +2 -2
- package/src/lib/usage-limits.js +20 -10
- package/dashboard/dist/assets/DialogDescription-DPyhpdeR.js +0 -1
- package/dashboard/dist/assets/LimitsPage-BXzzX87I.js +0 -5
- package/dashboard/dist/assets/SessionsPage-CJ0JvE8-.js +0 -2
- package/dashboard/dist/assets/SettingsPage-C32uYzqG.js +0 -2
- package/dashboard/dist/assets/__vite-browser-external-D95WhsZe.css +0 -1
- package/dashboard/dist/assets/use-limits-display-prefs-Blvn-7CB.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-f51jgK_S.js +0 -1
|
@@ -31,6 +31,7 @@ const readline = require("node:readline");
|
|
|
31
31
|
const { listClaudeProjectFiles, listRolloutFilesDeep, claudeMessageDedupKey } = require("./rollout");
|
|
32
32
|
const { parseCodexRolloutFile } = require("./codex-rollout-parser");
|
|
33
33
|
const { computeRowCost } = require("./pricing");
|
|
34
|
+
const { USD_TICKS_PER_USD, normalizeGrokUsage } = require("./grok-usage");
|
|
34
35
|
const wsl = require("./wsl-probe");
|
|
35
36
|
|
|
36
37
|
// Bump the sidecar when derived metrics change so cached rows are rebuilt
|
|
@@ -54,7 +55,11 @@ const wsl = require("./wsl-probe");
|
|
|
54
55
|
// stay valid while Grok entries appear on the next full rebuild.
|
|
55
56
|
// v11 groups native/WSL copies of one logical Claude/Codex session and scans
|
|
56
57
|
// their union, deduping shared records while retaining divergent tails.
|
|
57
|
-
|
|
58
|
+
// v12 fixes Grok's mutually exclusive token columns, retains exact reported
|
|
59
|
+
// cost, and adds metadata-only usage diagnostics to the local session browser.
|
|
60
|
+
// v12 retains Codex parent/subagent metadata locally and derives exact child
|
|
61
|
+
// token totals from observed child rollouts instead of spawn-call estimates.
|
|
62
|
+
const SIDECAR_VERSION = 12;
|
|
58
63
|
const EDIT_TOOLS = new Set([
|
|
59
64
|
"apply_patch",
|
|
60
65
|
"edit",
|
|
@@ -238,7 +243,12 @@ function finalizeRecord(record) {
|
|
|
238
243
|
record.active_ms = Math.max(0, finite(record.active_ms));
|
|
239
244
|
record.duration_ms = record.active_ms;
|
|
240
245
|
record.total_tokens = finite(record.total_tokens || record.tokens?.total_tokens);
|
|
241
|
-
|
|
246
|
+
const providerCost = Number(record.provider_cost_usd);
|
|
247
|
+
record.cost_usd = record.cost_source === "provider_reported"
|
|
248
|
+
&& Number.isFinite(providerCost)
|
|
249
|
+
&& providerCost >= 0
|
|
250
|
+
? providerCost
|
|
251
|
+
: computeRowCost({ source: record.source, model: record.model, ...record.tokens });
|
|
242
252
|
record.productive = record.edit_turns > 0;
|
|
243
253
|
// A first-pass delivery has exactly one user turn containing an observed
|
|
244
254
|
// edit and no repeated user request. The legacy one_shot field stays as an
|
|
@@ -511,6 +521,18 @@ async function scanCodexSession(filePath) {
|
|
|
511
521
|
version: SIDECAR_VERSION,
|
|
512
522
|
session_hash: sessionHash("codex", parsed.sessionId || primaryFilePath),
|
|
513
523
|
session_id: parsed.sessionId || null,
|
|
524
|
+
// Local-only lineage. Codex v2 normally writes forked_from_id; keep
|
|
525
|
+
// parent_thread_id as a compatibility fallback, but reject disagreement.
|
|
526
|
+
parent_session_id: parsed.forkedFromId || parsed.parentThreadId || null,
|
|
527
|
+
parent_link_conflict: Boolean(
|
|
528
|
+
parsed.forkedFromId
|
|
529
|
+
&& parsed.parentThreadId
|
|
530
|
+
&& parsed.forkedFromId !== parsed.parentThreadId
|
|
531
|
+
),
|
|
532
|
+
forked_from_id: parsed.forkedFromId || null,
|
|
533
|
+
thread_source: typeof parsed.threadSource === "string" ? parsed.threadSource : null,
|
|
534
|
+
agent_nickname: typeof parsed.agentNickname === "string" ? parsed.agentNickname : null,
|
|
535
|
+
agent_role: typeof parsed.agentRole === "string" ? parsed.agentRole : null,
|
|
514
536
|
// Local-only: stripped in summarizeSessions before any cloud/CSV export.
|
|
515
537
|
title,
|
|
516
538
|
source: "codex",
|
|
@@ -569,42 +591,6 @@ function grokTimestampIso(obj) {
|
|
|
569
591
|
return new Date(ms).toISOString();
|
|
570
592
|
}
|
|
571
593
|
|
|
572
|
-
// Grok reports inputTokens as the full prompt (including cache hits). Split so
|
|
573
|
-
// pricing can apply cache_read rates correctly — same authority as the usage
|
|
574
|
-
// parser and grok-context-breakdown.readUsageTotals. Prefer the reported
|
|
575
|
-
// totalTokens; do not re-sum input+cached (that double-counts cache hits).
|
|
576
|
-
function grokUsageTotals(usage) {
|
|
577
|
-
if (!usage || typeof usage !== "object") return emptyTotals();
|
|
578
|
-
const inputRaw = finite(usage.inputTokens ?? usage.input_tokens);
|
|
579
|
-
const cached_input_tokens = finite(
|
|
580
|
-
usage.cachedReadTokens ?? usage.cache_read_input_tokens ?? usage.cached_input_tokens,
|
|
581
|
-
);
|
|
582
|
-
const cache_creation_input_tokens = finite(
|
|
583
|
-
usage.cachedWriteTokens ?? usage.cache_creation_input_tokens,
|
|
584
|
-
);
|
|
585
|
-
const output_tokens = finite(usage.outputTokens ?? usage.output_tokens);
|
|
586
|
-
const reasoning_output_tokens = finite(
|
|
587
|
-
usage.reasoningTokens ?? usage.reasoning_output_tokens,
|
|
588
|
-
);
|
|
589
|
-
const input_tokens = Math.max(0, inputRaw - cached_input_tokens);
|
|
590
|
-
let total_tokens = finite(usage.totalTokens ?? usage.total_tokens);
|
|
591
|
-
if (total_tokens <= 0) {
|
|
592
|
-
total_tokens = input_tokens
|
|
593
|
-
+ cached_input_tokens
|
|
594
|
-
+ cache_creation_input_tokens
|
|
595
|
-
+ output_tokens
|
|
596
|
-
+ reasoning_output_tokens;
|
|
597
|
-
}
|
|
598
|
-
return {
|
|
599
|
-
input_tokens,
|
|
600
|
-
cached_input_tokens,
|
|
601
|
-
cache_creation_input_tokens,
|
|
602
|
-
output_tokens,
|
|
603
|
-
reasoning_output_tokens,
|
|
604
|
-
total_tokens,
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
|
|
608
594
|
function pickGrokModel(usage, fallback) {
|
|
609
595
|
const modelUsage = usage?.modelUsage;
|
|
610
596
|
if (modelUsage && typeof modelUsage === "object") {
|
|
@@ -704,6 +690,13 @@ async function scanGrokSession(filePath) {
|
|
|
704
690
|
let currentHadEdit = false;
|
|
705
691
|
let subagentCalls = 0;
|
|
706
692
|
const subagentTypes = new Map();
|
|
693
|
+
let usageEvents = 0;
|
|
694
|
+
let providerCostEvents = 0;
|
|
695
|
+
let providerCostUsd = 0;
|
|
696
|
+
let modelCalls = 0;
|
|
697
|
+
let apiDurationMs = 0;
|
|
698
|
+
let usageIsIncomplete = false;
|
|
699
|
+
let costIsPartial = false;
|
|
707
700
|
let lastPromptFingerprint = null;
|
|
708
701
|
// Grok streams user_message_chunk pieces for one prompt. Accumulate until
|
|
709
702
|
// turn_completed, then fingerprint the full prompt for retry detection.
|
|
@@ -772,7 +765,21 @@ async function scanGrokSession(filePath) {
|
|
|
772
765
|
if (sessionUpdate === "turn_completed") {
|
|
773
766
|
closeTurn();
|
|
774
767
|
const usage = update.usage;
|
|
775
|
-
|
|
768
|
+
const normalizedUsage = normalizeGrokUsage(usage);
|
|
769
|
+
if (normalizedUsage) {
|
|
770
|
+
usageEvents += 1;
|
|
771
|
+
addTotals(tokens, normalizedUsage);
|
|
772
|
+
modelCalls += finite(normalizedUsage.model_calls);
|
|
773
|
+
apiDurationMs += finite(normalizedUsage.api_duration_ms);
|
|
774
|
+
usageIsIncomplete ||= normalizedUsage.usage_is_incomplete;
|
|
775
|
+
costIsPartial ||= normalizedUsage.cost_is_partial;
|
|
776
|
+
if (normalizedUsage.total_cost_usd != null) {
|
|
777
|
+
providerCostEvents += 1;
|
|
778
|
+
providerCostUsd = Math.round(
|
|
779
|
+
(providerCostUsd + normalizedUsage.total_cost_usd) * USD_TICKS_PER_USD,
|
|
780
|
+
) / USD_TICKS_PER_USD;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
776
783
|
const candidate = pickGrokModel(usage, model);
|
|
777
784
|
if (candidate && candidate !== "unknown") model = candidate;
|
|
778
785
|
continue;
|
|
@@ -796,6 +803,11 @@ async function scanGrokSession(filePath) {
|
|
|
796
803
|
model = normalizeSessionModel(signals.primaryModelId) || model;
|
|
797
804
|
}
|
|
798
805
|
|
|
806
|
+
const hasCompleteProviderCost = usageEvents > 0
|
|
807
|
+
&& providerCostEvents === usageEvents
|
|
808
|
+
&& !usageIsIncomplete
|
|
809
|
+
&& !costIsPartial;
|
|
810
|
+
|
|
799
811
|
return finalizeRecord({
|
|
800
812
|
version: SIDECAR_VERSION,
|
|
801
813
|
session_hash: sessionHash("grok", observedSessionId || filePath),
|
|
@@ -813,7 +825,29 @@ async function scanGrokSession(filePath) {
|
|
|
813
825
|
subagent_calls: subagentCalls,
|
|
814
826
|
subagent_types: Object.fromEntries([...subagentTypes.entries()].sort()),
|
|
815
827
|
tokens,
|
|
816
|
-
|
|
828
|
+
usage_events: usageEvents,
|
|
829
|
+
usage_precision: usageIsIncomplete ? "reported_incomplete" : (usageEvents > 0 ? "reported" : "unavailable"),
|
|
830
|
+
usage_is_incomplete: usageIsIncomplete,
|
|
831
|
+
cost_is_partial: costIsPartial,
|
|
832
|
+
cost_source: hasCompleteProviderCost ? "provider_reported" : "model_pricing",
|
|
833
|
+
provider_cost_usd: hasCompleteProviderCost ? providerCostUsd : null,
|
|
834
|
+
model_calls: modelCalls,
|
|
835
|
+
api_duration_ms: apiDurationMs,
|
|
836
|
+
context_tokens_used: finite(signals.contextTokensUsed),
|
|
837
|
+
context_window_tokens: finite(signals.contextWindowTokens),
|
|
838
|
+
context_usage_percent: finite(signals.contextWindowUsage),
|
|
839
|
+
tool_calls: finite(signals.toolCallCount),
|
|
840
|
+
tool_failures: finite(signals.toolFailureCount),
|
|
841
|
+
error_count: finite(signals.errorCount),
|
|
842
|
+
compaction_count: finite(signals.compactionCount),
|
|
843
|
+
provenance: {
|
|
844
|
+
source: "local-session-log",
|
|
845
|
+
confidence: usageIsIncomplete ? "partial" : "observed",
|
|
846
|
+
retry_confidence: "inferred",
|
|
847
|
+
content_retained: false,
|
|
848
|
+
usage: usageEvents > 0 ? "turn_completed.usage" : "unavailable",
|
|
849
|
+
cost: hasCompleteProviderCost ? "provider_reported" : "model_pricing",
|
|
850
|
+
},
|
|
817
851
|
});
|
|
818
852
|
}
|
|
819
853
|
|
|
@@ -829,7 +863,14 @@ function providerRoots(home, providerDir, env, deps = {}) {
|
|
|
829
863
|
const discoverWslHome = deps.discoverWslHome || wsl.discoverWslHome;
|
|
830
864
|
const roots = [];
|
|
831
865
|
if (platform !== "win32" || wsl.shouldProbeNative(env)) {
|
|
832
|
-
|
|
866
|
+
const useProcessCodexHome = providerDir === ".codex"
|
|
867
|
+
&& path.resolve(home) === path.resolve(homedir())
|
|
868
|
+
&& typeof env?.CODEX_HOME === "string"
|
|
869
|
+
&& env.CODEX_HOME.trim();
|
|
870
|
+
const nativeRoot = useProcessCodexHome
|
|
871
|
+
? path.resolve(env.CODEX_HOME.trim())
|
|
872
|
+
: path.join(home, providerDir);
|
|
873
|
+
roots.push(nativeRoot);
|
|
833
874
|
}
|
|
834
875
|
// Only the machine's own home has a WSL sibling worth probing.
|
|
835
876
|
// discoverWslHome resolves \\wsl$ independently of `home`, so probing for an
|
|
@@ -1029,10 +1070,15 @@ function sessionFileStatKey(filePath) {
|
|
|
1029
1070
|
const codexTitleIndexCache = new Map();
|
|
1030
1071
|
|
|
1031
1072
|
function codexTitleIndexPathFor(filePath) {
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1073
|
+
let current = path.dirname(path.resolve(filePath));
|
|
1074
|
+
while (true) {
|
|
1075
|
+
if (["sessions", "archived_sessions"].includes(path.basename(current))) {
|
|
1076
|
+
return path.join(path.dirname(current), "session_index.jsonl");
|
|
1077
|
+
}
|
|
1078
|
+
const parent = path.dirname(current);
|
|
1079
|
+
if (parent === current) return null;
|
|
1080
|
+
current = parent;
|
|
1081
|
+
}
|
|
1036
1082
|
}
|
|
1037
1083
|
|
|
1038
1084
|
function loadCodexTitleIndex(filePath) {
|
|
@@ -1239,7 +1285,9 @@ function withinDayRange(row, from, to) {
|
|
|
1239
1285
|
}
|
|
1240
1286
|
|
|
1241
1287
|
function summarizeSessions(sessions, { from = "", to = "", includeSessions = true } = {}) {
|
|
1242
|
-
const filtered = (sessions || [])
|
|
1288
|
+
const filtered = annotateCodexThreadUsage((sessions || [])
|
|
1289
|
+
.filter((row) => withinDayRange(row, from, to))
|
|
1290
|
+
.map((row) => ({ ...row })));
|
|
1243
1291
|
const byModel = new Map();
|
|
1244
1292
|
const subagents = new Map();
|
|
1245
1293
|
for (const row of filtered) {
|
|
@@ -1268,6 +1316,26 @@ function summarizeSessions(sessions, { from = "", to = "", includeSessions = tru
|
|
|
1268
1316
|
agg.edit_cost_usd += finite(row.cost_usd);
|
|
1269
1317
|
}
|
|
1270
1318
|
byModel.set(key, agg);
|
|
1319
|
+
if (row.source === "codex" && row.parent_session_hash) {
|
|
1320
|
+
const name = row.agent_role || row.agent_nickname || "subagent";
|
|
1321
|
+
const sub = subagents.get(name) || {
|
|
1322
|
+
name,
|
|
1323
|
+
calls: 0,
|
|
1324
|
+
sessions: 0,
|
|
1325
|
+
total_tokens: 0,
|
|
1326
|
+
cost_usd: 0,
|
|
1327
|
+
attribution: "observed-child-session",
|
|
1328
|
+
};
|
|
1329
|
+
sub.calls += 1;
|
|
1330
|
+
sub.sessions += 1;
|
|
1331
|
+
sub.total_tokens += finite(row.total_tokens);
|
|
1332
|
+
sub.cost_usd += finite(row.cost_usd);
|
|
1333
|
+
subagents.set(name, sub);
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
// Codex child rollouts are first-class rows. A spawn call in the parent is
|
|
1337
|
+
// a control-plane event, not token usage, so do not mix in an estimate.
|
|
1338
|
+
if (row.source === "codex") continue;
|
|
1271
1339
|
for (const [name, calls] of Object.entries(row.subagent_types || {})) {
|
|
1272
1340
|
const sub = subagents.get(name) || { name, calls: 0, sessions: 0, total_tokens: 0, cost_usd: 0 };
|
|
1273
1341
|
sub.calls += finite(calls);
|
|
@@ -1301,7 +1369,22 @@ function summarizeSessions(sessions, { from = "", to = "", includeSessions = tru
|
|
|
1301
1369
|
// Git attribution and the local-only session browser, but never leave the
|
|
1302
1370
|
// Node process through API/CSV payloads.
|
|
1303
1371
|
sessions: includeSessions
|
|
1304
|
-
? filtered.map(({
|
|
1372
|
+
? filtered.map(({
|
|
1373
|
+
project_ref: _projectRef,
|
|
1374
|
+
session_id: _sessionId,
|
|
1375
|
+
parent_session_id: _parentSessionId,
|
|
1376
|
+
forked_from_id: _forkedFromId,
|
|
1377
|
+
parent_session_hash: _parentSessionHash,
|
|
1378
|
+
root_session_hash: _rootSessionHash,
|
|
1379
|
+
agent_nickname: _agentNickname,
|
|
1380
|
+
agent_role: _agentRole,
|
|
1381
|
+
thread_source: _threadSource,
|
|
1382
|
+
parent_link_conflict: _parentLinkConflict,
|
|
1383
|
+
orphaned_subagent: _orphanedSubagent,
|
|
1384
|
+
title: _title,
|
|
1385
|
+
_cache_key: _cacheKey,
|
|
1386
|
+
...row
|
|
1387
|
+
}) => row)
|
|
1305
1388
|
: [],
|
|
1306
1389
|
session_count: filtered.length,
|
|
1307
1390
|
summary: {
|
|
@@ -1347,9 +1430,18 @@ function resumeCommandFor(source, sessionId) {
|
|
|
1347
1430
|
}
|
|
1348
1431
|
|
|
1349
1432
|
function toSessionBrowserRow(row) {
|
|
1433
|
+
const tokens = row.tokens && typeof row.tokens === "object" ? row.tokens : {};
|
|
1350
1434
|
return {
|
|
1351
1435
|
session_hash: row.session_hash,
|
|
1352
1436
|
session_id: row.session_id || null,
|
|
1437
|
+
parent_session_id: row.parent_session_id || null,
|
|
1438
|
+
parent_session_hash: row.parent_session_hash || null,
|
|
1439
|
+
root_session_hash: row.root_session_hash || row.session_hash,
|
|
1440
|
+
thread_kind: row.parent_session_id ? "subagent" : "root",
|
|
1441
|
+
agent_nickname: row.agent_nickname || null,
|
|
1442
|
+
agent_role: row.agent_role || null,
|
|
1443
|
+
orphaned_subagent: Boolean(row.orphaned_subagent),
|
|
1444
|
+
parent_link_conflict: Boolean(row.parent_link_conflict),
|
|
1353
1445
|
title: row.title || null,
|
|
1354
1446
|
source: row.source,
|
|
1355
1447
|
project_key: row.project_key,
|
|
@@ -1364,8 +1456,35 @@ function toSessionBrowserRow(row) {
|
|
|
1364
1456
|
edit_turns: finite(row.edit_turns),
|
|
1365
1457
|
retry_turns: finite(row.retry_turns),
|
|
1366
1458
|
subagent_calls: finite(row.subagent_calls),
|
|
1459
|
+
input_tokens: finite(tokens.input_tokens),
|
|
1460
|
+
cached_input_tokens: finite(tokens.cached_input_tokens),
|
|
1461
|
+
cache_creation_input_tokens: finite(tokens.cache_creation_input_tokens),
|
|
1462
|
+
output_tokens: finite(tokens.output_tokens),
|
|
1463
|
+
reasoning_output_tokens: finite(tokens.reasoning_output_tokens),
|
|
1464
|
+
direct_subagent_count: finite(row.direct_subagent_count),
|
|
1465
|
+
descendant_subagent_count: finite(row.descendant_subagent_count),
|
|
1466
|
+
own_total_tokens: finite(row.total_tokens),
|
|
1467
|
+
subagent_total_tokens: finite(row.subagent_total_tokens),
|
|
1468
|
+
combined_total_tokens: finite(row.combined_total_tokens || row.total_tokens),
|
|
1367
1469
|
total_tokens: finite(row.total_tokens),
|
|
1470
|
+
own_cost_usd: finite(row.cost_usd),
|
|
1471
|
+
subagent_cost_usd: finite(row.subagent_cost_usd),
|
|
1472
|
+
combined_cost_usd: finite(row.combined_cost_usd || row.cost_usd),
|
|
1368
1473
|
cost_usd: finite(row.cost_usd),
|
|
1474
|
+
cost_source: row.cost_source || null,
|
|
1475
|
+
usage_precision: row.usage_precision || null,
|
|
1476
|
+
usage_is_incomplete: Boolean(row.usage_is_incomplete),
|
|
1477
|
+
cost_is_partial: Boolean(row.cost_is_partial),
|
|
1478
|
+
usage_events: finite(row.usage_events),
|
|
1479
|
+
model_calls: finite(row.model_calls),
|
|
1480
|
+
api_duration_ms: finite(row.api_duration_ms),
|
|
1481
|
+
context_tokens_used: finite(row.context_tokens_used),
|
|
1482
|
+
context_window_tokens: finite(row.context_window_tokens),
|
|
1483
|
+
context_usage_percent: finite(row.context_usage_percent),
|
|
1484
|
+
tool_calls: finite(row.tool_calls),
|
|
1485
|
+
tool_failures: finite(row.tool_failures),
|
|
1486
|
+
error_count: finite(row.error_count),
|
|
1487
|
+
compaction_count: finite(row.compaction_count),
|
|
1369
1488
|
productive: Boolean(row.productive),
|
|
1370
1489
|
first_pass: Boolean(row.first_pass ?? row.one_shot),
|
|
1371
1490
|
resume_command: resumeCommandFor(row.source, row.session_id),
|
|
@@ -1384,18 +1503,46 @@ function mergeSessionFragments(rows) {
|
|
|
1384
1503
|
const key = row.session_hash;
|
|
1385
1504
|
const cur = byHash.get(key);
|
|
1386
1505
|
if (!cur) {
|
|
1387
|
-
byHash.set(key, {
|
|
1506
|
+
byHash.set(key, {
|
|
1507
|
+
...row,
|
|
1508
|
+
tokens: row.tokens && typeof row.tokens === "object" ? { ...row.tokens } : emptyTotals(),
|
|
1509
|
+
_repr_tokens: finite(row.total_tokens),
|
|
1510
|
+
});
|
|
1388
1511
|
continue;
|
|
1389
1512
|
}
|
|
1390
1513
|
cur.turns = finite(cur.turns) + finite(row.turns);
|
|
1391
1514
|
cur.edit_turns = finite(cur.edit_turns) + finite(row.edit_turns);
|
|
1392
1515
|
cur.retry_turns = finite(cur.retry_turns) + finite(row.retry_turns);
|
|
1393
1516
|
cur.subagent_calls = finite(cur.subagent_calls) + finite(row.subagent_calls);
|
|
1517
|
+
addTotals(cur.tokens, row.tokens);
|
|
1394
1518
|
cur.total_tokens = finite(cur.total_tokens) + finite(row.total_tokens);
|
|
1395
1519
|
cur.cost_usd = finite(cur.cost_usd) + finite(row.cost_usd);
|
|
1520
|
+
cur.provider_cost_usd = finite(cur.provider_cost_usd) + finite(row.provider_cost_usd);
|
|
1521
|
+
cur.usage_events = finite(cur.usage_events) + finite(row.usage_events);
|
|
1522
|
+
cur.model_calls = finite(cur.model_calls) + finite(row.model_calls);
|
|
1523
|
+
cur.api_duration_ms = finite(cur.api_duration_ms) + finite(row.api_duration_ms);
|
|
1524
|
+
cur.tool_calls = finite(cur.tool_calls) + finite(row.tool_calls);
|
|
1525
|
+
cur.tool_failures = finite(cur.tool_failures) + finite(row.tool_failures);
|
|
1526
|
+
cur.error_count = finite(cur.error_count) + finite(row.error_count);
|
|
1527
|
+
cur.compaction_count = finite(cur.compaction_count) + finite(row.compaction_count);
|
|
1528
|
+
cur.context_tokens_used = Math.max(finite(cur.context_tokens_used), finite(row.context_tokens_used));
|
|
1529
|
+
cur.context_window_tokens = Math.max(finite(cur.context_window_tokens), finite(row.context_window_tokens));
|
|
1530
|
+
cur.context_usage_percent = Math.max(finite(cur.context_usage_percent), finite(row.context_usage_percent));
|
|
1531
|
+
cur.usage_is_incomplete = Boolean(cur.usage_is_incomplete) || Boolean(row.usage_is_incomplete);
|
|
1532
|
+
cur.cost_is_partial = Boolean(cur.cost_is_partial) || Boolean(row.cost_is_partial);
|
|
1533
|
+
if (cur.usage_precision !== row.usage_precision) cur.usage_precision = "mixed";
|
|
1534
|
+
if (cur.cost_source !== row.cost_source) cur.cost_source = "mixed";
|
|
1396
1535
|
cur.productive = Boolean(cur.productive) || Boolean(row.productive);
|
|
1397
1536
|
cur.active_ms = finite(cur.active_ms) + finite(row.active_ms);
|
|
1398
1537
|
if (!cur.title && row.title) cur.title = row.title;
|
|
1538
|
+
if (!cur.parent_session_id && row.parent_session_id) cur.parent_session_id = row.parent_session_id;
|
|
1539
|
+
if (cur.parent_session_id && row.parent_session_id && cur.parent_session_id !== row.parent_session_id) {
|
|
1540
|
+
cur.parent_session_id = null;
|
|
1541
|
+
cur.parent_link_conflict = true;
|
|
1542
|
+
}
|
|
1543
|
+
if (!cur.forked_from_id && row.forked_from_id) cur.forked_from_id = row.forked_from_id;
|
|
1544
|
+
if (!cur.agent_nickname && row.agent_nickname) cur.agent_nickname = row.agent_nickname;
|
|
1545
|
+
if (!cur.agent_role && row.agent_role) cur.agent_role = row.agent_role;
|
|
1399
1546
|
// Representative model/project comes from the busiest fragment (most
|
|
1400
1547
|
// tokens) so a tiny sidechain cannot overwrite the real coding model.
|
|
1401
1548
|
if (finite(row.total_tokens) > finite(cur._repr_tokens)) {
|
|
@@ -1427,19 +1574,127 @@ function mergeSessionFragments(rows) {
|
|
|
1427
1574
|
return merged;
|
|
1428
1575
|
}
|
|
1429
1576
|
|
|
1577
|
+
// Attach exact Codex parent/child usage without changing the authoritative
|
|
1578
|
+
// per-session total_tokens field. Combined fields are derived views only, so
|
|
1579
|
+
// summing total_tokens across all rows still equals the global usage ledger.
|
|
1580
|
+
function annotateCodexThreadUsage(rows) {
|
|
1581
|
+
const byId = new Map();
|
|
1582
|
+
const childrenById = new Map();
|
|
1583
|
+
const linkedParentById = new Map();
|
|
1584
|
+
|
|
1585
|
+
for (const row of rows || []) {
|
|
1586
|
+
if (row?.source !== "codex" || typeof row.session_id !== "string" || !row.session_id) continue;
|
|
1587
|
+
const existing = byId.get(row.session_id);
|
|
1588
|
+
if (existing && existing !== row) {
|
|
1589
|
+
existing.parent_link_conflict = true;
|
|
1590
|
+
row.parent_link_conflict = true;
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
byId.set(row.session_id, row);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
function hasParentCycle(start) {
|
|
1597
|
+
const seen = new Set();
|
|
1598
|
+
let current = start;
|
|
1599
|
+
while (current?.parent_session_id && byId.has(current.parent_session_id)) {
|
|
1600
|
+
if (seen.has(current.session_id) || current.parent_session_id === current.session_id) return true;
|
|
1601
|
+
seen.add(current.session_id);
|
|
1602
|
+
current = byId.get(current.parent_session_id);
|
|
1603
|
+
}
|
|
1604
|
+
return false;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
for (const row of byId.values()) {
|
|
1608
|
+
const parentId = typeof row.parent_session_id === "string" ? row.parent_session_id.trim() : "";
|
|
1609
|
+
if (!parentId) continue;
|
|
1610
|
+
if (row.parent_link_conflict || hasParentCycle(row)) {
|
|
1611
|
+
row.parent_link_conflict = true;
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
if (!byId.has(parentId)) {
|
|
1615
|
+
row.orphaned_subagent = true;
|
|
1616
|
+
continue;
|
|
1617
|
+
}
|
|
1618
|
+
const children = childrenById.get(parentId) || [];
|
|
1619
|
+
children.push(row);
|
|
1620
|
+
childrenById.set(parentId, children);
|
|
1621
|
+
linkedParentById.set(row.session_id, parentId);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
const memo = new Map();
|
|
1625
|
+
function aggregate(row, visiting = new Set()) {
|
|
1626
|
+
if (memo.has(row.session_id)) return memo.get(row.session_id);
|
|
1627
|
+
if (visiting.has(row.session_id)) {
|
|
1628
|
+
row.parent_link_conflict = true;
|
|
1629
|
+
return { count: 0, tokens: 0, cost: 0 };
|
|
1630
|
+
}
|
|
1631
|
+
const nextVisiting = new Set(visiting);
|
|
1632
|
+
nextVisiting.add(row.session_id);
|
|
1633
|
+
let count = 0;
|
|
1634
|
+
let tokens = 0;
|
|
1635
|
+
let cost = 0;
|
|
1636
|
+
for (const child of childrenById.get(row.session_id) || []) {
|
|
1637
|
+
const nested = aggregate(child, nextVisiting);
|
|
1638
|
+
count += 1 + nested.count;
|
|
1639
|
+
tokens += finite(child.total_tokens) + nested.tokens;
|
|
1640
|
+
cost += finite(child.cost_usd) + nested.cost;
|
|
1641
|
+
}
|
|
1642
|
+
const result = { count, tokens, cost };
|
|
1643
|
+
memo.set(row.session_id, result);
|
|
1644
|
+
return result;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
function rootFor(row) {
|
|
1648
|
+
const seen = new Set([row.session_id]);
|
|
1649
|
+
let current = row;
|
|
1650
|
+
while (current && linkedParentById.has(current.session_id)) {
|
|
1651
|
+
const linkedParentId = linkedParentById.get(current.session_id);
|
|
1652
|
+
if (seen.has(linkedParentId)) {
|
|
1653
|
+
row.parent_link_conflict = true;
|
|
1654
|
+
break;
|
|
1655
|
+
}
|
|
1656
|
+
seen.add(linkedParentId);
|
|
1657
|
+
current = byId.get(linkedParentId);
|
|
1658
|
+
}
|
|
1659
|
+
return current || row;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
for (const row of rows || []) {
|
|
1663
|
+
row.own_total_tokens = finite(row.total_tokens);
|
|
1664
|
+
row.own_cost_usd = finite(row.cost_usd);
|
|
1665
|
+
row.direct_subagent_count = row.source === "codex" && row.session_id
|
|
1666
|
+
? (childrenById.get(row.session_id) || []).length
|
|
1667
|
+
: 0;
|
|
1668
|
+
const nested = row.source === "codex" && row.session_id
|
|
1669
|
+
? aggregate(row)
|
|
1670
|
+
: { count: 0, tokens: 0, cost: 0 };
|
|
1671
|
+
row.descendant_subagent_count = nested.count;
|
|
1672
|
+
row.subagent_total_tokens = nested.tokens;
|
|
1673
|
+
row.subagent_cost_usd = nested.cost;
|
|
1674
|
+
row.combined_total_tokens = finite(row.total_tokens) + nested.tokens;
|
|
1675
|
+
row.combined_cost_usd = finite(row.cost_usd) + nested.cost;
|
|
1676
|
+
if (row.source === "codex" && row.session_id) {
|
|
1677
|
+
const parent = byId.get(linkedParentById.get(row.session_id));
|
|
1678
|
+
row.parent_session_hash = parent?.session_hash || null;
|
|
1679
|
+
row.root_session_hash = rootFor(row).session_hash || row.session_hash;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
return rows;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1430
1685
|
// Local-only session list for the dashboard session browser. Unlike
|
|
1431
1686
|
// summarizeSessions (cloud/CSV safe), this retains session_id + project_ref so
|
|
1432
1687
|
// the UI can offer one-click resume. Callers must only expose it over the
|
|
1433
1688
|
// local API.
|
|
1434
1689
|
function listSessionsForBrowser(sessions, { from = "", to = "", limit = 0 } = {}) {
|
|
1435
|
-
const filtered = mergeSessionFragments(sessions)
|
|
1690
|
+
const filtered = annotateCodexThreadUsage(mergeSessionFragments(sessions)
|
|
1436
1691
|
// Only sessions that actually spent tokens are worth listing. This drops
|
|
1437
1692
|
// two kinds of noise: non-session logs under ~/.claude/projects
|
|
1438
1693
|
// (skill-injections.jsonl, journal.jsonl, …), and sessions abandoned before
|
|
1439
1694
|
// the model replied. Both render as "unknown · 0 tokens · $0.00" rows with
|
|
1440
1695
|
// no model, no cost and nothing to analyze.
|
|
1441
1696
|
.filter((row) => finite(row.total_tokens) > 0)
|
|
1442
|
-
.filter((row) => withinDayRange(row, from, to));
|
|
1697
|
+
.filter((row) => withinDayRange(row, from, to)));
|
|
1443
1698
|
filtered.sort((a, b) => String(b.ended_at || "").localeCompare(String(a.ended_at || "")));
|
|
1444
1699
|
const cap = Number(limit) > 0 ? Number(limit) : 0;
|
|
1445
1700
|
const limited = cap > 0 ? filtered.slice(0, cap) : filtered;
|
package/src/lib/telemetry.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Privacy contract (documented in README "Privacy" section — keep in sync):
|
|
7
7
|
* the payload carries exactly four fields — a one-way sha256 hash of the
|
|
8
8
|
* machine id, the app version, `process.platform`, and the app shell
|
|
9
|
-
* (cli / macos / windows). No token counts, no model names, no usernames,
|
|
9
|
+
* (cli / macos / windows / linux). No token counts, no model names, no usernames,
|
|
10
10
|
* no paths. Opt out with TOKENTRACKER_NO_TELEMETRY=1, the DO_NOT_TRACK
|
|
11
11
|
* standard, or `"telemetry": false` in config.json.
|
|
12
12
|
*/
|
|
@@ -35,7 +35,7 @@ const MACHINE_HASH_NAMESPACE = "tokentracker-telemetry-v1:";
|
|
|
35
35
|
// cloud host; sync awaits this at most once per day so the worst-case stall
|
|
36
36
|
// is bounded and rare.
|
|
37
37
|
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
38
|
-
const VALID_SHELLS = new Set(["cli", "macos", "windows"]);
|
|
38
|
+
const VALID_SHELLS = new Set(["cli", "macos", "windows", "linux"]);
|
|
39
39
|
|
|
40
40
|
const THROTTLE_CONFIG = {
|
|
41
41
|
intervalMs: HEARTBEAT_INTERVAL_MS,
|
package/src/lib/usage-limits.js
CHANGED
|
@@ -1882,20 +1882,30 @@ function describeCopilotOtelStatus({ home, env = process.env } = {}) {
|
|
|
1882
1882
|
const explicitPath = typeof env.COPILOT_OTEL_FILE_EXPORTER_PATH === "string"
|
|
1883
1883
|
? env.COPILOT_OTEL_FILE_EXPORTER_PATH
|
|
1884
1884
|
: "";
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1885
|
+
const defaultDirs = [
|
|
1886
|
+
path.join(resolvedHome, ".copilot", "otel"),
|
|
1887
|
+
path.join(resolvedHome, ".copilot-otel"),
|
|
1888
|
+
];
|
|
1889
|
+
const detectedPaths = [];
|
|
1890
|
+
for (const defaultDir of defaultDirs) {
|
|
1891
|
+
try {
|
|
1892
|
+
if (!fs.existsSync(defaultDir)) continue;
|
|
1893
|
+
for (const entry of fs.readdirSync(defaultDir)) {
|
|
1894
|
+
if (entry.endsWith(".jsonl")) {
|
|
1895
|
+
detectedPaths.push(path.join(defaultDir, entry));
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
} catch (_e) {}
|
|
1899
|
+
}
|
|
1900
|
+
if (explicitPath && fs.existsSync(explicitPath)) detectedPaths.push(explicitPath);
|
|
1893
1901
|
return {
|
|
1894
1902
|
otel_enabled: enabled && (exporterType === "" || exporterType === "file"),
|
|
1895
1903
|
otel_exporter_type: exporterType || null,
|
|
1896
1904
|
otel_path: explicitPath || null,
|
|
1897
|
-
otel_default_dir:
|
|
1898
|
-
|
|
1905
|
+
otel_default_dir: defaultDirs[0],
|
|
1906
|
+
otel_default_dirs: defaultDirs,
|
|
1907
|
+
otel_detected_paths: detectedPaths,
|
|
1908
|
+
otel_has_files: detectedPaths.length > 0,
|
|
1899
1909
|
};
|
|
1900
1910
|
}
|
|
1901
1911
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,z as a}from"./__vite-browser-external-CsbVaBbw.js";import{u as p}from"./DialogTitle-BkAmJrQ_.js";import{u as c}from"./composite-MhgOkYfv.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,i=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
|