// ==UserScript==
// @name Codex Resets Debug
// @namespace http://tampermonkey.net/
// @version 2026-07-03-debug
// @author stjuan627
// @match https://chatgpt.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @run-at document-start
// @grant none
// ==/UserScript==
(function () {
‘use strict’;
const BOX_ID = 'codex-resets-debug-box';
function ensureBox() {
let box = document.getElementById(BOX_ID);
if (box) return box;
if (!document.body) return null;
box = document.createElement('div');
box.id = BOX_ID;
box.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
z-index: 2147483647;
width: 420px;
max-height: 70vh;
overflow: auto;
background: #111827;
color: #fff;
font-size: 13px;
line-height: 1.5;
padding: 14px;
border-radius: 12px;
box-shadow: 0 12px 30px rgba(0,0,0,.3);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
`;
box.innerHTML = `
<div style="font-weight:700;margin-bottom:8px;">Codex Resets Debug</div>
<div id="codex-resets-debug-log"></div>
`;
document.body.appendChild(box);
return box;
}
function log(message) {
console.log('[Codex Resets]', message);
const box = ensureBox();
if (!box) return;
const logEl = box.querySelector('#codex-resets-debug-log');
const line = document.createElement('div');
line.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logEl.appendChild(line);
}
function escapeHtml(value) {
return String(value ?? '')
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", ''');
}
function formatDate(value) {
if (!value) return '-';
return new Date(value).toLocaleString();
}
function renderCredits(data) {
const box = ensureBox();
if (!box) return;
const credits = Array.isArray(data?.credits) ? data.credits : [];
const availableCount = data?.available_count ?? credits.length;
const rows = credits.length
? credits.map((item, index) => `
<tr>
<td style="padding:6px;border-bottom:1px solid #374151;">${index + 1}</td>
<td style="padding:6px;border-bottom:1px solid #374151;">${escapeHtml(formatDate(item.granted_at))}</td>
<td style="padding:6px;border-bottom:1px solid #374151;">${escapeHtml(formatDate(item.expires_at))}</td>
</tr>
`).join('')
: `
<tr>
<td colspan="3" style="padding:10px;text-align:center;color:#d1d5db;">没有 reset</td>
</tr>
`;
box.innerHTML = `
<div style="font-weight:700;margin-bottom:8px;">Codex Resets</div>
<div style="margin-bottom:8px;">Available: ${escapeHtml(availableCount)}</div>
<table style="width:100%;border-collapse:collapse;color:#fff;">
<thead>
<tr>
<th style="padding:6px;border-bottom:1px solid #6b7280;text-align:left;">#</th>
<th style="padding:6px;border-bottom:1px solid #6b7280;text-align:left;">获得时间</th>
<th style="padding:6px;border-bottom:1px solid #6b7280;text-align:left;">过期时间</th>
</tr>
</thead>
<tbody>
${rows}
</tbody>
</table>
`;
}
async function main() {
log('script started');
log(`href: ${location.href}`);
if (!location.href.startsWith('https://chatgpt.com/codex/cloud/settings/analytics')) {
log('not analytics page, skip');
return;
}
const sessionResp = await fetch('/api/auth/session', {
credentials: 'include'
});
log(`session status: ${sessionResp.status}`);
if (!sessionResp.ok) {
throw new Error(`/api/auth/session failed: ${sessionResp.status}`);
}
const session = await sessionResp.json();
const accessToken = session?.accessToken;
log(`accessToken: ${accessToken ? 'yes' : 'no'}`);
const headers = {};
if (accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
}
const creditsResp = await fetch('/backend-api/wham/rate-limit-reset-credits', {
method: 'GET',
credentials: 'include',
headers
});
log(`credits status: ${creditsResp.status}`);
const rawText = await creditsResp.text();
log(`credits raw: ${rawText.slice(0, 300)}`);
if (!creditsResp.ok) {
throw new Error(`/backend-api/wham/rate-limit-reset-credits failed: ${creditsResp.status}`);
}
const data = JSON.parse(rawText);
renderCredits(data);
}
const timer = setInterval(() => {
if (document.body) {
clearInterval(timer);
ensureBox();
main().catch(err => {
log(`ERROR: ${err?.message || String(err)}`);
console.error('[Codex Resets] failed:', err);
});
}
}, 100);
})();