源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
export const AGENT_CONTEXT_DIAGNOSTICS_RESPONSE_MAX_BYTES = 1024 * 1024;
|
||||
export const AGENT_CONTEXT_DIAGNOSTICS_REQUEST_TIMEOUT_MS = 30_000;
|
||||
|
||||
export class AgentContextDiagnosticsFetchError extends Error {
|
||||
constructor(code, message) {
|
||||
super(message);
|
||||
this.name = "AgentContextDiagnosticsFetchError";
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function timeoutError() {
|
||||
return new AgentContextDiagnosticsFetchError(
|
||||
"agent_context_diagnostics_request_timed_out",
|
||||
"Agent context diagnostics request timed out.",
|
||||
);
|
||||
}
|
||||
|
||||
function responseTooLargeError() {
|
||||
return new AgentContextDiagnosticsFetchError(
|
||||
"agent_context_diagnostics_response_too_large",
|
||||
"Agent context diagnostics response exceeded the 1 MiB limit.",
|
||||
);
|
||||
}
|
||||
|
||||
function cancelUnlockedBody(response) {
|
||||
if (!response.body || response.body.locked) return;
|
||||
void response.body.cancel().catch(() => undefined);
|
||||
}
|
||||
|
||||
async function readBoundedResponseText(response, signal) {
|
||||
const declaredLength = response.headers.get("content-length")?.trim() ?? "";
|
||||
if (/^\d+$/.test(declaredLength)) {
|
||||
const declaredBytes = Number(declaredLength);
|
||||
if (
|
||||
!Number.isSafeInteger(declaredBytes)
|
||||
|| declaredBytes > AGENT_CONTEXT_DIAGNOSTICS_RESPONSE_MAX_BYTES
|
||||
) {
|
||||
cancelUnlockedBody(response);
|
||||
throw responseTooLargeError();
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.body) return "";
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const chunks = [];
|
||||
let bytesRead = 0;
|
||||
const cancelOnAbort = () => {
|
||||
void reader.cancel().catch(() => undefined);
|
||||
};
|
||||
signal.addEventListener("abort", cancelOnAbort, { once: true });
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (signal.aborted) throw timeoutError();
|
||||
const chunk = await reader.read();
|
||||
if (chunk.done) break;
|
||||
bytesRead += chunk.value.byteLength;
|
||||
if (bytesRead > AGENT_CONTEXT_DIAGNOSTICS_RESPONSE_MAX_BYTES) {
|
||||
void reader.cancel().catch(() => undefined);
|
||||
throw responseTooLargeError();
|
||||
}
|
||||
chunks.push(chunk.value);
|
||||
}
|
||||
} catch (error) {
|
||||
if (signal.aborted) throw timeoutError();
|
||||
throw error;
|
||||
} finally {
|
||||
signal.removeEventListener("abort", cancelOnAbort);
|
||||
reader.releaseLock();
|
||||
}
|
||||
|
||||
const bytes = new Uint8Array(bytesRead);
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
bytes.set(chunk, offset);
|
||||
offset += chunk.byteLength;
|
||||
}
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
|
||||
function boundedDeadlineAt(requestedDeadlineAtMs) {
|
||||
const now = Date.now();
|
||||
const latestAllowedDeadline = now + AGENT_CONTEXT_DIAGNOSTICS_REQUEST_TIMEOUT_MS;
|
||||
const requested = Number(requestedDeadlineAtMs);
|
||||
if (!Number.isFinite(requested)) return now;
|
||||
return Math.min(requested, latestAllowedDeadline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Electron's IPC fetch must consume the body in the main process. Keep that
|
||||
* buffering under the renderer's absolute diagnostics deadline and fixed cap.
|
||||
*/
|
||||
export async function fetchAgentContextDiagnosticsResponse(
|
||||
fetchImpl,
|
||||
url,
|
||||
init,
|
||||
requestedDeadlineAtMs,
|
||||
) {
|
||||
const deadlineAtMs = boundedDeadlineAt(requestedDeadlineAtMs);
|
||||
const remainingMs = deadlineAtMs - Date.now();
|
||||
if (remainingMs <= 0) throw timeoutError();
|
||||
|
||||
const controller = new AbortController();
|
||||
let timeoutId = null;
|
||||
const deadline = new Promise((_, reject) => {
|
||||
timeoutId = setTimeout(() => {
|
||||
controller.abort();
|
||||
reject(timeoutError());
|
||||
}, remainingMs);
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await Promise.race([
|
||||
fetchImpl(url, { ...init, redirect: "error", signal: controller.signal }),
|
||||
deadline,
|
||||
]);
|
||||
const body = await Promise.race([
|
||||
readBoundedResponseText(response, controller.signal),
|
||||
deadline,
|
||||
]);
|
||||
return {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: Array.from(response.headers.entries()),
|
||||
body,
|
||||
};
|
||||
} catch (error) {
|
||||
if (controller.signal.aborted) throw timeoutError();
|
||||
throw error;
|
||||
} finally {
|
||||
if (timeoutId !== null) clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user