(
req: CursorChatRequest,
onChunk: (event: CursorSSEEvent) => void,
externalSignal?: AbortSignal,
)
| 72 | } |
| 73 | |
| 74 | async function sendCursorRequestInner( |
| 75 | req: CursorChatRequest, |
| 76 | onChunk: (event: CursorSSEEvent) => void, |
| 77 | externalSignal?: AbortSignal, |
| 78 | ): Promise<void> { |
| 79 | const headers = getChromeHeaders(); |
| 80 | |
| 81 | // 详细日志记录在 handler 层 |
| 82 | |
| 83 | const config = getConfig(); |
| 84 | const controller = new AbortController(); |
| 85 | // 链接外部信号:外部中止时同步中止内部 controller |
| 86 | if (externalSignal) { |
| 87 | if (externalSignal.aborted) { controller.abort(); } |
| 88 | else { externalSignal.addEventListener('abort', () => controller.abort(), { once: true }); } |
| 89 | } |
| 90 | |
| 91 | // ★ 空闲超时(Idle Timeout):用读取活动检测替换固定总时长超时。 |
| 92 | // 每次收到新数据时重置计时器,只有在指定时间内完全无数据到达时才中断。 |
| 93 | // 这样长输出(如写长文章、大量工具调用)不会因总时长超限被误杀。 |
| 94 | const IDLE_TIMEOUT_MS = config.timeout * 1000; // 复用 timeout 配置作为空闲超时阈值 |
| 95 | let idleTimer: ReturnType<typeof setTimeout> | null = null; |
| 96 | |
| 97 | const resetIdleTimer = () => { |
| 98 | if (idleTimer) clearTimeout(idleTimer); |
| 99 | idleTimer = setTimeout(() => { |
| 100 | console.warn(`[Cursor] 空闲超时(${config.timeout}s 无新数据),中止请求`); |
| 101 | controller.abort(); |
| 102 | }, IDLE_TIMEOUT_MS); |
| 103 | }; |
| 104 | |
| 105 | // 启动初始计时(等待服务器开始响应) |
| 106 | resetIdleTimer(); |
| 107 | |
| 108 | try { |
| 109 | const resp = await fetch(CURSOR_CHAT_API, { |
| 110 | method: 'POST', |
| 111 | headers, |
| 112 | body: JSON.stringify(req), |
| 113 | signal: controller.signal, |
| 114 | ...getProxyFetchOptions(), |
| 115 | } as any); |
| 116 | |
| 117 | if (!resp.ok) { |
| 118 | const body = await resp.text(); |
| 119 | throw new Error(`Cursor API 错误: HTTP ${resp.status} - ${body}`); |
| 120 | } |
| 121 | |
| 122 | if (!resp.body) { |
| 123 | throw new Error('Cursor API 响应无 body'); |
| 124 | } |
| 125 | |
| 126 | // 流式读取 SSE 响应 |
| 127 | const reader = resp.body.getReader(); |
| 128 | const decoder = new TextDecoder(); |
| 129 | let buffer = ''; |
| 130 | |
| 131 | // ★ 退化重复检测器 (#66) |
no test coverage detected