codex重置后周限额直逼9000刀

ましろSaber 2026-08-26 14:06 1


上次重置完周限额只有一千多刀,这次重置完计算了一下人傻了 ^-^

最新回复 (19)
  • linganshi 08-26 14:08
    1

    你这个周期推算,起始时间没有精确到小时吧,把25号当天所有的消耗算到当前周期了

  • TeainfrostOUO 08-26 14:09
    2

    之前看到一个8000的:

    爆炸,周限额测出来 8000 刀…… - 搞七捻三 - LINUX DO

  • kou 08-26 14:19
    3

    夸张 ^-^ 难道是我家token离家出走到佬这了

  • swack01 08-26 14:21
    4
    // ==UserScript==
    // @name Codex Quota Compas`预先格式化的文本`s (Accurate Usage Edition)
    // @namespace http://tampermonkey.net/
    // @version 2.1.0
    // @description Codex 用量分析:实时百分比 + 同日 Credits/Percent 反推额度,避免统计延迟导致的错误周额度
    // @author Jun Zhao + ChatGPT
    // @match https://chatgpt.com/codex/cloud/settings/analytics*
    // @match https://chatgpt.com/codex/*/settings/analytics*
    // @match https://chatgpt.com/codex/*/settings/usage*
    // @grant GM_addStyle
    // ==/UserScript==

    (function () {
    "use strict";

    if (window.__codexQuotaCompassV21) return;
    window.__codexQuotaCompassV21 = true;

    const CONFIG = {
    USD_PER_CREDIT: 0.04,
    HISTORY_DAYS: 45,
    RATIO_TOLERANCE: 0.03, // 与最新样本相差 3% 内视为同一额度档位
    MIN_LONG_WINDOW_SECONDS: 2 * 86400,
    };

    const DAY_MS = 86400000;
    const n = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
    const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
    const esc = (v) =>
    String(v ?? "").replace(
    /[&<>"']/g,
    (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c],
    );

    const dayKey = (ms) => new Date(ms).toISOString().slice(0, 10);
    const addDays = (key, days) => dayKey(Date.parse(key + "T00:00:00Z") + days * DAY_MS);

    const fmtNum = (value) => {
    const x = n(value);
    if (x >= 1e9) return (x / 1e9).toFixed(2) + "B";
    if (x >= 1e6) return (x / 1e6).toFixed(2) + "M";
    if (x >= 1e3) return (x / 1e3).toFixed(2) + "K";
    return x.toLocaleString("zh-CN", { maximumFractionDigits: 2 });
    };

    const fmtCredits = (value, digits = 1) =>
    Number.isFinite(Number(value))
    ? Number(value).toLocaleString("en-US", {
    minimumFractionDigits: digits,
    maximumFractionDigits: digits,
    })
    : "—";

    const fmtPct = (value, digits = 1) =>
    Number.isFinite(Number(value)) ? `${Number(value).toFixed(digits)}%` : "—";

    const fmtUSD = (credits) =>
    Number.isFinite(Number(credits)) ? `$ ${(Number(credits) * CONFIG.USD_PER_CREDIT).toFixed(2)}` : "—";

    const fmtTime = (epochMs) => {
    if (!Number.isFinite(Number(epochMs))) return "—";
    return new Date(Number(epochMs)).toLocaleString("zh-CN", {
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
    });
    };

    const tokenTotal = (obj = {}) =>
    n(obj.text_total_tokens) ||
    n(obj.cached_text_input_tokens) + n(obj.uncached_text_input_tokens) + n(obj.text_output_tokens);

    const median = (values) => {
    const arr = values.filter(Number.isFinite).sort((a, b) => a - b);
    if (!arr.length) return null;
    const mid = Math.floor(arr.length / 2);
    return arr.length % 2 ? arr[mid] : (arr[mid - 1] + arr[mid]) / 2;
    };

    GM_addStyle(`
    #codex-compass-root {
    position: fixed; top: 4%; left: 50%; transform: translateX(-50%);
    width: min(1040px, calc(100vw - 28px)); max-height: 92vh;
    background: #fff; border-radius: 14px; box-shadow: 0 12px 55px rgba(0,0,0,.32);
    z-index: 2147483646; padding: 22px; display: none; flex-direction: column;
    border: 1px solid #e5e5e5; color: #2f2f2f; box-sizing: border-box;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    }
    #codex-compass-root * { box-sizing: border-box; }
    .cc-header { display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:14px; }
    .cc-title { font-size:19px; font-weight:700; }
    .cc-subtitle { color:#777; font-size:12px; margin-top:3px; }
    .cc-close { cursor:pointer; font-size:28px; color:#999; line-height:1; border:0; background:transparent; }
    .cc-scroll { overflow:auto; padding-right:2px; }
    .cc-grid { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:10px; margin-bottom:14px; }
    .cc-card { background:#fafafa; border:1px solid #ececec; border-radius:10px; padding:12px; min-height:78px; }
    .cc-card.good { background:#eefaf5; border-color:#ccebdd; }
    .cc-card.warn { background:#fff8e8; border-color:#f3dfab; }
    .cc-label { font-size:12px; color:#6b6b6b; margin-bottom:5px; }
    .cc-value { font-size:20px; line-height:1.2; font-weight:750; color:#0b8f70; white-space:nowrap; }
    .cc-value.small { font-size:17px; }
    .cc-note { margin-top:5px; color:#777; font-size:11px; line-height:1.35; }
    .cc-section-title { font-size:13px; font-weight:700; margin:15px 0 7px; }
    .cc-banner { border-radius:9px; padding:9px 11px; margin:8px 0; font-size:12px; line-height:1.55; }
    .cc-banner.info { background:#f4f7fb; border:1px solid #dce5ef; color:#435160; }
    .cc-banner.warn { background:#fff8e8; border:1px solid #f0dca5; color:#795d18; }
    .cc-banner.ok { background:#effaf5; border:1px solid #ccebdd; color:#246a56; }
    .cc-windows { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:8px; }
    .cc-window { border:1px solid #ececec; border-radius:9px; padding:9px 10px; background:#fcfcfc; }
    .cc-window.target { border-color:#a9dfcf; background:#f3fbf8; }
    .cc-window-head { display:flex; justify-content:space-between; gap:8px; font-size:12px; font-weight:700; }
    .cc-window-meta { color:#777; font-size:11px; margin-top:5px; line-height:1.5; }
    .cc-table-wrap { max-height:285px; overflow:auto; border:1px solid #ececec; border-radius:8px; }
    .cc-table { width:100%; border-collapse:collapse; font-size:11.5px; }
    .cc-table thead { position:sticky; top:0; z-index:2; background:#f5f5f5; }
    .cc-table th { text-align:left; color:#666; padding:8px 7px; border-bottom:1px solid #e5e5e5; white-space:nowrap; }
    .cc-table td { padding:7px; border-bottom:1px solid #f0f0f0; white-space:nowrap; }
    .cc-table tr.latest { background:#f6fbf9; }
    .cc-muted { color:#999; }
    .cc-green { color:#0b8f70; font-weight:700; }
    .cc-amber { color:#a56c00; font-weight:700; }
    .cc-footer { margin-top:10px; color:#777; font-size:11px; line-height:1.55; }
    #codex-compass-btn {
    position:fixed; bottom:20px; right:20px; z-index:2147483645;
    padding:10px 16px; background:#10a37f; color:#fff; border:0; border-radius:9px;
    cursor:pointer; font-weight:700; box-shadow:0 3px 14px rgba(0,0,0,.16);
    }
    #codex-compass-btn:disabled { opacity:.65; cursor:wait; }
    @media (max-width:760px) {
    .cc-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
    #codex-compass-root { padding:15px; top:2%; max-height:96vh; }
    }
    `);

    async function getToken() {
    const boot = document.getElementById("client-bootstrap")?.textContent || "";
    const jwt = boot.match(/eyJ[\w-]*\.[\w-]+\.[\w-]+/g);
    if (jwt?.[0]) return jwt[0];

    try {
    const res = await fetch("/api/auth/session", {
    credentials: "include",
    cache: "no-store",
    });
    if (!res.ok) return null;
    const data = await res.json();
    return data?.accessToken || data?.access_token || null;
    } catch (_) {
    return null;
    }
    }

    async function apiGet(path, token) {
    const res = await fetch(path, {
    headers: {
    Authorization: `Bearer ${token}`,
    Accept: "application/json",
    },
    credentials: "include",
    cache: "no-store",
    });
    const text = await res.text();
    if (!res.ok) throw new Error(`${path.split("?")[0]} → HTTP ${res.status}: ${text.slice(0, 160)}`);
    try {
    return JSON.parse(text);
    } catch (_) {
    throw new Error(`${path.split("?")[0]} 返回了非 JSON 数据`);
    }
    }

    const softGet = (path, token) => apiGet(path, token).catch((e) => {
    console.warn("[Codex Quota Compass] optional endpoint failed:", e.message);
    return null;
    });

    function classifyWindow(seconds) {
    const s = n(seconds);
    if (Math.abs(s - 5 * 3600) <= 1800) return "约 5 小时";
    if (Math.abs(s - 24 * 3600) <= 3600) return "约 1 天";
    if (Math.abs(s - 7 * 86400) <= 6 * 3600) return "约 7 天";
    if (s >= 27 * 86400 && s <= 32 * 86400) return "约 1 个月";
    if (s >= 86400) return `${(s / 86400).toFixed(1)} 天`;
    return `${(s / 3600).toFixed(1)} 小时`;
    }

    function parseWindows(usage) {
    const rate = usage?.rate_limit || {};
    const rows = [];
    for (const key of ["primary_window", "secondary_window"]) {
    const w = rate?.[key];
    if (!w) continue;
    const sec = n(w.limit_window_seconds);
    const resetAtMs = n(w.reset_at) * 1000;
    if (!(sec > 0) || !(resetAtMs > 0)) continue;
    rows.push({
    key,
    role: key === "primary_window" ? "Primary" : "Secondary",
    usedPercent: clamp(n(w.used_percent), 0, 100),
    windowSec: sec,
    resetAtMs,
    startAtMs: resetAtMs - sec * 1000,
    label: classifyWindow(sec),
    });
    }
    return rows;
    }

    // 新版后端有时把主要额度放 primary,有时旧账号仍是 secondary。
    // 不再写死字段名:优先选 >=2 天的 primary;否则选最长的 >=2 天窗口;再否则选最长窗口。
    function chooseAllowanceWindow(windows) {
    if (!windows.length) return null;
    const primaryLong = windows.find(
    (w) => w.key === "primary_window" && w.windowSec >= CONFIG.MIN_LONG_WINDOW_SECONDS,
    );
    if (primaryLong) return primaryLong;
    const long = windows
    .filter((w) => w.windowSec >= CONFIG.MIN_LONG_WINDOW_SECONDS)
    .sort((a, b) => b.windowSec - a.windowSec);
    if (long[0]) return long[0];
    return [...windows].sort((a, b) => b.windowSec - a.windowSec)[0];
    }

    function dailyPercent(row = {}) {
    const surfaces = row.product_surface_usage_values;
    if (surfaces && typeof surfaces === "object") {
    const sum = Object.values(surfaces).reduce((a, v) => a + Math.max(0, n(v)), 0);
    if (sum > 0) return sum;
    }

    for (const key of ["used_percent", "usage_percent", "percent", "total_percent"]) {
    if (n(row[key]) > 0) return n(row[key]);
    }

    if (Array.isArray(row.models)) {
    const sum = row.models.reduce(
    (a, m) => a + Math.max(0, n(m.used_percent || m.usage_percent || m.percent)),
    0,
    );
    if (sum > 0) return sum;
    }
    return 0;
    }

    function normalizeCounts(data) {
    return Array.isArray(data) ? data : Array.isArray(data?.data) ? data.data : [];
    }

    function normalizeRows(data) {
    return Array.isArray(data?.data) ? data.data : Array.isArray(data) ? data : [];
    }

    function mergeDaily(countsData, pctData) {
    const counts = normalizeCounts(countsData);
    const pctRows = normalizeRows(pctData);
    const pctMap = new Map(pctRows.map((r) => [r.date, r]));

    return counts
    .map((row) => {
    const totals = row.totals || {};
    const credits = n(totals.credits);
    const pct = dailyPercent(pctMap.get(row.date) || {});
    const impliedAllowance = credits > 0 && pct > 0 ? (credits / pct) * 100 : null;
    return {
    date: row.date,
    credits,
    pct,
    impliedAllowance,
    tokens: tokenTotal(totals),
    turns: n(totals.turns),
    threads: n(totals.threads),
    };
    })
    .filter((r) => r.date)
    .sort((a, b) => a.date.localeCompare(b.date));
    }

    // 核心修复:绝不再用“延迟的周期 Credits ÷ 实时 used_percent”。
    // 改为“同一天的 Credits ÷ 同一天的 allowance percent”,两边来自同一日桶。
    function estimateAllowance(dailyRows, targetWindow) {
    const usable = dailyRows.filter((d) => d.credits > 0 && d.pct > 0 && d.impliedAllowance > 0);
    if (!usable.length) return null;

    const cycleStartKey = targetWindow ? dayKey(targetWindow.startAtMs) : null;
    const inWindow = cycleStartKey ? usable.filter((d) => d.date >= cycleStartKey) : [];
    const pool = inWindow.length ? inWindow : usable;
    const desc = [...pool].sort((a, b) => b.date.localeCompare(a.date));
    const newest = desc[0].impliedAllowance;

    const agreeing = desc.filter(
    (d) => Math.abs(d.impliedAllowance - newest) / newest <= CONFIG.RATIO_TOLERANCE,
    );
    const value = median(agreeing.map((d) => d.impliedAllowance));
    if (!(value > 0)) return null;

    const deviations = agreeing.map((d) => Math.abs(d.impliedAllowance - value) / value);
    const maxDeviation = deviations.length ? Math.max(...deviations) : 0;
    const confidence = agreeing.length >= 3 && maxDeviation <= 0.02 ? "高" : agreeing.length >= 2 ? "中" : "低";

    return {
    credits: value,
    samples: agreeing.length,
    dropped: pool.length - agreeing.length,
    newestDate: desc[0].date,
    confidence,
    source: "daily-ratio",
    };
    }

    function stats(rows) {
    return rows.reduce(
    (a, r) => {
    a.credits += r.credits;
    a.tokens += r.tokens;
    a.turns += r.turns;
    a.pct += r.pct;
    return a;
    },
    { credits: 0, tokens: 0, turns: 0, pct: 0 },
    );
    }

    function renderWindows(windows, target) {
    if (!windows.length) return `<div class="cc-banner warn">usage 接口没有返回可识别的限流窗口。</div>`;
    return `<div class="cc-windows">${windows
    .map(
    (w) => `
    <div class="cc-window ${target?.key === w.key ? "target" : ""}">
    <div class="cc-window-head">
    <span>${esc(w.role)} · ${esc(w.label)}</span>
    <span class="${target?.key === w.key ? "cc-green" : ""}">${fmtPct(w.usedPercent, 0)}</span>
    </div>
    <div class="cc-window-meta">
    ${target?.key === w.key ? "✓ 当前作为主额度窗口<br>" : ""}
    开始:${esc(fmtTime(w.startAtMs))}<br>
    重置:${esc(fmtTime(w.resetAtMs))}<br>
    原始窗口:${Math.round(w.windowSec).toLocaleString("en-US")} 秒
    </div>
    </div>`,
    )
    .join("")}</div>`;
    }

    function renderDailyTable(rows, estimate) {
    if (!rows.length) return `<div class="cc-banner warn">daily-workspace-usage-counts 没有返回日用量。</div>`;
    const latest = rows[rows.length - 1]?.date;
    return `
    <div class="cc-table-wrap">
    <table class="cc-table">
    <thead>
    <tr>
    <th>日期 (UTC)</th>
    <th>Credits</th>
    <th>当日额度%</th>
    <th>当日反推总额</th>
    <th>Tokens</th>
    <th>轮数</th>
    </tr>
    </thead>
    <tbody>
    ${[...rows]
    .reverse()
    .map((r) => {
    const agrees =
    estimate?.credits && r.impliedAllowance
    ? Math.abs(r.impliedAllowance - estimate.credits) / estimate.credits <= CONFIG.RATIO_TOLERANCE
    : false;
    return `
    <tr class="${r.date === latest ? "latest" : ""}">
    <td>${esc(r.date)}${r.date === latest ? " <span class='cc-muted'>最新</span>" : ""}</td>
    <td>${fmtCredits(r.credits, 3)}</td>
    <td>${r.pct > 0 ? fmtPct(r.pct, 3) : "<span class='cc-muted'>—</span>"}</td>
    <td class="${agrees ? "cc-green" : r.impliedAllowance ? "cc-amber" : "cc-muted"}">
    ${r.impliedAllowance ? fmtCredits(r.impliedAllowance, 1) : "—"}
    </td>
    <td>${fmtNum(r.tokens)}</td>
    <td>${fmtCredits(r.turns, 0)}</td>
    </tr>`;
    })
    .join("")}
    </tbody>
    </table>
    </div>`;
    }

    function showPanel(data) {
    const root = document.getElementById("codex-compass-root");
    const {
    usage,
    windows,
    targetWindow,
    dailyRows,
    allowance,
    countsOk,
    percentsOk,
    queryStart,
    queryEnd,
    } = data;

    const usedPct = targetWindow?.usedPercent ?? null;
    const remainingPct = usedPct == null ? null : Math.max(0, 100 - usedPct);
    const liveSpent = allowance && usedPct != null ? allowance.credits * (usedPct / 100) : null;
    const liveRemaining = allowance && usedPct != null ? allowance.credits * (remainingPct / 100) : null;

    const cycleStartKey = targetWindow ? dayKey(targetWindow.startAtMs) : null;
    const cycleRows = cycleStartKey ? dailyRows.filter((d) => d.date >= cycleStartKey) : [];
    const cycleStats = stats(cycleRows);
    const allStats = stats(dailyRows);
    const latestDailyDate = dailyRows[dailyRows.length - 1]?.date || null;
    const today = dayKey(Date.now());
    const lagDays = latestDailyDate ? Math.max(0, Math.round((Date.parse(today) - Date.parse(latestDailyDate)) / DAY_MS)) : null;

    const mismatch = liveSpent && cycleStats.credits > 0 ? Math.abs(liveSpent - cycleStats.credits) / liveSpent : null;

    let statusBanner = "";
    if (!percentsOk) {
    statusBanner = `<div class="cc-banner warn"><b>缺少 daily-token-usage-breakdown。</b> 当前只能准确显示实时百分比和已入账 Credits,不能可靠反推绝对额度。</div>`;
    } else if (!allowance) {
    statusBanner = `<div class="cc-banner warn"><b>暂时没有可配对样本。</b> 等 daily Credits 与 daily allowance% 至少有一天同时入账后,再计算绝对额度;实时 used% 仍然有效。</div>`;
    } else if (lagDays > 0 || mismatch > 0.12) {
    statusBanner = `<div class="cc-banner warn"><b>检测到统计延迟/口径差。</b> 日台账最新到 ${esc(latestDailyDate || "—")},因此不要再用“已入账 Credits ÷ 当前实时 used%”。上方“实时已用折算”使用同日比例测出的额度基准,不受这个错位公式直接影响。</div>`;
    } else {
    statusBanner = `<div class="cc-banner ok"><b>数据口径基本一致。</b> 实时百分比来自 usage;绝对额度来自同一天 Credits 与同一天 allowance% 的比值。</div>`;
    }

    const allowanceNote = allowance
    ? `${allowance.samples} 个同档样本 · 置信度 ${allowance.confidence}${allowance.dropped ? ` · 排除 ${allowance.dropped} 个异档样本` : ""}`
    : "等待同日 Credits + allowance% 样本";

    root.innerHTML = `
    <div class="cc-header">
    <div>
    <div class="cc-title">📊 Codex 配额深度分析 V2.1</div>
    <div class="cc-subtitle">修复:实时 used% 与延迟 Credits 不再直接相除;按真实窗口长度识别主额度</div>
    </div>
    <button class="cc-close" id="cc-close" aria-label="关闭">&times;</button>
    </div>

    <div class="cc-scroll">
    <div class="cc-grid">
    <div class="cc-card good">
    <div class="cc-label">实时已用(官方)</div>
    <div class="cc-value">${usedPct == null ? "—" : fmtPct(usedPct, 0)}</div>
    <div class="cc-note">来自 /wham/usage · ${esc(targetWindow?.label || "未知窗口")}</div>
    </div>
    <div class="cc-card good">
    <div class="cc-label">实时剩余(官方)</div>
    <div class="cc-value">${remainingPct == null ? "—" : fmtPct(remainingPct, 0)}</div>
    <div class="cc-note">百分比是当前最可信的实时用量</div>
    </div>
    <div class="cc-card ${allowance ? "good" : "warn"}">
    <div class="cc-label">额度基准(同日实测反推)</div>
    <div class="cc-value small">${allowance ? fmtCredits(allowance.credits, 1) + " Credits" : "—"}</div>
    <div class="cc-note">${esc(allowanceNote)}</div>
    </div>
    <div class="cc-card">
    <div class="cc-label">实时已用折算</div>
    <div class="cc-value small">${liveSpent == null ? "—" : fmtCredits(liveSpent, 1) + " Credits"}</div>
    <div class="cc-note">${liveSpent == null ? "需先测出额度基准" : `${fmtUSD(liveSpent)} · 额度基准 × 实时 used%`}</div>
    </div>
    <div class="cc-card">
    <div class="cc-label">实时剩余折算</div>
    <div class="cc-value small">${liveRemaining == null ? "—" : fmtCredits(liveRemaining, 1) + " Credits"}</div>
    <div class="cc-note">${liveRemaining == null ? "需先测出额度基准" : `${fmtUSD(liveRemaining)} · 估算值,不是余额字段`}</div>
    </div>
    <div class="cc-card">
    <div class="cc-label">本窗口已入账台账</div>
    <div class="cc-value small">${fmtCredits(cycleStats.credits, 1)} Credits</div>
    <div class="cc-note">最新日桶 ${esc(latestDailyDate || "—")} · 可能延迟,且窗口边界日是整日桶</div>
    </div>
    </div>

    ${statusBanner}

    <div class="cc-section-title">⏱️ 官方实时限流窗口</div>
    ${renderWindows(windows, targetWindow)}

    <div class="cc-section-title">📅 每日校准表:真正用于反推额度的数据</div>
    <div class="cc-banner info">
    公式:<b>当天 Credits ÷ 当天 allowance% × 100</b>。同一天的两个日桶配对,避免把“昨天的 Credits”除以“现在的 used%”。<br>
    查询范围:${esc(queryStart)} ~ ${esc(queryEnd)}(UTC,end_date 取次日以覆盖当天);workspace 用量强制使用 <code>workspace_user=true</code>。
    </div>
    ${renderDailyTable(dailyRows, allowance)}

    <div class="cc-section-title">🧾 查询诊断</div>
    <div class="cc-banner info">
    Plan: <b>${esc(usage?.plan_type || "unknown")}</b> ·
    日 Credits 接口: <b>${countsOk ? "OK" : "失败"}</b> ·
    日百分比接口: <b>${percentsOk ? "OK" : "失败"}</b> ·
    ${latestDailyDate ? `日台账最新:<b>${esc(latestDailyDate)}</b>` : "没有日台账"}<br>
    近 ${CONFIG.HISTORY_DAYS} 天已入账:${fmtCredits(allStats.credits, 3)} Credits / ${fmtNum(allStats.tokens)} Tokens / ${fmtCredits(allStats.turns, 0)} 轮。
    </div>

    <div class="cc-footer">
    <b>解释:</b>“实时已用/剩余 %”是服务器实时限流数据;“已入账 Credits”是 Analytics 日桶,可能延迟;“额度基准/实时 Credits 折算”属于推算值。<br>
    1 Credit = $${CONFIG.USD_PER_CREDIT.toFixed(2)} 仅用于价值换算,不影响额度百分比。若 OpenAI 改接口字段,面板会保留实时窗口并把不可用项显示为“—”。
    </div>
    </div>`;

    root.style.display = "flex";
    document.getElementById("cc-close").onclick = () => (root.style.display = "none");
    }

    async function run() {
    const btn = document.getElementById("codex-compass-btn");
    btn.disabled = true;
    btn.textContent = "分析中…";

    try {
    const token = await getToken();
    if (!token) throw new Error("令牌获取失败,请确认已登录 ChatGPT 后刷新页面。");

    const usage = await apiGet("/backend-api/wham/usage", token);
    const windows = parseWindows(usage);
    const targetWindow = chooseAllowanceWindow(windows);

    const today = dayKey(Date.now());
    const queryStart = addDays(today, -CONFIG.HISTORY_DAYS);
    const queryEnd = addDays(today, 1); // 后端日期范围通常按日桶,取次日保证覆盖今天
    const range = `start_date=${encodeURIComponent(queryStart)}&end_date=${encodeURIComponent(queryEnd)}&group_by=day`;

    // 关键:workspace_user=true,避免 workspace 总量和个人 used_percent 混在一起。
    const [countsData, pctData] = await Promise.all([
    softGet(`/backend-api/wham/analytics/daily-workspace-usage-counts?${range}&workspace_user=true`, token),
    softGet(`/backend-api/wham/usage/daily-token-usage-breakdown?${range}`, token),
    ]);

    const dailyRows = mergeDaily(countsData || { data: [] }, pctData || { data: [] });
    const allowance = estimateAllowance(dailyRows, targetWindow);

    showPanel({
    usage,
    windows,
    targetWindow,
    dailyRows,
    allowance,
    countsOk: !!countsData,
    percentsOk: !!pctData,
    queryStart,
    queryEnd,
    });
    } catch (e) {
    console.error("[Codex Quota Compass]", e);
    alert("Codex Quota Compass 错误:\n" + (e?.message || String(e)));
    } finally {
    btn.disabled = false;
    btn.textContent = "📊 运行用量分析 V2.1";
    }
    }

    function mount() {
    let btn = document.getElementById("codex-compass-btn");
    if (!btn) {
    btn = document.createElement("button");
    btn.id = "codex-compass-btn";
    btn.textContent = "📊 运行用量分析 V2.1";
    btn.onclick = run;
    document.body.appendChild(btn);
    }

    let root = document.getElementById("codex-compass-root");
    if (!root) {
    root = document.createElement("div");
    root.id = "codex-compass-root";
    document.body.appendChild(root);
    }
    }

    if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", mount, { once: true });
    } else {
    mount();
    }
    })();

    改了一下v1.8 版本的,感觉原来哪个不太准,我一个plus号测出来30​^-^,改完后测130​^-^左右

  • 夜碼 08-26 14:21
    5

    所以意思是指 OpenAI 的补贴量是 (9000 * 4) / 200

    180倍那么多吗?

  • Roww25 08-26 14:22
    6

    我的plus测出来也就40刀一周,都怀疑是不是算错了

  • puppywang 08-26 14:23
    7

    25号重置过,这个脚本无法处理重置的情况,你看看你上次重置前用了多少,需要扣减掉那部分的用量

  • Roww25 08-26 14:26
    8

    用了佬你的插件算是6000多分,280刀左右。我看是没统计进来今天的用量,过两天再看看吧

  • El_ 08-26 14:52
    9



    我这正常吗 ^-^一周86刀

  • swack01 08-26 14:55
    10

    这不太清楚 在正常范围内,应该正常的吧 ^-^

  • john180 08-26 14:56
    11

    pro 5x体感是今天重置以后变得更加耐用了,上午用sol xhigh处理了几个简单的修改就只用了1%的周限额。

  • Anglyao 08-26 15:03
    12

    用的V2.1,才3.6w credits,据说正常pro20是5w左右。。已经大约两周没反代了

  • Xeron 08-26 15:04
    13

    我的大兵plus额度还挺多166刀周限

  • popyui 08-26 15:06
    14



    我的plus根本不耐用啊啊啊啊啊

  • El_ 08-26 15:07
    15

    看了一圈没人比我少了,真服了 ^-^

  • linux_master 08-26 15:13
    16

    PLUS 限额后不够用的,中断了重新执行先分析再干事,浪费太多额度了 md

  • haohao 08-26 15:36
    17

    这个怎么用呀油猴脚本吗。为什么不起作用我这边

  • swack01 08-26 15:37
    18



    添加进去,点击开启

  • 左光明 08-26 15:37
    19

    这个是什么工具呀?我也想测试看看

* 帖子来源Linux.do
返回