evOo-local/packages/core/src/model-oauth.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

48 lines
1.5 KiB
TypeScript

import { abortableDelay } from "./async.js";
export type ModelOAuthCompletion =
| { status: "pending" }
| { status: "ready" }
| { status: "error"; error: string };
type OAuthControllerRef = { current: AbortController | null };
export function cancelModelOAuthAttempt(ref: OAuthControllerRef, reset: () => void) {
const controller = ref.current;
controller?.abort();
if (ref.current !== controller) return;
ref.current = null;
reset();
}
export function finishModelOAuthAttempt(
ref: OAuthControllerRef,
controller: AbortController,
resetBusy: () => void,
) {
if (ref.current !== controller) return;
ref.current = null;
resetBusy();
}
export async function waitForModelOAuthCompletion(
complete: () => Promise<ModelOAuthCompletion>,
options: { signal?: AbortSignal; attempts?: number; pollIntervalMs?: number } = {},
): Promise<{ status: "ready" }> {
const attempts = options.attempts ?? 180;
const pollIntervalMs = options.pollIntervalMs ?? 5000;
for (let attempt = 1; attempt <= attempts; attempt += 1) {
throwIfAborted(options.signal);
const result = await complete();
throwIfAborted(options.signal);
if (result.status === "ready") return result;
if (result.status === "error") throw new Error(result.error);
if (attempt < attempts) await abortableDelay(pollIntervalMs, options.signal);
}
throw new Error("Sign-in timed out. Try again.");
}
function throwIfAborted(signal?: AbortSignal) {
if (signal?.aborted) throw signal.reason ?? new Error("OAuth polling cancelled");
}