mirror of
https://github.com/stablyai/orca.git
synced 2026-10-13 16:02:49 +00:00
* fix(rate-limits): stop driving a hidden Codex TUI to read usage When the headless Codex usage call failed, Orca opened a hidden interactive Codex, typed /status and pressed Enter without reading the screen, then killed it after 15 s. If Codex showed its "Update available" prompt, that Enter picked "Update now", Codex started its installer, and the 15 s kill interrupted it, leaving the global install broken. Drop the hidden-terminal fallback. When the headless call fails with a non-sign-in error, read the same usage from the HTTP endpoint Orca already calls on WSL and for the 5-hour window, so a usage check can no longer answer any Codex startup screen. Fixes #17415 * test(rate-limits): pin the RPC error when the Codex HTTP fallback fails Also drop comments that still described the removed hidden-terminal fallback. * test(rate-limits): use real Response objects in Codex fetcher tests The changed-code gate rejects the new type assertions this PR added.
75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
/** Carries the sign-in link of an in-flight `codex login` from main to every window. */
|
|
export const CODEX_PENDING_LOGIN_URL_CHANGED_CHANNEL = 'codexAccounts:pendingLoginUrlChanged'
|
|
|
|
/** The rejection a cancelled `codex login` produces; the Accounts pane reads it to keep a cancellation out of the error toast. */
|
|
export const CODEX_LOGIN_CANCELLED_MESSAGE = 'Codex sign-in was cancelled.'
|
|
|
|
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,
|
|
/sign in with chatgpt/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; that home has no ChatGPT token, so a fallback read cannot succeed.
|
|
/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)
|
|
}
|
|
}
|