Codex 周限到底用了多少,其实可以自己查。
一次性使用代码
(() => {
"use strict";
const addStyle = (css) => {
const style = document.createElement("style");
style.textContent = css;
document.head.appendChild(style);
};
const CONFIG = {
USD_PER_CREDIT: 40 / 1000,
};
const fmtNum = (n) => {
if (n >= 1e8) return (n / 1e8).toFixed(2) + "亿";
if (n >= 1e4) return (n / 1e4).toFixed(1) + "万";
return Number(n || 0).toLocaleString();
};
const n = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
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);
function getPageBearerToken() {
const bootstrapData =
document.getElementById("client-bootstrap")?.textContent || "";
return bootstrapData.match(
/[\w-]{30,}\.[\w-]{30,}\.[\w-]{30,}/,
)?.[0];
}
document.getElementById("codex-compass-btn")?.remove();
document.getElementById("codex-compass-root")?.remove();
addStyle(`
#codex-compass-root {
position: fixed; top: 5%; left: 50%; transform: translateX(-50%);
width: 720px; max-height: 90vh; overflow: auto; background: #fff;
border-radius: 12px; box-shadow: 0 10px 50px rgba(0,0,0,0.3);
z-index: 2147483647; padding: 24px; display: none;
flex-direction: column; border: 1px solid #e5e5e5; color: #333;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
}
.compass-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.compass-title { font-size: 18px; font-weight: 600; }
.compass-close { cursor: pointer; font-size: 28px; color: #999; line-height: 1; }
.compass-note { font-size: 12px; color: #777; line-height: 1.6; margin: -4px 0 16px; }
.compass-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 20px; }
.compass-card { background: #f9f9f9; padding: 12px; border-radius: 8px; border: 1px solid #eee; }
.compass-card.highlight { background: #eefaf5; border-color: #d1f2e1; }
.card-label { font-size: 12px; color: #666; margin-bottom: 4px; }
.card-value { font-size: 15px; font-weight: 700; color: #10a37f; white-space: nowrap; }
.table-container { max-height: 220px; overflow-y: auto; border: 1px solid #eee; border-radius: 6px; margin-bottom: 15px; }
.compass-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.compass-table thead { position: sticky; top: 0; background: #f5f5f5; z-index: 1; }
.compass-table th { text-align: left; padding: 8px; border-bottom: 1px solid #eee; color: #666; }
.compass-table td { padding: 8px; border-bottom: 1px solid #f0f0f0; }
.compass-footer-row { background: #fafafa; font-weight: 700; position: sticky; bottom: 0; border-top: 2px solid #eee; }
#codex-compass-btn {
position: fixed; bottom: 20px; right: 20px; z-index: 2147483646;
padding: 10px 20px; background: #10a37f; color: white;
border: none; border-radius: 8px; cursor: pointer; font-weight: 600;
}
`);
async function apiGet(path, token) {
if (!token) {
throw new Error("未找到页面 Bearer token,无法访问 Codex usage 接口。");
}
const res = await fetch(path, {
headers: {
Authorization: `Bearer ${token}`,
},
credentials: "omit",
});
const text = await res.text();
if (!res.ok) {
throw new Error(`${path} HTTP ${res.status}: ${text.slice(0, 300)}`);
}
try {
return JSON.parse(text);
} catch {
throw new Error(`${path} returned non-JSON: ${text.slice(0, 300)}`);
}
}
function getStats(list) {
const credits = list.reduce((sum, d) => sum + n(d.totals?.credits), 0);
const turns = list.reduce((sum, d) => sum + n(d.totals?.turns), 0);
const tokens = list.reduce((sum, d) => sum + tokenTotal(d.totals), 0);
return { credits, turns, tokens, usd: credits * CONFIG.USD_PER_CREDIT };
}
function renderTable(list, stats) {
return `
<div class="table-container">
<table class="compass-table">
<thead>
<tr><th>日期</th><th>Credits</th><th>Tokens</th><th>金额</th><th>轮数</th></tr>
</thead>
<tbody>
${[...list].reverse().map((row) => `
<tr>
<td>${row.date}</td>
<td style="font-family:monospace">${n(row.totals?.credits).toFixed(3)}</td>
<td style="font-family:monospace">${fmtNum(tokenTotal(row.totals))}</td>
<td>$ ${(n(row.totals?.credits) * CONFIG.USD_PER_CREDIT).toFixed(2)}</td>
<td>${n(row.totals?.turns)}</td>
</tr>
`).join("")}
</tbody>
<tfoot>
<tr class="compass-footer-row">
<td>合计</td>
<td>${stats.credits.toFixed(3)}</td>
<td style="font-family:monospace">${fmtNum(stats.tokens)}</td>
<td style="color:#10a37f">$ ${stats.usd.toFixed(2)}</td>
<td>${stats.turns}</td>
</tr>
</tfoot>
</table>
</div>
`;
}
function showPanel(data) {
const root = document.getElementById("codex-compass-root");
const { secondary, dailyList, cycleStartDate } = data;
const currentCycleList = [];
const historyList = [];
dailyList.forEach((item) => {
if (new Date(item.date) >= new Date(cycleStartDate)) {
currentCycleList.push(item);
} else {
historyList.push(item);
}
});
const currentStats = getStats(currentCycleList);
const historyStats = getStats(historyList);
const ratio = n(secondary?.used_percent) / 100;
const estCredits = ratio > 0 ? currentStats.credits / ratio : 0;
let historyRangeTitle = "历史记录(本周期外)";
if (historyList.length > 0) {
const sortedHistory = [...historyList].sort(
(a, b) => new Date(a.date) - new Date(b.date),
);
historyRangeTitle =
`历史记录(本周期外 ${sortedHistory[0].date} 至 ${sortedHistory.at(-1).date})`;
}
root.innerHTML = `
<div class="compass-header">
<div class="compass-title">Codex 配额分析</div>
<div class="compass-close" id="compass-close-btn">×</div>
</div>
<div class="compass-note">
说明:analytics 可能延迟几小时;周期切换通常按美西时间计算。脚本会读取当前 chatgpt.com 页面内已有的 Bearer token,仅用于请求 chatgpt.com 同域 usage / analytics 接口;不读取 cookie 内容,不打印、不保存、不上传 token。
</div>
<div class="compass-grid">
<div class="compass-card">
<div class="card-label">已用比例</div>
<div class="card-value">${n(secondary?.used_percent)}%</div>
</div>
<div class="compass-card">
<div class="card-label">本周期已用</div>
<div class="card-value">${currentStats.credits.toFixed(1)} Credits</div>
</div>
<div class="compass-card highlight">
<div class="card-label">推算总额</div>
<div class="card-value">${estCredits.toFixed(1)} Credits</div>
</div>
<div class="compass-card">
<div class="card-label">周期价值估算</div>
<div class="card-value">$ ${(estCredits * CONFIG.USD_PER_CREDIT).toFixed(2)}</div>
</div>
</div>
<div style="font-weight:600; margin-bottom:8px; font-size:13px;">本周期明细(始于 ${cycleStartDate})</div>
${renderTable(currentCycleList, currentStats)}
${
historyList.length > 0
? `<div style="font-weight:600; margin-bottom:8px; font-size:13px; color:#666;">${historyRangeTitle}</div>
${renderTable(historyList, historyStats)}`
: ""
}
`;
root.style.display = "flex";
document.getElementById("compass-close-btn").onclick = () => {
root.style.display = "none";
};
}
async function run() {
const btn = document.getElementById("codex-compass-btn");
btn.innerText = "分析中...";
try {
const token = getPageBearerToken();
const usage = await apiGet("/backend-api/wham/usage", token);
const secondary =
usage?.rate_limit?.secondary_window ||
usage?.rate_limit?.primary_window;
const endDate = new Date(Date.now() + 86400000)
.toISOString()
.split("T")[0];
const startDate = new Date(Date.now() - 30 * 86400000)
.toISOString()
.split("T")[0];
const cycleStartDate = secondary
? new Date((secondary.reset_at - secondary.limit_window_seconds) * 1000)
.toISOString()
.split("T")[0]
: startDate;
const dailyData = await apiGet(
`/backend-api/wham/analytics/daily-workspace-usage-counts?start_date=${startDate}&end_date=${endDate}&group_by=day`,
token,
);
showPanel({
secondary,
dailyList: dailyData.data || [],
cycleStartDate,
});
} catch (e) {
console.error("[Codex Compass]", e);
alert("错误: " + e.message);
} finally {
btn.innerText = "运行用量分析";
}
}
const btn = document.createElement("button");
btn.id = "codex-compass-btn";
btn.innerText = "运行用量分析";
btn.onclick = run;
document.body.appendChild(btn);
const root = document.createElement("div");
root.id = "codex-compass-root";
document.body.appendChild(root);
})();油猴脚本代码
// ==UserScript==
// @name Codex Quota Compass V2.0
// @namespace https://chatgpt.com/
// @version 2.0
// @description 展示 Codex 配额、Credits、Tokens、周期估算与历史用量
// @match https://chatgpt.com/*
// @run-at document-idle
// @grant GM_addStyle
// ==/UserScript==
(() => {
"use strict";
const CONFIG = {
USD_PER_CREDIT: 40 / 1000,
};
const TARGET_PATH = "/codex/cloud/settings/analytics";
const fmtNum = (n) => {
if (n >= 1e8) return (n / 1e8).toFixed(2) + "亿";
if (n >= 1e4) return (n / 1e4).toFixed(1) + "万";
return Number(n || 0).toLocaleString();
};
const n = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
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);
function getPageBearerToken() {
const bootstrapData =
document.getElementById("client-bootstrap")?.textContent || "";
return bootstrapData.match(
/[\w-]{30,}\.[\w-]{30,}\.[\w-]{30,}/,
)?.[0];
}
GM_addStyle(`
#codex-compass-root {
position: fixed; top: 5%; left: 50%; transform: translateX(-50%);
width: 720px; max-height: 90vh; overflow: auto; background: #fff;
border-radius: 12px; box-shadow: 0 10px 50px rgba(0,0,0,0.3);
z-index: 2147483647; padding: 24px; display: none;
flex-direction: column; border: 1px solid #e5e5e5; color: #333;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
}
.compass-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.compass-title { font-size: 18px; font-weight: 600; }
.compass-close { cursor: pointer; font-size: 28px; color: #999; line-height: 1; }
.compass-note { font-size: 12px; color: #777; line-height: 1.6; margin: -4px 0 16px; }
.compass-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 20px; }
.compass-card { background: #f9f9f9; padding: 12px; border-radius: 8px; border: 1px solid #eee; }
.compass-card.highlight { background: #eefaf5; border-color: #d1f2e1; }
.card-label { font-size: 12px; color: #666; margin-bottom: 4px; }
.card-value { font-size: 15px; font-weight: 700; color: #10a37f; white-space: nowrap; }
.table-container { max-height: 220px; overflow-y: auto; border: 1px solid #eee; border-radius: 6px; margin-bottom: 15px; }
.compass-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.compass-table thead { position: sticky; top: 0; background: #f5f5f5; z-index: 1; }
.compass-table th { text-align: left; padding: 8px; border-bottom: 1px solid #eee; color: #666; }
.compass-table td { padding: 8px; border-bottom: 1px solid #f0f0f0; }
.compass-footer-row { background: #fafafa; font-weight: 700; position: sticky; bottom: 0; border-top: 2px solid #eee; }
#codex-compass-btn {
position: fixed; bottom: 20px; right: 20px; z-index: 2147483646;
padding: 10px 20px; background: #10a37f; color: white;
border: none; border-radius: 8px; cursor: pointer; font-weight: 600;
}
`);
async function apiGet(path, token) {
if (!token) {
throw new Error("未找到页面 Bearer token,无法访问 Codex usage 接口。");
}
const res = await fetch(path, {
headers: {
Authorization: `Bearer ${token}`,
},
credentials: "omit",
});
const text = await res.text();
if (!res.ok) {
throw new Error(`${path} HTTP ${res.status}: ${text.slice(0, 300)}`);
}
try {
return JSON.parse(text);
} catch {
throw new Error(`${path} returned non-JSON: ${text.slice(0, 300)}`);
}
}
function getStats(list) {
const credits = list.reduce((sum, d) => sum + n(d.totals?.credits), 0);
const turns = list.reduce((sum, d) => sum + n(d.totals?.turns), 0);
const tokens = list.reduce((sum, d) => sum + tokenTotal(d.totals), 0);
return { credits, turns, tokens, usd: credits * CONFIG.USD_PER_CREDIT };
}
function renderTable(list, stats) {
return `
<div class="table-container">
<table class="compass-table">
<thead>
<tr><th>日期</th><th>Credits</th><th>Tokens</th><th>金额</th><th>轮数</th></tr>
</thead>
<tbody>
${[...list].reverse().map((row) => `
<tr>
<td>${row.date}</td>
<td style="font-family:monospace">${n(row.totals?.credits).toFixed(3)}</td>
<td style="font-family:monospace">${fmtNum(tokenTotal(row.totals))}</td>
<td>$ ${(n(row.totals?.credits) * CONFIG.USD_PER_CREDIT).toFixed(2)}</td>
<td>${n(row.totals?.turns)}</td>
</tr>
`).join("")}
</tbody>
<tfoot>
<tr class="compass-footer-row">
<td>合计</td>
<td>${stats.credits.toFixed(3)}</td>
<td style="font-family:monospace">${fmtNum(stats.tokens)}</td>
<td style="color:#10a37f">$ ${stats.usd.toFixed(2)}</td>
<td>${stats.turns}</td>
</tr>
</tfoot>
</table>
</div>
`;
}
function showPanel(data) {
const root = document.getElementById("codex-compass-root");
const { secondary, dailyList, cycleStartDate } = data;
const currentCycleList = [];
const historyList = [];
dailyList.forEach((item) => {
if (new Date(item.date) >= new Date(cycleStartDate)) {
currentCycleList.push(item);
} else {
historyList.push(item);
}
});
const currentStats = getStats(currentCycleList);
const historyStats = getStats(historyList);
const ratio = n(secondary?.used_percent) / 100;
const estCredits = ratio > 0 ? currentStats.credits / ratio : 0;
let historyRangeTitle = "历史记录(本周期外)";
if (historyList.length > 0) {
const sortedHistory = [...historyList].sort(
(a, b) => new Date(a.date) - new Date(b.date),
);
historyRangeTitle =
`历史记录(本周期外 ${sortedHistory[0].date} 至 ${sortedHistory.at(-1).date})`;
}
root.innerHTML = `
<div class="compass-header">
<div class="compass-title">Codex 配额分析</div>
<div class="compass-close" id="compass-close-btn">×</div>
</div>
<div class="compass-note">
说明:analytics 可能延迟几小时;周期切换通常按美西时间计算。脚本会读取当前 chatgpt.com 页面内已有的 Bearer token,仅用于请求 chatgpt.com 同域 usage / analytics 接口;不读取 cookie 内容,不打印、不保存、不上传 token。
</div>
<div class="compass-grid">
<div class="compass-card">
<div class="card-label">已用比例</div>
<div class="card-value">${n(secondary?.used_percent)}%</div>
</div>
<div class="compass-card">
<div class="card-label">本周期已用</div>
<div class="card-value">${currentStats.credits.toFixed(1)} Credits</div>
</div>
<div class="compass-card highlight">
<div class="card-label">推算总额</div>
<div class="card-value">${estCredits.toFixed(1)} Credits</div>
</div>
<div class="compass-card">
<div class="card-label">周期价值估算</div>
<div class="card-value">$ ${(estCredits * CONFIG.USD_PER_CREDIT).toFixed(2)}</div>
</div>
</div>
<div style="font-weight:600; margin-bottom:8px; font-size:13px;">本周期明细(始于 ${cycleStartDate})</div>
${renderTable(currentCycleList, currentStats)}
${
historyList.length > 0
? `<div style="font-weight:600; margin-bottom:8px; font-size:13px; color:#666;">${historyRangeTitle}</div>
${renderTable(historyList, historyStats)}`
: ""
}
`;
root.style.display = "flex";
document.getElementById("compass-close-btn").onclick = () => {
root.style.display = "none";
};
}
async function run() {
const btn = document.getElementById("codex-compass-btn");
btn.innerText = "分析中...";
try {
const token = getPageBearerToken();
const usage = await apiGet("/backend-api/wham/usage", token);
const secondary =
usage?.rate_limit?.secondary_window ||
usage?.rate_limit?.primary_window;
const endDate = new Date(Date.now() + 86400000)
.toISOString()
.split("T")[0];
const startDate = new Date(Date.now() - 30 * 86400000)
.toISOString()
.split("T")[0];
const cycleStartDate = secondary
? new Date((secondary.reset_at - secondary.limit_window_seconds) * 1000)
.toISOString()
.split("T")[0]
: startDate;
const dailyData = await apiGet(
`/backend-api/wham/analytics/daily-workspace-usage-counts?start_date=${startDate}&end_date=${endDate}&group_by=day`,
token,
);
showPanel({
secondary,
dailyList: dailyData.data || [],
cycleStartDate,
});
} catch (e) {
console.error("[Codex Compass]", e);
alert("错误: " + e.message);
} finally {
btn.innerText = "运行用量分析";
}
}
function removePanel() {
document.getElementById("codex-compass-btn")?.remove();
document.getElementById("codex-compass-root")?.remove();
}
function mount() {
removePanel();
const btn = document.createElement("button");
btn.id = "codex-compass-btn";
btn.innerText = "运行用量分析";
btn.onclick = run;
document.body.appendChild(btn);
const root = document.createElement("div");
root.id = "codex-compass-root";
document.body.appendChild(root);
}
function boot() {
if (location.pathname === TARGET_PATH) {
if (!document.getElementById("codex-compass-btn")) {
mount();
}
} else {
removePanel();
}
}
boot();
setInterval(boot, 1000);
})();继续阅读
基于全文检索与主题相似度
AI工具
基于 Codex 30 天工作记录识别可封装 workflows
本文介绍如何让 Codex 回顾近 30 天工作记录、sessions、memories、Chronicle 与现有资产,识别高频且可复用的工作流,并按证据封装为 skill、subagent 或 automation,避免重复、过宽和 speculative 创建,适合团队定期盘点与效率优化。
AI工具
Codex 对话日志复盘提示词
整理 Codex 对话日志复盘提示词,说明如何检索历史 sessions、执行日志与配置,提炼用户偏好、经验规则并接入 .agent、AGENTS.md 等加载链路。
AI工具
Antigravity 2.0 账号地区不可用
# Antigravity 2.0 账号地区不可用 记录 Antigravity 2.0 登录时出现账号不可用提示的排查与处理。适用于错误: ```text Sorry, this account is ineligible to use Antigravity Authentication failed. ``` !