c0f6f9ef2a
* Add `BrowserTool` * add some logging * go with npm install -g * remove dep changes since the switch to npm install -g * tweak * tweak * tweak * tweak * tweak timeout * tweak * remove logs * skill investigation doc * wip * wip * tweak * lock agent-browser version * tweak * logs * logs * more logs * more debug stuff * try this * try this * try this * fix PATH * try this * tweak * tweak * tweak * update wiki entries * update wiki once again * lint fix --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
48 lines
1.3 KiB
TypeScript
48 lines
1.3 KiB
TypeScript
import { setTimeout as sleep } from "node:timers/promises";
|
|
import { log } from "./cli.ts";
|
|
|
|
export type RetryOptions = {
|
|
maxAttempts?: number;
|
|
delayMs?: number;
|
|
shouldRetry?: (error: unknown) => boolean;
|
|
label?: string;
|
|
};
|
|
|
|
const defaultShouldRetry = (error: unknown): boolean => {
|
|
if (!(error instanceof Error)) return false;
|
|
// retry on transient network errors
|
|
return (
|
|
error.name === "AbortError" ||
|
|
error.message.includes("fetch failed") ||
|
|
error.message.includes("ECONNRESET") ||
|
|
error.message.includes("ETIMEDOUT")
|
|
);
|
|
};
|
|
|
|
export async function retry<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {
|
|
const maxAttempts = options.maxAttempts ?? 3;
|
|
const delayMs = options.delayMs ?? 1000;
|
|
const shouldRetry = options.shouldRetry ?? defaultShouldRetry;
|
|
const label = options.label ?? "operation";
|
|
|
|
let lastError: unknown;
|
|
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
try {
|
|
return await fn();
|
|
} catch (error) {
|
|
lastError = error;
|
|
|
|
if (attempt === maxAttempts || !shouldRetry(error)) {
|
|
throw error;
|
|
}
|
|
|
|
const delay = delayMs * attempt;
|
|
log.info(`» ${label} failed (attempt ${attempt}/${maxAttempts}), retrying in ${delay}ms...`);
|
|
await sleep(delay);
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|