Feat : Auto restart after crash
This commit is contained in:
@@ -169,7 +169,9 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
let lastError = null;
|
||||
let lastStatus = 0;
|
||||
const MAX_AUTO_RETRIES = 3;
|
||||
const MAX_RETRY_AFTER_RETRIES = 3;
|
||||
const retryAttemptsByUrl = {}; // Track retry attempts per URL
|
||||
const retryAfterAttemptsByUrl = {}; // Track Retry-After retries per URL
|
||||
|
||||
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
|
||||
const url = this.buildUrl(model, stream, urlIndex);
|
||||
@@ -177,10 +179,13 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
const sessionId = transformedBody.request?.sessionId;
|
||||
const headers = this.buildHeaders(credentials, stream, sessionId);
|
||||
|
||||
// Initialize retry counter for this URL
|
||||
// Initialize retry counters for this URL
|
||||
if (!retryAttemptsByUrl[urlIndex]) {
|
||||
retryAttemptsByUrl[urlIndex] = 0;
|
||||
}
|
||||
if (!retryAfterAttemptsByUrl[urlIndex]) {
|
||||
retryAfterAttemptsByUrl[urlIndex] = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await proxyAwareFetch(url, {
|
||||
@@ -206,8 +211,9 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
if (retryMs && retryMs <= MAX_RETRY_AFTER_MS) {
|
||||
log?.debug?.("RETRY", `${response.status} with Retry-After: ${Math.ceil(retryMs / 1000)}s, waiting...`);
|
||||
if (retryMs && retryMs <= MAX_RETRY_AFTER_MS && retryAfterAttemptsByUrl[urlIndex] < MAX_RETRY_AFTER_RETRIES) {
|
||||
retryAfterAttemptsByUrl[urlIndex]++;
|
||||
log?.debug?.("RETRY", `${response.status} with Retry-After: ${Math.ceil(retryMs / 1000)}s, waiting... (${retryAfterAttemptsByUrl[urlIndex]}/${MAX_RETRY_AFTER_RETRIES})`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryMs));
|
||||
urlIndex--;
|
||||
continue;
|
||||
|
||||
@@ -155,13 +155,30 @@ export class CursorExecutor extends BaseExecutor {
|
||||
throw new Error("http2 module not available");
|
||||
}
|
||||
|
||||
const HTTP2_TIMEOUT_MS = 60000; // 60s max — prevent hung sessions
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const urlObj = new URL(url);
|
||||
const client = http2.connect(`https://${urlObj.host}`);
|
||||
const chunks = [];
|
||||
let responseHeaders = {};
|
||||
let settled = false;
|
||||
|
||||
client.on("error", reject);
|
||||
// Ensure client is always closed on settle
|
||||
const finish = (fn) => (...args) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(hangTimeout);
|
||||
client.close();
|
||||
fn(...args);
|
||||
};
|
||||
|
||||
// Hard timeout: close session if server never responds
|
||||
const hangTimeout = setTimeout(finish(() => {
|
||||
reject(new Error("HTTP/2 request timed out"));
|
||||
}), HTTP2_TIMEOUT_MS);
|
||||
|
||||
client.on("error", finish(reject));
|
||||
|
||||
const req = client.request({
|
||||
":method": "POST",
|
||||
@@ -173,25 +190,18 @@ export class CursorExecutor extends BaseExecutor {
|
||||
|
||||
req.on("response", (hdrs) => { responseHeaders = hdrs; });
|
||||
req.on("data", (chunk) => { chunks.push(chunk); });
|
||||
req.on("end", () => {
|
||||
client.close();
|
||||
req.on("end", finish(() => {
|
||||
resolve({
|
||||
status: responseHeaders[":status"],
|
||||
headers: responseHeaders,
|
||||
body: Buffer.concat(chunks)
|
||||
});
|
||||
});
|
||||
req.on("error", (err) => {
|
||||
client.close();
|
||||
reject(err);
|
||||
});
|
||||
}));
|
||||
req.on("error", finish(reject));
|
||||
|
||||
if (signal) {
|
||||
signal.addEventListener("abort", () => {
|
||||
req.close();
|
||||
client.close();
|
||||
reject(new Error("Request aborted"));
|
||||
});
|
||||
const onAbort = finish(() => reject(new Error("Request aborted")));
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
}
|
||||
|
||||
req.write(body);
|
||||
|
||||
@@ -198,6 +198,9 @@ export class GithubExecutor extends BaseExecutor {
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.body) {
|
||||
return { response: new Response("", { status: response.status, headers: response.headers }), url, headers, transformedBody };
|
||||
}
|
||||
const convertedStream = response.body.pipeThrough(transformStream);
|
||||
|
||||
return {
|
||||
|
||||
@@ -345,6 +345,9 @@ export class KiroExecutor extends BaseExecutor {
|
||||
});
|
||||
|
||||
// Pipe response body through transform stream
|
||||
if (!response.body) {
|
||||
return new Response("data: [DONE]\n\n", { status: response.status, headers: { "Content-Type": "text/event-stream" } });
|
||||
}
|
||||
const transformedStream = response.body.pipeThrough(transformStream);
|
||||
|
||||
return new Response(transformedStream, {
|
||||
|
||||
Reference in New Issue
Block a user