Files
openchamber/packages/web/server/lib/quota/providers/codex.js
T
Bohdan Triapitsyn eff6f46ad9 feat: improve mobile UX (#1591)
Added a mobile MCP overlay so MCP tools can be opened and managed from the mobile UI without relying on desktop-only dropdown behavior.
Improved mobile session panel touch handling so tapping the status/session area opens the right panel reliably on phones and tablets.
Cleaned up mobile usage provider metadata by removing duplicate rows, hiding unset providers, and showing provider logos consistently.
Added eager loading for provider logos used in mobile usage views to avoid delayed or missing icons when the panel opens.
Refined the mobile update and about flows in OpenChamber settings so release/update information is easier to read on small screens.
Adjusted related layout, header, VS Code layout, command palette, and settings text/localization details needed for the mobile polish.
2026-06-10 12:00:10 +03:00

114 lines
3.0 KiB
JavaScript

import { readAuthFile } from '../../opencode/auth.js';
import {
getAuthEntry,
normalizeAuthEntry,
buildResult,
toUsageWindow,
toNumber,
toTimestamp,
formatMoney
} from '../utils/index.js';
export const providerId = 'codex';
export const providerName = 'Codex';
export const aliases = ['openai', 'codex', 'chatgpt'];
export const isConfigured = () => {
const auth = readAuthFile();
const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
return Boolean(entry?.access || entry?.token);
};
export const fetchQuota = async () => {
const auth = readAuthFile();
const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
const accessToken = entry?.access ?? entry?.token;
const accountId = entry?.accountId;
if (!accessToken) {
return buildResult({
providerId,
providerName,
ok: false,
configured: false,
error: 'Not configured'
});
}
try {
const headers = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
...(accountId ? { 'ChatGPT-Account-Id': accountId } : {})
};
const response = await fetch('https://chatgpt.com/backend-api/wham/usage', {
method: 'GET',
headers
});
if (!response.ok) {
return buildResult({
providerId,
providerName,
ok: false,
configured: true,
error: response.status === 401
? 'Session expired \u2014 please re-authenticate with OpenAI'
: `API error: ${response.status}`
});
}
const payload = await response.json();
const primary = payload?.rate_limit?.primary_window ?? null;
const secondary = payload?.rate_limit?.secondary_window ?? null;
const credits = payload?.credits ?? null;
const windows = {};
if (primary) {
windows['5h'] = toUsageWindow({
usedPercent: toNumber(primary.used_percent),
windowSeconds: toNumber(primary.limit_window_seconds),
resetAt: toTimestamp(primary.reset_at)
});
}
if (secondary) {
windows['weekly'] = toUsageWindow({
usedPercent: toNumber(secondary.used_percent),
windowSeconds: toNumber(secondary.limit_window_seconds),
resetAt: toTimestamp(secondary.reset_at)
});
}
if (credits) {
const balance = toNumber(credits.balance);
const unlimited = Boolean(credits.unlimited);
const label = unlimited
? 'Unlimited'
: balance !== null
? `$${formatMoney(balance)}`
: null;
windows.credits_balance = toUsageWindow({
usedPercent: null,
windowSeconds: null,
resetAt: null,
valueLabel: label
});
}
return buildResult({
providerId,
providerName,
ok: true,
configured: true,
usage: { windows }
});
} catch (error) {
return buildResult({
providerId,
providerName,
ok: false,
configured: true,
error: error instanceof Error ? error.message : 'Request failed'
});
}
};