feat(usage): add xAI quota reporting #2628

feat(usage): add xAI quota reporting
This commit is contained in:
Bohdan Triapitsyn
2026-08-07 11:48:56 +03:00
committed by GitHub
9 changed files with 832 additions and 7 deletions
+5 -2
View File
@@ -25,16 +25,19 @@ function readAuthFile() {
function writeAuthFile(auth) {
try {
if (!fs.existsSync(OPENCODE_DATA_DIR)) {
fs.mkdirSync(OPENCODE_DATA_DIR, { recursive: true });
fs.mkdirSync(OPENCODE_DATA_DIR, { recursive: true, mode: 0o700 });
}
if (process.platform !== 'win32') fs.chmodSync(OPENCODE_DATA_DIR, 0o700);
if (fs.existsSync(AUTH_FILE)) {
const backupFile = `${AUTH_FILE}.openchamber.backup`;
fs.copyFileSync(AUTH_FILE, backupFile);
if (process.platform !== 'win32') fs.chmodSync(backupFile, 0o600);
console.log(`Created auth backup: ${backupFile}`);
}
fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), 'utf8');
fs.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { encoding: 'utf8', mode: 0o600 });
if (process.platform !== 'win32') fs.chmodSync(AUTH_FILE, 0o600);
console.log('Successfully wrote auth file');
} catch (error) {
console.error('Failed to write auth file:', error);
@@ -35,6 +35,7 @@ These provider IDs are currently dispatchable via `fetchQuotaForProvider(provide
| `wafer` | Wafer.ai | `providers/wafer.js` | `wafer`, `wafer-ai`, `wafer_ai`, `wafer.ai` |
| `opencode-go` | OpenCode Go | `providers/opencode-go.js` | Manual workspace ID and auth cookie stored under `~/.config/openchamber/quota/` |
| `neuralwatt` | NeuralWatt | `providers/neuralwatt.js` | `neuralwatt` (API key under `key` or `token`) |
| `xai` | xAI | `providers/xai.js` | `xai` OAuth entry in OpenCode `auth.json` |
## Internal-only provider module
- `providers/openai.js` exists for logic parity/reuse but is intentionally not registered for dispatcher ID routing.
@@ -26,6 +26,7 @@ import * as neuralwatt from './neuralwatt.js';
import * as ollamaCloud from './ollama-cloud.js';
import * as wafer from './wafer.js';
import * as opencodeGo from './opencode-go.js';
import * as xai from './xai.js';
const registry = {
claude: {
@@ -141,6 +142,12 @@ const registry = {
providerName: neuralwatt.providerName,
isConfigured: neuralwatt.isConfigured,
fetchQuota: neuralwatt.fetchQuota
},
xai: {
providerId: xai.providerId,
providerName: xai.providerName,
isConfigured: xai.isConfigured,
fetchQuota: xai.fetchQuota
}
};
@@ -0,0 +1,396 @@
import { readAuthFile, writeAuthFile } from '../../opencode/auth.js';
import { buildResult, toUsageWindow } from '../utils/index.js';
export const providerId = 'xai';
export const providerName = 'xAI';
const USAGE_URL = 'https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig';
const TOKEN_URL = 'https://auth.x.ai/oauth2/token';
const CLIENT_ID = 'b1a00492-073a-47ea-816f-4c329264a828';
const REFRESH_SKEW_MS = 120_000;
const REQUEST_TIMEOUT_MS = 15_000;
const EMPTY_GRPC_WEB_BODY = new Uint8Array([0, 0, 0, 0, 0]);
let refreshPromise = null;
const nonEmptyString = (value) => {
if (typeof value !== 'string') return null;
const trimmed = value.trim();
return trimmed ? trimmed : null;
};
const readXaiAuth = () => {
try {
const entry = readAuthFile()?.xai;
if (!entry || typeof entry !== 'object' || entry.type !== 'oauth') {
return { entry: null, error: null };
}
if (!nonEmptyString(entry.access) && !nonEmptyString(entry.refresh)) {
return { entry: null, error: null };
}
return { entry, error: null };
} catch {
return { entry: null, error: 'Failed to read xAI OAuth credentials' };
}
};
const decodeJwtClaims = (token) => {
try {
const payload = token.split('.')[1];
if (!payload) return null;
return JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
} catch {
return null;
}
};
const tokenNeedsRefresh = (entry) => {
const access = nonEmptyString(entry.access);
if (!access) return true;
const refreshDeadline = Date.now() + REFRESH_SKEW_MS;
const storedExpiry = Number(entry.expires);
if (Number.isFinite(storedExpiry) && storedExpiry <= refreshDeadline) return true;
const jwtExpiry = Number(decodeJwtClaims(access)?.exp) * 1000;
return Number.isFinite(jwtExpiry) && jwtExpiry <= refreshDeadline;
};
const refreshXaiOauth = async (entry) => {
if (!refreshPromise) {
refreshPromise = (async () => {
const refreshToken = nonEmptyString(entry.refresh);
if (!refreshToken) {
throw new Error('xAI OAuth entry has no usable refresh token');
}
const response = await fetch(TOKEN_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: CLIENT_ID,
refresh_token: refreshToken,
grant_type: 'refresh_token'
}),
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
});
if (!response.ok) {
throw new Error(`xAI OAuth refresh failed with HTTP ${response.status}`);
}
let payload;
try {
payload = await response.json();
} catch {
throw new Error('xAI OAuth refresh returned invalid JSON');
}
const access = nonEmptyString(payload?.access_token);
if (!access) {
throw new Error('xAI OAuth refresh returned no access token');
}
const expiresIn = payload?.expires_in ?? 3600;
if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn)) {
throw new Error('xAI OAuth refresh returned an invalid expiry');
}
const expires = Date.now() + expiresIn * 1000;
const refreshed = {
...entry,
type: 'oauth',
access,
refresh: nonEmptyString(payload?.refresh_token) ?? refreshToken,
expires
};
const auth = readAuthFile();
auth.xai = refreshed;
writeAuthFile(auth);
return refreshed;
})().finally(() => {
refreshPromise = null;
});
}
return refreshPromise;
};
const ensureFreshAccess = async (entry) => {
if (!tokenNeedsRefresh(entry)) return entry;
if (!nonEmptyString(entry.refresh)) {
throw new Error('xAI OAuth access token is expired and has no usable refresh token');
}
return refreshXaiOauth(entry);
};
const readVarint = (bytes, state) => {
let value = 0n;
for (let shift = 0n; state.index < bytes.length && shift < 64n; shift += 7n) {
const byte = bytes[state.index++];
if (shift === 63n && (byte & 0x7e) !== 0) return null;
value |= BigInt(byte & 0x7f) << shift;
if ((byte & 0x80) === 0) return value;
}
return null;
};
const samePath = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
// CodexBar observes both the flat billing message and the response envelope.
const USAGE_PERCENT_PATHS = [[1], [1, 1]];
const hasPath = (paths, candidate) => paths.some((path) => samePath(path, candidate));
const scanProtobuf = (bytes, path = [], depth = 0, state = { index: 0, order: 0 }) => {
const fixed32Fields = [];
const varintFields = [];
while (state.index < bytes.length) {
const key = readVarint(bytes, state);
if (key === null || key === 0n) return false;
const fieldNumber = Number(key >> 3n);
const wireType = Number(key & 0x07n);
if (!fieldNumber || fieldNumber > 0x1fffffff) return false;
const fieldPath = [...path, fieldNumber];
if (wireType === 0) {
const value = readVarint(bytes, state);
if (value === null) return false;
varintFields.push({ path: fieldPath, value });
continue;
}
if (wireType === 1) {
if (state.index + 8 > bytes.length) return false;
state.index += 8;
continue;
}
if (wireType === 2) {
const length = readVarint(bytes, state);
if (length === null || length > BigInt(bytes.length - state.index)) return false;
const end = state.index + Number(length);
if (depth >= 4 && length !== 0n) return false;
if (depth < 4) {
const nestedState = { index: 0, order: state.order };
const nested = scanProtobuf(bytes.slice(state.index, end), fieldPath, depth + 1, nestedState);
if (nested === false) return false;
fixed32Fields.push(...nested.fixed32Fields);
varintFields.push(...nested.varintFields);
state.order = nestedState.order;
}
state.index = end;
continue;
}
if (wireType === 5) {
if (state.index + 4 > bytes.length) return false;
const value = Buffer.from(bytes.slice(state.index, state.index + 4)).readFloatLE(0);
fixed32Fields.push({ path: fieldPath, value, order: state.order++ });
state.index += 4;
continue;
}
return false;
}
return { fixed32Fields, varintFields };
};
const parseFrames = (bytes) => {
if (bytes.length < 5 || (bytes[0] & 0x7f) !== 0) return null;
const messages = [];
const trailerStatuses = [];
let trailerStarted = false;
let index = 0;
while (index < bytes.length) {
if (index + 5 > bytes.length) return false;
const flags = bytes[index++];
if ((flags & 0x7f) !== 0) return false;
const isTrailer = (flags & 0x80) !== 0;
if (trailerStarted && !isTrailer) return false;
const length = (bytes[index] * 0x1000000)
+ (bytes[index + 1] << 16)
+ (bytes[index + 2] << 8)
+ bytes[index + 3];
index += 4;
const end = index + length;
if (end > bytes.length) return false;
const payload = bytes.slice(index, end);
if (isTrailer) {
trailerStarted = true;
const status = parseGrpcTrailerStatus(payload);
if (status === null) return false;
trailerStatuses.push(status);
} else {
messages.push(payload);
}
index = end;
}
return { messages, trailerStatuses };
};
const parseGrpcTrailerStatus = (bytes) => {
let text;
try {
text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
} catch {
return null;
}
let status = null;
for (const line of text.split(/\r?\n/)) {
if (!line) continue;
const separator = line.indexOf(':');
if (separator <= 0) return null;
const key = line.slice(0, separator).trim().toLowerCase();
if (!key) return null;
if (key !== 'grpc-status') continue;
if (status !== null) return null;
const rawStatus = line.slice(separator + 1).trim();
if (!/^\d+$/.test(rawStatus)) return null;
status = Number(rawStatus);
if (!Number.isSafeInteger(status)) return null;
}
return status;
};
const looksLikeProtobuf = (bytes) => {
if (!bytes.length) return false;
const fieldNumber = bytes[0] >> 3;
const wireType = bytes[0] & 0x07;
return fieldNumber > 0 && [0, 1, 2, 5].includes(wireType);
};
const parseUsage = (bytes) => {
const framed = parseFrames(bytes);
if (framed === false) throw new Error('xAI billing returned malformed gRPC-web framing');
const payloads = framed ? framed.messages : (looksLikeProtobuf(bytes) ? [bytes] : []);
if (framed) {
for (const status of framed.trailerStatuses) {
if (status !== 0) throw new Error(`xAI billing RPC failed with status ${status}`);
}
}
if (payloads.length === 0) throw new Error('xAI billing returned an empty protobuf response');
const scan = { fixed32Fields: [], varintFields: [] };
for (const payload of payloads) {
const result = scanProtobuf(payload);
if (result === false) throw new Error('xAI billing returned malformed protobuf');
scan.fixed32Fields.push(...result.fixed32Fields);
scan.varintFields.push(...result.varintFields);
}
const percentages = scan.fixed32Fields
.filter((field) => (
hasPath(USAGE_PERCENT_PATHS, field.path)
&& Number.isFinite(field.value)
&& field.value >= 0
&& field.value <= 100
))
.sort((left, right) => left.path.length - right.path.length || left.order - right.order);
const usedPercent = percentages.length > 0 ? percentages[0].value : null;
const resetCandidates = scan.varintFields
.filter((field) => field.value >= 1_700_000_000n && field.value <= 2_100_000_000n)
.map((field) => ({ ...field, seconds: Number(field.value) }))
.map((field) => ({ ...field, resetAt: field.seconds * 1000 }))
.filter((field) => field.resetAt > Date.now());
const preferredReset = resetCandidates.filter((field) => samePath(field.path, [1, 5, 1]));
const resetAt = (preferredReset.length > 0 ? preferredReset : resetCandidates)
.sort((left, right) => left.resetAt - right.resetAt)[0]?.resetAt ?? null;
const hasUsagePeriod = scan.varintFields.some((field) => (
(field.path.length >= 2 && field.path[0] === 1 && field.path[1] === 6)
|| (samePath(field.path, [1, 8, 1]) && (field.value === 1n || field.value === 2n))
));
if (usedPercent === null && scan.fixed32Fields.length === 0 && resetAt !== null && hasUsagePeriod) {
return { usedPercent: 0, resetAt };
}
if (usedPercent === null) throw new Error('xAI billing response had no usable current-period usage');
return { usedPercent, resetAt };
};
const fetchUsage = async (accessToken) => {
const response = await fetch(USAGE_URL, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
Origin: 'https://grok.com',
Referer: 'https://grok.com/?_s=usage',
Accept: '*/*',
'Content-Type': 'application/grpc-web+proto',
'x-grpc-web': '1',
'x-user-agent': 'connect-es/2.1.1',
'User-Agent': 'OpenChamber'
},
body: EMPTY_GRPC_WEB_BODY,
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
});
const headerStatus = response.headers.get('grpc-status');
if (headerStatus !== null) {
if (!/^\d+$/.test(headerStatus.trim())) throw new Error('xAI billing returned malformed gRPC status');
const status = Number(headerStatus.trim());
if (!Number.isSafeInteger(status)) throw new Error('xAI billing returned malformed gRPC status');
if (status !== 0) throw new Error(`xAI billing RPC failed with status ${status}`);
}
if (!response.ok) throw new Error(`xAI billing request failed with HTTP ${response.status}`);
return parseUsage(new Uint8Array(await response.arrayBuffer()));
};
export const isConfigured = () => Boolean(readXaiAuth().entry);
export const fetchQuota = async () => {
const { entry, error: authError } = readXaiAuth();
if (authError) {
return buildResult({
providerId,
providerName,
ok: false,
configured: true,
error: authError
});
}
if (!entry) {
return buildResult({
providerId,
providerName,
ok: false,
configured: false,
error: 'Not configured'
});
}
try {
const freshEntry = await ensureFreshAccess(entry);
const accessToken = nonEmptyString(freshEntry.access);
if (!accessToken) throw new Error('xAI OAuth entry has no usable access token');
const usage = await fetchUsage(accessToken);
return buildResult({
providerId,
providerName,
ok: true,
configured: true,
usage: {
windows: {
billing_cycle: toUsageWindow({
usedPercent: usage.usedPercent,
windowSeconds: null,
resetAt: usage.resetAt
})
}
}
});
} catch (error) {
return buildResult({
providerId,
providerName,
ok: false,
configured: true,
error: error instanceof Error ? error.message : 'Request failed'
});
}
};