pi extensions update

This commit is contained in:
Jonas H
2026-03-24 09:10:04 +01:00
parent 0b11a0d315
commit 677f5d8ca5
4 changed files with 666 additions and 12 deletions

View File

@@ -293,6 +293,7 @@ export default function (pi: ExtensionAPI) {
let pollInFlight: Promise<void> | null = null;
let pollQueued = false;
let pollStartedAt = 0;
let streamingTimer: ReturnType<typeof setInterval> | null = null;
let ctx: any = null;
@@ -309,6 +310,7 @@ export default function (pi: ExtensionAPI) {
session: data.session,
weekly: data.weekly,
sessionResetsIn: data.sessionResetsIn,
sessionResetsAt: data.sessionResetsAt,
weeklyResetsIn: data.weeklyResetsIn,
});
}
@@ -364,7 +366,7 @@ export default function (pi: ExtensionAPI) {
// ---------------------------------------------------------------------------
// Polling
// ---------------------------------------------------------------------------
async function runPoll(options: PollOptions = {}) {
async function runPollInner(options: PollOptions = {}) {
const auth = readAuth();
const active = state.activeProvider;
@@ -378,7 +380,14 @@ export default function (pi: ExtensionAPI) {
const blockedUntil = cache?.rateLimitedUntil?.[active] ?? 0;
if (now < blockedUntil) {
if (cache?.data?.[active]) state[active] = cache.data[active]!;
if (cache?.data?.[active]) {
state[active] = cache.data[active]!;
} else {
// Rate-limited but no cached data — show a meaningful status instead
// of leaving state null (which shows eternal "loading…").
const retryMin = Math.ceil((blockedUntil - now) / 60000);
state[active] = { session: 0, weekly: 0, error: `rate limited (retry in ${retryMin}m)` };
}
state.lastPoll = now; updateStatus(); return;
}
@@ -396,7 +405,11 @@ export default function (pi: ExtensionAPI) {
const tokenExpiredOrMissing = !creds?.access || (expires > 0 && Date.now() + 60_000 >= expires);
if (tokenExpiredOrMissing && creds?.refresh) {
try {
const refreshed = await ensureFreshAuthForProviders([oauthId as OAuthProviderId], { auth, persist: true });
const refreshPromise = ensureFreshAuthForProviders([oauthId as OAuthProviderId], { auth, persist: true });
const timeoutPromise = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("OAuth refresh timeout")), 15_000),
);
const refreshed = await Promise.race([refreshPromise, timeoutPromise]);
if (refreshed.auth) effectiveAuth = refreshed.auth;
} catch {}
}
@@ -452,11 +465,40 @@ export default function (pi: ExtensionAPI) {
updateStatus();
}
async function runPoll(options: PollOptions = {}): Promise<void> {
const timeout = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("runPoll timeout")), 25_000),
);
await Promise.race([runPollInner(options), timeout]);
}
const POLL_TIMEOUT_MS = 30_000;
async function poll(options: PollOptions = {}) {
// If a previous poll has been running longer than POLL_TIMEOUT_MS, abandon it
// so we don't queue forever behind a stuck request.
if (pollInFlight && pollStartedAt > 0 && Date.now() - pollStartedAt > POLL_TIMEOUT_MS) {
pollInFlight = null;
pollQueued = false;
const active = state.activeProvider;
if (active && !state[active]) {
state[active] = { session: 0, weekly: 0, error: "poll timeout" };
updateStatus();
}
}
if (pollInFlight) { pollQueued = true; await pollInFlight; return; }
do {
pollQueued = false;
pollInFlight = runPoll(options).catch(() => {}).finally(() => { pollInFlight = null; });
pollStartedAt = Date.now();
pollInFlight = runPoll(options).catch(() => {
// If runPoll threw, ensure we don't leave status stuck at "loading…"
const active = state.activeProvider;
if (active && !state[active]) {
state[active] = { session: 0, weekly: 0, error: "poll failed" };
updateStatus();
}
}).finally(() => { pollInFlight = null; pollStartedAt = 0; });
await pollInFlight;
} while (pollQueued);
}