Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions index.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updated_at": "2026-06-16T06:18:38.000Z",
"updated_at": "2026-06-25T06:01:55.000Z",
"scripts": [
{
"id": "codex-context-ring-restore",
Expand Down Expand Up @@ -72,7 +72,7 @@
"id": "codex-daily-token-usage",
"name": "Codex Daily Token Usage",
"description": "每日 Token 统计,近 5 日滚动存储,自动复用已有采集,支持 Model 价格、成本估算、趋势明细和分享图",
"version": "1.4.3",
"version": "1.4.5",
"author": "kts-kris",
"tags": [
"codex",
Expand All @@ -84,7 +84,7 @@
],
"homepage": "",
"script_url": "https://raw.githubusercontent.com/BigPizzaV3/CodexPlusPlusScriptMarket/main/scripts/codex-daily-token-usage.js",
"sha256": "890df2074a4ef0be3d424c86a21dca1ed1b9bd9a121dea112baa16fbb242d04a"
"sha256": "b565fe5a5728d52700d4d089d874a3f3e7f258a82fdea69762155ebbdf1c2c2f"
},
{
"id": "codex-list-pagebuster",
Expand Down
101 changes: 77 additions & 24 deletions scripts/codex-daily-token-usage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ==UserScript==
// @name Codex Daily Token Usage
// @namespace codex-plus-plus
// @version 1.4.3
// @version 1.4.5
// @description 每日 Token 统计,近 5 日滚动存储,优先复用已有采集,必要时内置采集,支持 Model 价格、成本估算、日期切换、5 日趋势与分享图。
// @match app://-/*
// @run-at document-start
Expand All @@ -10,7 +10,7 @@
(() => {
"use strict";

const VERSION = "1.4.3";
const VERSION = "1.4.5";
const API_KEY = "__codexDailyTokenUsage";
const SOURCE_API_KEY = "__codexTokenUsage";
const STORAGE_KEY = "__codexDailyTokenUsageV1";
Expand All @@ -23,12 +23,16 @@
const MAX_TURNS_PER_DAY = 2000;
const CAPTURE_DEDUPE_WINDOW_MS = 3000;
const MAX_CAPTURE_BODY_CHARS = 2_000_000;
const EXTERNAL_SOURCE_GRACE_MS = 4000;
const EXTERNAL_EMPTY_LIMIT = 4;
const TREND_DAYS = 5;
const MODEL_BIND_WINDOW_MS = 30 * 60 * 1000;
const UNKNOWN_MODEL = "Unknown";
const PRICE_FIELDS = ["input", "cachedInput", "output", "reasoning"];
const FLOATING_TOP = 2;
const FLOATING_RIGHT = 280;
const PANEL_GAP = 8;
const PANEL_MARGIN = 12;
const WINDOW_BUTTON_SAFE_RIGHT = 132;

const previous = window[API_KEY];
if (previous && typeof previous.destroy === "function") {
Expand All @@ -55,7 +59,6 @@
let lastCaptureAt = 0;
let captureSeq = 0;
let externalEmptyCount = 0;
let startedAt = Date.now();
let lastRenderedTotal = -1;
let lastDateKey = getDateKey(Date.now());
let selectedDateKey = lastDateKey;
Expand Down Expand Up @@ -140,32 +143,47 @@
const usage = rawUsage && typeof rawUsage === "object" ? rawUsage : {};
const input = firstCount(
usage.inputTotalTokens,
usage.input_total_tokens,
usage.promptTotalTokens,
usage.prompt_total_tokens,
usage.inputTokens,
usage.input_tokens,
usage.promptTokens,
usage.prompt_tokens
);
const output = firstCount(
usage.outputTotalTokens,
usage.outputTokens,
usage.output_tokens,
usage.completionTokens,
usage.completion_tokens
);
const cached = firstCount(
usage.cachedReadTokens,
usage.cachedTokens,
usage.cacheReadTokens,
usage.cachedInputTokens,
usage.cached_input_tokens,
usage.cacheReadInputTokens,
usage.cache_read_input_tokens,
usage.promptTokensDetails?.cachedTokens,
usage.prompt_tokens_details?.cached_tokens,
usage.inputTokensDetails?.cachedTokens,
usage.input_tokens_details?.cached_tokens
);
const reasoning = firstCount(
usage.reasoningTokens,
usage.reasoning_tokens,
usage.outputTokensDetails?.reasoningTokens,
usage.output_tokens_details?.reasoning_tokens
);
const total = firstCount(
usage.requestTotalTokens,
usage.totalTokens,
usage.total_tokens,
usage.usedTokens,
usage.used_tokens,
usage.used,
input + output
);

Expand Down Expand Up @@ -1162,6 +1180,8 @@
"usage",
"token_usage",
"tokenUsage",
"context_usage",
"contextUsage",
"last_usage",
"lastUsage",
"last_token_usage",
Expand Down Expand Up @@ -1189,6 +1209,7 @@
"item",
"output",
"details",
"info",
]) {
candidates.push(...findUsageCandidates(value[key], depth + 1, id, model));
}
Expand Down Expand Up @@ -1270,6 +1291,10 @@
return changed;
}

function shouldProcessStandalonePayload() {
return sourceMode !== "external" && (captureInstalled || !externalSourceAvailable());
}

function processModelPayload(payload) {
if (!payload) return false;
if (typeof payload === "string") {
Expand All @@ -1294,7 +1319,11 @@
"codex-message-from-view",
(event) => {
try {
processModelPayload(event.detail);
if (shouldProcessStandalonePayload()) {
processCapturePayload(event.detail, "codex-message");
} else {
processModelPayload(event.detail);
}
} catch {
// 不影响 Codex 自身消息投递。
}
Expand Down Expand Up @@ -1464,7 +1493,7 @@
function shouldInstallStandaloneCapture(externalTurns) {
if (captureInstalled) return false;
if (!externalSourceAvailable()) {
return Date.now() - startedAt >= EXTERNAL_SOURCE_GRACE_MS;
return true;
}
if (externalTurns.length > 0) return false;
externalEmptyCount += 1;
Expand Down Expand Up @@ -1514,15 +1543,17 @@
display: flex;
flex: 0 0 auto;
align-items: center;
pointer-events: auto;
pointer-events: none;
-webkit-app-region: no-drag;
z-index: 2147483600;
font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
#${ROOT_ID}.codex-daily-floating {
position: fixed;
top: 10px;
right: 80px;
top: ${FLOATING_TOP}px;
right: ${FLOATING_RIGHT}px;
bottom: auto;
left: auto;
}
#${ROOT_ID} .codex-daily-trigger {
box-sizing: border-box;
Expand All @@ -1546,6 +1577,7 @@
user-select: none;
outline: none;
transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
pointer-events: auto;
-webkit-app-region: no-drag;
}
#${ROOT_ID} .codex-daily-trigger:hover,
Expand Down Expand Up @@ -2167,11 +2199,12 @@
document.body.appendChild(panel);

const trigger = root.querySelector(".codex-daily-trigger");
trigger.addEventListener("click", () => {
trigger.addEventListener("click", (event) => {
event.stopPropagation();
pinnedOpen = !pinnedOpen;
if (pinnedOpen) {
showPanel();
} else if (!root.matches(":hover") && !panel.matches(":hover")) {
} else {
hidePanel();
}
});
Expand All @@ -2184,11 +2217,10 @@
});
trigger.addEventListener("focus", showPanel);
trigger.addEventListener("blur", schedulePanelClose);
root.addEventListener("mouseenter", showPanel);
root.addEventListener("mouseleave", schedulePanelClose);
trigger.addEventListener("mouseenter", showPanel);
trigger.addEventListener("mouseleave", schedulePanelClose);
panel.addEventListener("mouseenter", cancelPanelClose);
panel.addEventListener("mouseleave", schedulePanelClose);

panel.querySelector('[data-action="previous-day"]').addEventListener("click", () => {
selectDate(shiftDateKey(selectedDateKey, -1));
});
Expand Down Expand Up @@ -2383,8 +2415,19 @@
function positionPanel() {
if (!root || !panel) return;
const rect = root.getBoundingClientRect();
panel.style.top = `${Math.round(rect.bottom + 8)}px`;
panel.style.right = `${Math.max(12, Math.round(innerWidth - rect.right))}px`;
const panelRect = panel.getBoundingClientRect();
const panelWidth = panelRect.width || Math.min(350, innerWidth - PANEL_MARGIN * 2);
const panelHeight = panelRect.height || 580;
const maxLeft = Math.max(PANEL_MARGIN, innerWidth - panelWidth - PANEL_MARGIN);
const safeMaxLeft = Math.max(PANEL_MARGIN, innerWidth - panelWidth - WINDOW_BUTTON_SAFE_RIGHT);
const preferredLeft = rect.left;
const left = Math.min(Math.max(PANEL_MARGIN, preferredLeft), safeMaxLeft, maxLeft);
const preferredTop = rect.bottom + PANEL_GAP;
const maxTop = Math.max(PANEL_MARGIN, innerHeight - panelHeight - PANEL_MARGIN);
const top = Math.min(Math.max(PANEL_MARGIN, preferredTop), maxTop);
panel.style.top = `${Math.round(top)}px`;
panel.style.left = `${Math.round(left)}px`;
panel.style.right = "auto";
}

function cancelPanelClose() {
Expand Down Expand Up @@ -2414,7 +2457,9 @@
function schedulePanelClose() {
cancelPanelClose();
closeTimer = window.setTimeout(() => {
if (!pinnedOpen && !root?.matches(":hover") && !panel?.matches(":hover")) {
const trigger = root?.querySelector(".codex-daily-trigger");
const triggerActive = trigger?.matches(":hover") || trigger?.matches(":focus");
if (!pinnedOpen && !triggerActive && !panel?.matches(":hover")) {
hidePanel();
}
}, 140);
Expand All @@ -2427,12 +2472,16 @@
}

function findToolbar() {
return null;

const plusMenu = document.getElementById("codex-plus-menu");
if (plusMenu?.parentElement) return plusMenu.parentElement;
if (plusMenu?.parentElement && plusMenu.getBoundingClientRect().top >= 30) {
return plusMenu.parentElement;
}

const candidates = Array.from(document.querySelectorAll("button")).filter((button) => {
const rect = button.getBoundingClientRect();
return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.top < 60 && rect.right > innerWidth - 360;
return rect.width > 0 && rect.height > 0 && rect.top >= 30 && rect.top < 72 && rect.right > innerWidth - 360;
});

for (const button of candidates) {
Expand All @@ -2444,7 +2493,7 @@
style.display === "flex" &&
rect.height > 20 &&
rect.height < 60 &&
rect.right > innerWidth - 24 &&
rect.right > innerWidth - WINDOW_BUTTON_SAFE_RIGHT &&
current.children.length > 1
) {
return current;
Expand All @@ -2466,7 +2515,14 @@
}

root.classList.add("codex-daily-floating");
if (root.parentElement !== document.body) document.body.appendChild(root);
if (root.parentElement !== document.body) {
document.body.appendChild(root);
}
root.style.top = `${FLOATING_TOP}px`;
root.style.right = `${FLOATING_RIGHT}px`;
root.style.left = "auto";
root.style.bottom = "auto";
root.style.transform = "none";
if (panel?.classList.contains("is-visible")) positionPanel();
}

Expand Down Expand Up @@ -2739,9 +2795,6 @@
externalSourceAvailable,
getSourceMode: () => sourceMode,
isCaptureInstalled: () => captureInstalled,
setStartedAt(value) {
startedAt = value;
},
getRawState() {
return JSON.parse(JSON.stringify(state));
},
Expand Down