feat(quota): add opt-in Codex auto-ping
Generalize Claude 5h auto-ping into a provider-generic scheduler and add opt-in Codex auto-ping that warms the next 5h window via a tiny gpt-5.5 request when session.resetAt slides. Default off, per-connection toggle, failure cooldown, blocking-quota skip, drains stream before success. Closes #2107 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
fc8722e897
commit
b66b5c68ce
9 files changed
+757
-168
No files matched your search
@@ -23,6 +23,9 @@ export default function ConnectionRow({ connection, proxyPools, isOAuth, isFirst
|
||||
: hasLegacyProxy
|
||||
? `Legacy: ${connection.providerSpecificData?.connectionProxyUrl}`
|
||||
: "";
|
||||
const autoPingTooltip = autoPing?.provider === "codex"
|
||||
? "Auto-starts the next 5h Codex window after reset by sending a tiny gpt-5.5 request. Consumes a small amount of quota."
|
||||
: "When your 5h quota runs out, auto-sends a request the moment it resets so a new window starts right away.";
|
||||
|
||||
let maskedProxyUrl = "";
|
||||
if (boundProxyPool?.proxyUrl || connection.providerSpecificData?.connectionProxyUrl) {
|
||||
@@ -244,7 +247,7 @@ export default function ConnectionRow({ connection, proxyPools, isOAuth, isFirst
|
||||
</div>
|
||||
)}
|
||||
{autoPing && (
|
||||
<Tooltip text="When your 5h quota runs out, auto-sends a request the moment it resets so a new window starts right away.">
|
||||
<Tooltip text={autoPingTooltip}>
|
||||
<button
|
||||
onClick={() => autoPing.onToggle(!autoPing.on)}
|
||||
className={`flex w-full flex-col items-center rounded px-2 py-1 transition-colors hover:bg-black/5 dark:hover:bg-white/5 ${autoPing.on ? "text-primary" : "text-text-muted hover:text-primary"}`}
|
||||
@@ -310,5 +313,6 @@ ConnectionRow.propTypes = {
|
||||
autoPing: PropTypes.shape({
|
||||
on: PropTypes.bool,
|
||||
onToggle: PropTypes.func,
|
||||
provider: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
@@ -23,6 +23,11 @@ import BulkImportCodexModal from "./BulkImportCodexModal";
|
||||
|
||||
const ONE_BY_ONE_DELAY_MS = 1000;
|
||||
|
||||
const AUTO_PING_SETTINGS_KEYS = {
|
||||
claude: "claudeAutoPing",
|
||||
codex: "codexAutoPing",
|
||||
};
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
@@ -273,7 +278,8 @@ export default function ProviderDetailPage() {
|
||||
// Load per-provider thinking config
|
||||
const thinkingCfg = (settingsData.providerThinking || {})[providerId] || {};
|
||||
setThinkingMode(thinkingCfg.mode || "auto");
|
||||
const apCfg = settingsData.claudeAutoPing || {};
|
||||
const autoPingSettingsKey = AUTO_PING_SETTINGS_KEYS[providerId];
|
||||
const apCfg = autoPingSettingsKey ? settingsData[autoPingSettingsKey] || {} : {};
|
||||
setAutoPing({ enabled: apCfg.enabled === true, connections: apCfg.connections || {} });
|
||||
if (nodesRes.ok) {
|
||||
let node = (nodesData.nodes || []).find((entry) => entry.id === providerId) || null;
|
||||
@@ -388,12 +394,15 @@ export default function ProviderDetailPage() {
|
||||
};
|
||||
|
||||
const saveAutoPing = async (next) => {
|
||||
const autoPingSettingsKey = AUTO_PING_SETTINGS_KEYS[providerId];
|
||||
if (!autoPingSettingsKey) return;
|
||||
|
||||
setAutoPing(next);
|
||||
try {
|
||||
await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ claudeAutoPing: next }),
|
||||
body: JSON.stringify({ [autoPingSettingsKey]: next }),
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error saving auto-ping config:", error);
|
||||
@@ -888,9 +897,10 @@ export default function ProviderDetailPage() {
|
||||
onMoveUp={() => handleSwapPriority(index, index - 1)}
|
||||
onMoveDown={() => handleSwapPriority(index, index + 1)}
|
||||
onToggleActive={(isActive) => handleUpdateConnectionStatus(conn.id, isActive)}
|
||||
autoPing={providerId === "claude" && conn.authType === "oauth" ? {
|
||||
autoPing={AUTO_PING_SETTINGS_KEYS[providerId] && conn.authType === "oauth" ? {
|
||||
on: autoPing.connections[conn.id] === true,
|
||||
onToggle: (on) => handleAutoPingConnection(conn.id, on),
|
||||
provider: providerId,
|
||||
} : null}
|
||||
onUpdateProxy={async (proxyPoolId) => {
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user