by codex: feat. General Improvements
This commit is contained in:
+76
-2
@@ -19,6 +19,78 @@ function buildHeaders(auth: ClaudeAuth): Record<string, string> {
|
||||
};
|
||||
}
|
||||
|
||||
function isClaudeWindow(value: unknown): boolean {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
const window = value as Record<string, unknown>;
|
||||
return (
|
||||
typeof window.utilization === "number" &&
|
||||
Number.isFinite(window.utilization) &&
|
||||
(window.resets_at === undefined ||
|
||||
window.resets_at === null ||
|
||||
typeof window.resets_at === "string")
|
||||
);
|
||||
}
|
||||
|
||||
export function parseClaudeOrgsResponse(data: unknown): ClaudeOrg[] {
|
||||
if (
|
||||
!Array.isArray(data) ||
|
||||
data.some(
|
||||
(org) =>
|
||||
!org ||
|
||||
typeof org !== "object" ||
|
||||
typeof (org as Record<string, unknown>).uuid !== "string" ||
|
||||
typeof (org as Record<string, unknown>).name !== "string"
|
||||
)
|
||||
) {
|
||||
throw new Error("INVALID_CLAUDE_ORGS_RESPONSE");
|
||||
}
|
||||
return data.map((org) => {
|
||||
const value = org as Record<string, unknown>;
|
||||
return { uuid: value.uuid as string, name: value.name as string };
|
||||
});
|
||||
}
|
||||
|
||||
export function parseClaudeUsageResponse(data: unknown): ClaudeUsageResponse {
|
||||
if (!data || typeof data !== "object") {
|
||||
throw new Error("INVALID_CLAUDE_RESPONSE");
|
||||
}
|
||||
|
||||
const value = data as Record<string, unknown>;
|
||||
if (
|
||||
!isClaudeWindow(value.five_hour) ||
|
||||
!isClaudeWindow(value.seven_day) ||
|
||||
(value.seven_day_sonnet !== undefined &&
|
||||
value.seven_day_sonnet !== null &&
|
||||
!isClaudeWindow(value.seven_day_sonnet)) ||
|
||||
(value.seven_day_opus !== undefined &&
|
||||
value.seven_day_opus !== null &&
|
||||
!isClaudeWindow(value.seven_day_opus))
|
||||
) {
|
||||
throw new Error("INVALID_CLAUDE_RESPONSE");
|
||||
}
|
||||
|
||||
const toWindow = (window: unknown) => {
|
||||
const item = window as Record<string, unknown>;
|
||||
return {
|
||||
utilization: item.utilization as number,
|
||||
...(typeof item.resets_at === "string"
|
||||
? { resets_at: item.resets_at }
|
||||
: {}),
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
five_hour: toWindow(value.five_hour),
|
||||
seven_day: toWindow(value.seven_day),
|
||||
...(value.seven_day_sonnet
|
||||
? { seven_day_sonnet: toWindow(value.seven_day_sonnet) }
|
||||
: {}),
|
||||
...(value.seven_day_opus
|
||||
? { seven_day_opus: toWindow(value.seven_day_opus) }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchClaudeOrgs(auth: ClaudeAuth): Promise<ClaudeOrg[]> {
|
||||
const response = await fetch(`${BASE_URL}/organizations`, {
|
||||
headers: buildHeaders(auth),
|
||||
@@ -31,7 +103,8 @@ export async function fetchClaudeOrgs(auth: ClaudeAuth): Promise<ClaudeOrg[]> {
|
||||
throw new Error(`HTTP_ERROR_${response.status}`);
|
||||
}
|
||||
|
||||
return response.json() as Promise<ClaudeOrg[]>;
|
||||
const data: unknown = await response.json();
|
||||
return parseClaudeOrgsResponse(data);
|
||||
}
|
||||
|
||||
export async function fetchClaudeUsage(
|
||||
@@ -49,5 +122,6 @@ export async function fetchClaudeUsage(
|
||||
throw new Error(`HTTP_ERROR_${response.status}`);
|
||||
}
|
||||
|
||||
return response.json() as Promise<ClaudeUsageResponse>;
|
||||
const data: unknown = await response.json();
|
||||
return parseClaudeUsageResponse(data);
|
||||
}
|
||||
|
||||
+60
-1
@@ -2,6 +2,64 @@ import type { CodexAuth, CodexUsageResponse } from "@/types/codex";
|
||||
|
||||
const USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
||||
|
||||
function isNumber(value: unknown): value is number {
|
||||
return typeof value === "number" && Number.isFinite(value);
|
||||
}
|
||||
|
||||
export function parseCodexUsageResponse(data: unknown): CodexUsageResponse {
|
||||
if (!data || typeof data !== "object") {
|
||||
throw new Error("INVALID_CODEX_RESPONSE");
|
||||
}
|
||||
|
||||
const value = data as Record<string, unknown>;
|
||||
const rateLimit = value.rate_limit as Record<string, unknown> | undefined;
|
||||
const primary = rateLimit?.primary_window as Record<string, unknown> | undefined;
|
||||
const secondary = rateLimit?.secondary_window as Record<string, unknown> | undefined;
|
||||
const credits = value.credits as Record<string, unknown> | undefined;
|
||||
|
||||
const validWindow = (window: Record<string, unknown> | undefined) =>
|
||||
!!window &&
|
||||
isNumber(window.used_percent) &&
|
||||
isNumber(window.reset_at) &&
|
||||
isNumber(window.limit_window_seconds);
|
||||
|
||||
if (
|
||||
typeof value.plan_type !== "string" ||
|
||||
!validWindow(primary) ||
|
||||
!validWindow(secondary) ||
|
||||
!credits ||
|
||||
typeof credits.has_credits !== "boolean" ||
|
||||
typeof credits.unlimited !== "boolean" ||
|
||||
!isNumber(credits.balance)
|
||||
) {
|
||||
throw new Error("INVALID_CODEX_RESPONSE");
|
||||
}
|
||||
|
||||
const primaryWindow = primary as Record<string, unknown>;
|
||||
const secondaryWindow = secondary as Record<string, unknown>;
|
||||
|
||||
return {
|
||||
plan_type: value.plan_type,
|
||||
rate_limit: {
|
||||
primary_window: {
|
||||
used_percent: primaryWindow.used_percent as number,
|
||||
reset_at: primaryWindow.reset_at as number,
|
||||
limit_window_seconds: primaryWindow.limit_window_seconds as number,
|
||||
},
|
||||
secondary_window: {
|
||||
used_percent: secondaryWindow.used_percent as number,
|
||||
reset_at: secondaryWindow.reset_at as number,
|
||||
limit_window_seconds: secondaryWindow.limit_window_seconds as number,
|
||||
},
|
||||
},
|
||||
credits: {
|
||||
has_credits: credits.has_credits,
|
||||
unlimited: credits.unlimited,
|
||||
balance: credits.balance,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchCodexUsage(auth: CodexAuth): Promise<CodexUsageResponse> {
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${auth.accessToken}`,
|
||||
@@ -22,5 +80,6 @@ export async function fetchCodexUsage(auth: CodexAuth): Promise<CodexUsageRespon
|
||||
throw new Error(`HTTP_ERROR_${response.status}`);
|
||||
}
|
||||
|
||||
return response.json() as Promise<CodexUsageResponse>;
|
||||
const data: unknown = await response.json();
|
||||
return parseCodexUsageResponse(data);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user