| import { randomUUID } from 'crypto'; |
| import tokenManager from '../auth/token_manager.js'; |
| import config from '../config/config.js'; |
| import fingerprintRequester from '../requester.js'; |
| import { saveBase64Image } from '../utils/imageStorage.js'; |
| import logger from '../utils/logger.js'; |
| import memoryManager from '../utils/memoryManager.js'; |
| import { httpRequest, httpStreamRequest } from '../utils/httpClient.js'; |
| import { generateTrajectorybody } from '../utils/trajectory.js'; |
| import { buildRecordCodeAssistMetricsBody } from '../utils/recordCodeAssistMetrics.js'; |
| import { createTelemetryBatch, serializeTelemetryBatch } from "../utils/createTelemetry.js" |
| import { createLog1, createLog2 } from "../utils/additionalLogs.js" |
| import { buildClientRegister, buildFrontEnd, buildClientFeatrueHeaders, buildClientRegisterHeaders, buildFrontEndHeaders } from "../utils/unleash.js" |
| import { MODEL_LIST_CACHE_TTL, QA_PAIRS } from '../constants/index.js'; |
| import { createApiError } from '../utils/errors.js'; |
| import { generateCheckpointBody } from '../utils/checkPoint.js'; |
| import path from 'path'; |
| import { fileURLToPath } from 'url'; |
| import { |
| convertToToolCall, |
| registerStreamMemoryCleanup |
| } from './stream_parser.js'; |
| import { setSignature, shouldCacheSignature, isImageModel } from '../utils/thoughtSignatureCache.js'; |
| import { |
| isDebugDumpEnabled, |
| createDumpId, |
| createStreamCollector, |
| collectStreamChunk, |
| dumpFinalRequest, |
| dumpStreamResponse, |
| dumpFinalRawResponse |
| } from './debugDump.js'; |
| import { getUpstreamStatus, readUpstreamErrorBody, isCallerDoesNotHavePermission } from './upstreamError.js'; |
| import { createStreamLineProcessor } from './streamLineProcessor.js'; |
| import { runAxiosSseStream, runNativeSseStream, postJsonAndParse } from './geminiTransport.js'; |
| import { parseGeminiCandidateParts, toOpenAIUsage } from './geminiResponseParser.js'; |
| import axios from 'axios'; |
|
|
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
|
|
| |
| const tokenTimers = new Map(); |
| const TOKEN_TIMEOUT = 3 * 60 * 1000; |
| const BACKEND_CALL_INTERVAL = 60 * 1000; |
| const checkPointList = new Set([]); |
|
|
| function getTokenKey(token) { |
| return token.access_token; |
| } |
|
|
| function startTokenTimer(token) { |
| const key = getTokenKey(token); |
| const now = Date.now(); |
|
|
| if (tokenTimers.has(key)) { |
| tokenTimers.get(key).lastUsed = now; |
| return; |
| } |
| sendClientRegister(token).catch(err => logger.warn('定时调用ClientRegister失败:', err.message)); |
| sendClientFeature(token).catch(err => logger.warn('定时调用ClientFeature失败:', err.message)); |
| sendFrontEnd(token).catch(err => logger.warn('定时调用FrontEnd失败:', err.message)); |
|
|
| const intervalId = setInterval(() => { |
| sendClientRegister(token).catch(err => logger.warn('定时调用ClientRegister失败:', err.message)); |
| sendClientFeature(token).catch(err => logger.warn('定时调用ClientFeature失败:', err.message)); |
| sendFrontEnd(token).catch(err => logger.warn('定时调用FrontEnd失败:', err.message)); |
| }, BACKEND_CALL_INTERVAL); |
|
|
| tokenTimers.set(key, { lastUsed: now, intervalId }); |
| } |
|
|
| function checkTokenTimeout() { |
| const now = Date.now(); |
| for (const [key, data] of tokenTimers.entries()) { |
| if (now - data.lastUsed > TOKEN_TIMEOUT) { |
| clearInterval(data.intervalId); |
| tokenTimers.delete(key); |
| } |
| } |
| } |
|
|
| setInterval(checkTokenTimeout, 30 * 1000); |
|
|
| |
| let requester = null; |
| let useAxios = false; |
|
|
| |
| if (config.useNativeAxios === true) { |
| useAxios = true; |
| logger.info('使用原生 axios 请求'); |
| } else { |
| try { |
| |
| |
| const isPkg = typeof process.pkg !== 'undefined'; |
|
|
| |
| const configPath = isPkg |
| ? path.join(path.dirname(process.execPath), 'bin', 'tls_config.json') |
| : path.join(__dirname, '..', 'bin', 'tls_config.json'); |
| requester = fingerprintRequester.create({ |
| configPath, |
| timeout: config.timeout ? Math.ceil(config.timeout / 1000) : 30, |
| proxy: config.proxy || null, |
| }); |
| logger.info('使用 FingerprintRequester 请求'); |
| } catch (error) { |
| logger.warn('FingerprintRequester 初始化失败,自动降级使用 axios:', error.message); |
| useAxios = true; |
| } |
| } |
|
|
| |
|
|
| |
| const getModelCacheTTL = () => { |
| return config.cache?.modelListTTL || MODEL_LIST_CACHE_TTL; |
| }; |
|
|
| let modelListCache = null; |
| let modelListCacheTime = 0; |
|
|
| |
| |
| const DEFAULT_MODELS = Object.freeze([ |
| 'claude-opus-4-6', |
| 'claude-opus-4-6-thinking', |
| 'claude-sonnet-4-6', |
| 'claude-sonnet-4-6-thinking', |
| 'gemini-3.1-pro-high', |
| 'gemini-2.5-flash-lite', |
| 'gemini-3.1-flash-image', |
| 'gemini-3.1-flash-image-4K', |
| 'gemini-3.1-flash-image-2K', |
| 'gemini-2.5-flash-thinking', |
| 'gemini-2.5-pro', |
| 'gemini-2.5-flash', |
| 'gemini-3.1-pro-low', |
| 'chat_20706', |
| 'rev19-uic3-1p', |
| 'gpt-oss-120b-medium', |
| 'chat_23310' |
| ]); |
|
|
| |
| function getDefaultModelList() { |
| const created = Math.floor(Date.now() / 1000); |
| return { |
| object: 'list', |
| data: DEFAULT_MODELS.map(id => ({ |
| id, |
| object: 'model', |
| created, |
| owned_by: 'google' |
| })) |
| }; |
| } |
|
|
|
|
| |
| function registerMemoryCleanup() { |
| |
| registerStreamMemoryCleanup(); |
|
|
| |
| memoryManager.registerCleanup(() => { |
| const ttl = getModelCacheTTL(); |
| const now = Date.now(); |
| if (modelListCache && (now - modelListCacheTime) > ttl) { |
| modelListCache = null; |
| modelListCacheTime = 0; |
| } |
| }); |
| } |
|
|
| |
| registerMemoryCleanup(); |
|
|
| |
|
|
| function buildHeaders(token) { |
| return { |
| 'Host': config.api.host, |
| 'User-Agent': config.api.userAgent, |
| 'Authorization': `Bearer ${token.access_token}`, |
| 'Content-Type': 'application/json', |
| 'Accept-Encoding': 'gzip' |
| }; |
| } |
|
|
| function buildRequesterConfig(headers, body = null, method = "POST") { |
| const reqConfig = { |
| method: method, |
| headers, |
| timeout_ms: config.timeout, |
| proxy: config.proxy |
| }; |
| if (body !== null) { |
| |
| if (Buffer.isBuffer(body) || body instanceof Uint8Array) { |
| reqConfig.body = body; |
| } else { |
| reqConfig.body = JSON.stringify(body); |
| } |
| } |
| return reqConfig; |
| } |
|
|
|
|
| |
| async function handleApiError(error, token, dumpId = null) { |
| const status = getUpstreamStatus(error); |
| const errorBody = await readUpstreamErrorBody(error); |
|
|
| if (dumpId) { |
| await dumpFinalRawResponse(dumpId, String(errorBody ?? '')); |
| } |
|
|
| if (status === 403) { |
| if (isCallerDoesNotHavePermission(errorBody)) { |
| throw createApiError(`超出模型最大上下文。错误详情: ${errorBody}`, status, errorBody); |
| } |
| tokenManager.disableCurrentToken(token); |
| throw createApiError(`该账号没有使用权限,已自动禁用。错误详情: ${errorBody}`, status, errorBody); |
| } |
|
|
| throw createApiError(`API请求失败 (${status}): ${errorBody}`, status, errorBody); |
| } |
|
|
|
|
| |
|
|
| export async function generateAssistantResponse(requestBody, token, callback) { |
| startTokenTimer(token); |
| const trajectoryId = requestBody.requestId.split('/')[2]; |
| const conversationId = randomUUID(); |
| const messageId = randomUUID(); |
| const modelName = requestBody.model; |
| const headers = buildHeaders(token); |
| const dumpId = isDebugDumpEnabled() ? createDumpId('stream') : null; |
| const streamCollector = dumpId ? createStreamCollector() : null; |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(requestBody))); |
| let num = Math.floor(Math.random() * QA_PAIRS.length); |
| if (dumpId) { |
| await dumpFinalRequest(dumpId, requestBody); |
| } |
|
|
| |
| const state = { |
| toolCalls: [], |
| reasoningSignature: null, |
| sessionId: requestBody.request?.sessionId, |
| model: requestBody.model |
| }; |
| const processor = createStreamLineProcessor({ |
| state, |
| onEvent: callback, |
| onRawChunk: (chunk) => collectStreamChunk(streamCollector, chunk) |
| }); |
|
|
| try { |
| if (useAxios) { |
| await runAxiosSseStream({ |
| url: config.api.url, |
| headers, |
| data: requestBody, |
| timeout: config.timeout, |
| processor |
| }); |
| } else { |
| const streamResponse = requester.antigravity_fetchStream(config.api.url, buildRequesterConfig(headers, requestBody)); |
| await runNativeSseStream({ |
| streamResponse, |
| processor, |
| onErrorChunk: (chunk) => collectStreamChunk(streamCollector, chunk) |
| }); |
| } |
|
|
| |
| if (dumpId) { |
| await dumpStreamResponse(dumpId, streamCollector); |
| } |
| sendRecordCodeAssistMetrics(token, trajectoryId).catch(err => logger.warn('发送RecordCodeAssistMetrics失败:', err.message)); |
| sendRecordTrajectoryAnalytics(token, num, trajectoryId,messageId,conversationId, modelName).catch(err => logger.warn('发送轨迹分析失败:', err.message)); |
| sendLog(token,num,trajectoryId,conversationId,messageId).catch(err => logger.warn('发送log失败:', err.message)); |
| sendCheckPoint(token).catch(err => logger.warn('发送checkPoint失败:', err.message));; |
| } catch (error) { |
| try { processor.close(); } catch { } |
| await handleApiError(error, token, dumpId); |
| } |
| } |
|
|
| |
| async function fetchRawModels(headers, token) { |
| try { |
| if (useAxios) { |
| const response = await httpRequest({ |
| method: 'POST', |
| url: config.api.modelsUrl, |
| headers, |
| data: {} |
| }); |
| return response.data; |
| } |
| const response = await requester.antigravity_fetch(config.api.modelsUrl, buildRequesterConfig(headers, {})); |
| if (response.status !== 200) { |
| const errorBody = await response.text(); |
| throw { status: response.status, message: errorBody }; |
| } |
| return await response.json(); |
| } catch (error) { |
| await handleApiError(error, token); |
| } |
| } |
|
|
| export async function getAvailableModels() { |
| |
| const now = Date.now(); |
| const ttl = getModelCacheTTL(); |
| if (modelListCache && (now - modelListCacheTime) < ttl) { |
| return modelListCache; |
| } |
|
|
| const token = await tokenManager.getToken(); |
| if (!token) { |
| |
| logger.warn('没有可用的 token,返回默认模型列表'); |
| return getDefaultModelList(); |
| } |
|
|
| const headers = buildHeaders(token); |
| const data = await fetchRawModels(headers, token); |
| if (!data) { |
| |
| return getDefaultModelList(); |
| } |
|
|
| const created = Math.floor(Date.now() / 1000); |
| const modelList = Object.keys(data.models || {}).map(id => ({ |
| id, |
| object: 'model', |
| created, |
| owned_by: 'google' |
| })); |
|
|
| |
| const existingIds = new Set(modelList.map(m => m.id)); |
| for (const defaultModel of DEFAULT_MODELS) { |
| if (!existingIds.has(defaultModel)) { |
| modelList.push({ |
| id: defaultModel, |
| object: 'model', |
| created, |
| owned_by: 'google' |
| }); |
| } |
| } |
|
|
| const result = { |
| object: 'list', |
| data: modelList |
| }; |
|
|
| |
| modelListCache = result; |
| modelListCacheTime = now; |
| const currentTTL = getModelCacheTTL(); |
| logger.info(`模型列表已缓存 (有效期: ${currentTTL / 1000}秒, 模型数量: ${modelList.length})`); |
|
|
| return result; |
| } |
|
|
| |
| export function clearModelListCache() { |
| modelListCache = null; |
| modelListCacheTime = 0; |
| logger.info('模型列表缓存已清除'); |
| } |
|
|
| export async function getModelsWithQuotas(token) { |
| const headers = buildHeaders(token); |
| const data = await fetchRawModels(headers, token); |
| if (!data) return {}; |
|
|
| const quotas = {}; |
| Object.entries(data.models || {}).forEach(([modelId, modelData]) => { |
| if (modelData.quotaInfo) { |
| quotas[modelId] = { |
| r: modelData.quotaInfo.remainingFraction, |
| t: modelData.quotaInfo.resetTime |
| }; |
| } |
| }); |
|
|
| return quotas; |
| } |
|
|
| export async function generateAssistantResponseNoStream(requestBody, token) { |
| startTokenTimer(token); |
| const trajectoryId = requestBody.requestId.split('/')[2]; |
| const conversationId = randomUUID(); |
| const messageId = randomUUID(); |
| const modelName = requestBody.model; |
| const headers = buildHeaders(token); |
| const dumpId = isDebugDumpEnabled() ? createDumpId('no_stream') : null; |
| let num = Math.floor(Math.random() * QA_PAIRS.length); |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(requestBody))); |
|
|
| if (dumpId) await dumpFinalRequest(dumpId, requestBody); |
| let data; |
| try { |
| data = await postJsonAndParse({ |
| useAxios, |
| requester, |
| url: config.api.noStreamUrl, |
| headers, |
| body: requestBody, |
| timeout: config.timeout, |
| requesterConfig: buildRequesterConfig(headers, requestBody), |
| dumpId, |
| dumpFinalRawResponse, |
| rawFormat: 'json' |
| }); |
| sendRecordCodeAssistMetrics(token, trajectoryId).catch(err => logger.warn('发送RecordCodeAssistMetrics失败:', err.message)); |
| sendRecordTrajectoryAnalytics(token, num, trajectoryId,messageId,conversationId, modelName).catch(err => logger.warn('发送轨迹分析失败:', err.message)); |
| sendLog(token,num,trajectoryId,conversationId,messageId).catch(err => logger.warn('发送log失败:', err.message)); |
| } catch (error) { |
| await handleApiError(error, token, dumpId); |
| } |
| |
| const parts = data.response?.candidates?.[0]?.content?.parts || []; |
| const parsed = parseGeminiCandidateParts({ |
| parts, |
| sessionId: requestBody.request?.sessionId, |
| model: requestBody.model, |
| convertToToolCall, |
| saveBase64Image |
| }); |
|
|
| const usageData = toOpenAIUsage(data.response?.usageMetadata); |
|
|
| |
| const sessionId = requestBody.request?.sessionId; |
| const model = requestBody.model; |
| const hasTools = parsed.toolCalls.length > 0; |
| const isImage = isImageModel(model); |
|
|
| |
| if (sessionId && model && shouldCacheSignature({ hasTools, isImageModel: isImage })) { |
| |
| let finalSignature = parsed.reasoningSignature; |
|
|
| |
| if (hasTools) { |
| for (let i = parsed.toolCalls.length - 1; i >= 0; i--) { |
| const sig = parsed.toolCalls[i]?.thoughtSignature; |
| if (sig) { |
| finalSignature = sig; |
| break; |
| } |
| } |
| } |
|
|
| if (finalSignature) { |
| const cachedContent = parsed.reasoningContent || ' '; |
| setSignature(sessionId, model, finalSignature, cachedContent, { hasTools, isImageModel: isImage }); |
| } |
| } |
|
|
| |
| if (parsed.imageUrls.length > 0) { |
| let markdown = parsed.content ? parsed.content + '\n\n' : ''; |
| markdown += parsed.imageUrls.map(url => ``).join('\n\n'); |
| return { content: markdown, reasoningContent: parsed.reasoningContent, reasoningSignature: parsed.reasoningSignature, toolCalls: parsed.toolCalls, usage: usageData }; |
| } |
|
|
| return { content: parsed.content, reasoningContent: parsed.reasoningContent, reasoningSignature: parsed.reasoningSignature, toolCalls: parsed.toolCalls, usage: usageData }; |
| } |
|
|
| export async function generateImageForSD(requestBody, token) { |
| startTokenTimer(token); |
| const trajectoryId = requestBody.requestId.split('/')[2]; |
| const conversationId = randomUUID(); |
| const messageId = randomUUID(); |
| const modelName = requestBody.model; |
| const headers = buildHeaders(token); |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(requestBody),'utf-8')); |
| let data; |
| let num = Math.floor(Math.random() * QA_PAIRS.length); |
|
|
| |
|
|
| try { |
| if (useAxios) { |
| data = (await httpRequest({ |
| method: 'POST', |
| url: config.api.noStreamUrl, |
| headers, |
| data: requestBody |
| })).data; |
| } else { |
| const response = await requester.antigravity_fetch(config.api.noStreamUrl, buildRequesterConfig(headers, requestBody)); |
| if (response.status !== 200) { |
| const errorBody = await response.text(); |
| throw { status: response.status, message: errorBody }; |
| } |
| data = await response.json(); |
| } |
| } catch (error) { |
| await handleApiError(error, token); |
| } |
| sendRecordCodeAssistMetrics(token, trajectoryId).catch(err => logger.warn('发送RecordCodeAssistMetrics失败:', err.message)); |
| sendRecordTrajectoryAnalytics(token, num, trajectoryId,messageId,conversationId, modelName).catch(err => logger.warn('发送轨迹分析失败:', err.message)); |
| sendLog(token,num,trajectoryId,conversationId,messageId).catch(err => logger.warn('发送log失败:', err.message)); |
|
|
| const parts = data.response?.candidates?.[0]?.content?.parts || []; |
| const images = parts.filter(p => p.inlineData).map(p => p.inlineData.data); |
|
|
| return images; |
| } |
|
|
| export async function sendRecordTrajectoryAnalytics(token, num, trajectoryId,executionId,cascadeId, modelName = "claude-opus-4-6-thinking") { |
| const trajectorybody = generateTrajectorybody(num, trajectoryId,executionId,cascadeId, modelName, token); |
| const headers = buildHeaders(token); |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(trajectorybody))); |
| try { |
| if (useAxios) { |
| await httpRequest({ |
| method: 'POST', |
| url: config.api.recordTrajectory, |
| headers, |
| data: trajectorybody |
| }); |
| } else { |
| const response = await requester.antigravity_fetch(config.api.recordTrajectory, buildRequesterConfig(headers, trajectorybody)); |
| if (response.status !== 200) { |
| const errorBody = await response.text(); |
| throw new Error(`轨迹分析请求失败 (${response.status}): ${errorBody}`); |
| } |
| } |
| } catch (error) { |
| throw error; |
| } |
| } |
| export async function sendLog(token, num, trajectoryId, conversationId,messageId) { |
| const sessionId = trajectoryId; |
| |
| |
| const logs = [ |
| createLog2(conversationId, token, sessionId), |
| createTelemetryBatch(num, sessionId,conversationId,messageId,token.sub), |
| createLog1(conversationId, token, sessionId) |
| ]; |
| |
| const headers = buildHeaders(token); |
| headers["Host"] = "play.googleapis.com"; |
| headers["User-Agent"] = "Go-http-client/1.1"; |
| headers["Content-Type"] = "application/octet-stream"; |
| headers["Accept-Encoding"] = "gzip"; |
| |
| try { |
| for (const log of logs) { |
| const serializeData = serializeTelemetryBatch(log); |
| if (!serializeData.success) { |
| throw new Error(`Telemetry proto 序列化失败: ${serializeData.error}`); |
| } |
| const serializeLogBody = serializeData.data; |
| headers["Content-Length"] = String(serializeLogBody.length); |
| |
| await axios({ |
| method: 'POST', |
| url: "https://play.googleapis.com/log", |
| headers, |
| data: serializeLogBody |
| }); |
| } |
| } catch (error) { |
| throw error; |
| } |
| } |
|
|
| export async function sendRecordCodeAssistMetrics(token, trajectoryId) { |
| const requestBody = buildRecordCodeAssistMetricsBody(token, trajectoryId); |
| const headers = buildHeaders(token); |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(requestBody),'utf-8')); |
| try { |
| if (useAxios) { |
| await httpRequest({ |
| method: 'POST', |
| url: config.api.recordCodeAssistMetrics, |
| headers, |
| data: requestBody |
| }); |
| } else { |
| const response = await requester.antigravity_fetch(config.api.recordCodeAssistMetrics, buildRequesterConfig(headers, requestBody)); |
| if (response.status !== 200) { |
| const errorBody = await response.text(); |
| throw new Error(`RecordCodeAssistMetrics请求失败 (${response.status}): ${errorBody}`); |
| } |
| } |
| } catch (error) { |
| throw error; |
| } |
| } |
|
|
| export async function sendClientRegister(token) { |
| const requestBody = buildClientRegister(token); |
| const headers = buildClientRegisterHeaders(token); |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(requestBody),'utf-8')); |
| try { |
| if (useAxios) { |
| await httpRequest({ |
| method: 'POST', |
| url: config.api.unleash.register, |
| headers, |
| data: requestBody |
| }); |
| } else { |
| const response = await requester.antigravity_fetch(config.api.unleash.register, buildRequesterConfig(headers, requestBody)); |
| if (response.status !== 200 && response.status !== 202) { |
| const errorBody = await response.text(); |
| throw new Error(`ClientRegister请求失败 (${response.status}): ${errorBody}`); |
| } |
| } |
| } catch (error) { |
| throw error; |
| } |
| } |
|
|
| export async function sendClientFeature(token) { |
| const headers = buildClientFeatrueHeaders(token); |
| |
| try { |
| if (useAxios) { |
| await httpRequest({ |
| method: 'GET', |
| url: config.api.unleash.features, |
| headers |
| }); |
| } else { |
| const response = await requester.antigravity_fetch(config.api.unleash.features, buildRequesterConfig(headers, null, "GET")); |
| if (response.status !== 200 && response.status !== 202) { |
| const errorBody = await response.text(); |
| throw new Error(`ClientFeature请求失败 (${response.status}): ${errorBody}`); |
| } |
| } |
| } catch (error) { |
| throw error; |
| } |
| } |
|
|
| export async function sendFrontEnd(token) { |
| const requestBody = buildFrontEnd(token); |
| const headers = buildFrontEndHeaders(token); |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(requestBody),'utf-8')); |
| try { |
| if (useAxios) { |
| await httpRequest({ |
| method: 'POST', |
| url: config.api.unleash.frontend, |
| headers, |
| data: requestBody |
| }); |
| } else { |
| const response = await requester.antigravity_fetch(config.api.unleash.frontend, buildRequesterConfig(headers, requestBody)); |
| if (response.status !== 200 && response.status !== 202) { |
| const errorBody = await response.text(); |
| throw new Error(`FrontEnd请求失败 (${response.status}): ${errorBody}`); |
| } |
| } |
| } catch (error) { |
| throw error; |
| } |
| } |
|
|
| export async function sendCheckPoint(token) { |
| const requestBody = generateCheckpointBody(token); |
| const headers = buildHeaders(token); |
| headers["Content-Length"] = String(Buffer.byteLength(JSON.stringify(requestBody),'utf-8')); |
| if (checkPointList.has(token.sessionId)){ |
| return; |
| }else{ |
| checkPointList.add(token.sessionId); |
| } |
| try { |
| if (useAxios) { |
| await httpRequest({ |
| method: 'POST', |
| url: config.api.url, |
| headers, |
| data: requestBody |
| }); |
| } else { |
| const response = await requester.antigravity_fetch(config.api.url, buildRequesterConfig(headers, requestBody)); |
| if (response.status !== 200 && response.status !== 202) { |
| const errorBody = await response.text(); |
| throw new Error(`CheckPoint请求失败 (${response.status}): ${errorBody}`); |
| } |
| } |
| } catch (error) { |
| throw error; |
| } |
| } |
|
|
| export function closeRequester() { |
| if (requester) requester.close(); |
| } |
|
|
| |
| export { registerMemoryCleanup }; |
|
|