mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* Classify Codex app-server chatgpt-auth-required as an auth error When auth.json holds only an OPENAI_API_KEY (no ChatGPT tokens), the app-server RPC rejects account/rateLimits/read with "chatgpt authentication required to read rate limits". That string matched none of CODEX_AUTH_ERROR_PATTERNS, so fetchCodexRateLimits fell through to the hidden PTY /status probe, which cannot render usage for such accounts and burned the full 15s PTY timeout on every refresh cycle, surfacing as a permanent "Refresh failed — PTY timeout" status chip. Classify the message as an auth error so the RPC result is returned directly (fast, accurate) and no PTY is spawned. * Keep auth-required usage errors from rendering as a rate-limit Limited label The new Codex app-server error 'chatgpt authentication required to read rate limits' mentions rate limits only as the object it failed to read, but the status bar's rate-limit classifier matched the phrase and labeled the chip 'Limited'. Classify authentication-required messages as auth failures so they get the standard softened refresh copy instead.
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
const CODEX_AUTH_ERROR_PATTERNS = [
|
|
/access token could not be refreshed/i,
|
|
/authentication session could not be refreshed/i,
|
|
/refresh token (?:has expired|was already used|was revoked)/i,
|
|
/you have since logged out or signed in to another account/i,
|
|
/please (?:log out and )?sign in again/i,
|
|
/please reauthenticate/i,
|
|
/not logged in/i,
|
|
/token data is not available/i,
|
|
/auth (?:is missing|tokens are missing|does not expose)/i,
|
|
// Why: app-server rejects account/rateLimits/read with this when auth.json
|
|
// holds only an API key; without classification the fetcher falls through to
|
|
// a hidden PTY probe that can only time out (15s) on every refresh.
|
|
/chatgpt authentication required/i
|
|
]
|
|
const ANSI_ESCAPE_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*[a-zA-Z]`, 'g')
|
|
|
|
export function isCodexAuthError(error: string | null | undefined): boolean {
|
|
const message = error?.trim()
|
|
if (!message) {
|
|
return false
|
|
}
|
|
return CODEX_AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(message))
|
|
}
|
|
|
|
export function extractCodexAuthError(output: string | null | undefined): string | null {
|
|
if (!output) {
|
|
return null
|
|
}
|
|
|
|
let cleanPrefix = ''
|
|
for (const rawLine of iterateCodexOutputLines(output)) {
|
|
const line = rawLine.replace(ANSI_ESCAPE_RE, '').trim()
|
|
if (!line) {
|
|
continue
|
|
}
|
|
if (isCodexAuthError(line)) {
|
|
return line.slice(0, 4_000)
|
|
}
|
|
if (cleanPrefix.length < 4_000) {
|
|
cleanPrefix = cleanPrefix ? `${cleanPrefix}\n${line}` : line
|
|
cleanPrefix = cleanPrefix.slice(0, 4_000)
|
|
}
|
|
}
|
|
|
|
return isCodexAuthError(cleanPrefix) ? cleanPrefix : null
|
|
}
|
|
|
|
function* iterateCodexOutputLines(output: string): Generator<string> {
|
|
let lineStart = 0
|
|
|
|
for (let index = 0; index < output.length; index++) {
|
|
const code = output.charCodeAt(index)
|
|
if (code !== 10 && code !== 13) {
|
|
continue
|
|
}
|
|
|
|
yield output.slice(lineStart, index)
|
|
if (code === 13 && output.charCodeAt(index + 1) === 10) {
|
|
index++
|
|
}
|
|
lineStart = index + 1
|
|
}
|
|
|
|
if (lineStart <= output.length) {
|
|
yield output.slice(lineStart)
|
|
}
|
|
}
|