(
req: CursorChatRequest,
onChunk: (event: CursorSSEEvent) => void,
externalSignal?: AbortSignal,
)
| 46 | * 发送请求到 Cursor /api/chat 并以流式方式处理响应(带重试) |
| 47 | */ |
| 48 | export async function sendCursorRequest( |
| 49 | req: CursorChatRequest, |
| 50 | onChunk: (event: CursorSSEEvent) => void, |
| 51 | externalSignal?: AbortSignal, |
| 52 | ): Promise<void> { |
| 53 | const maxRetries = 2; |
| 54 | for (let attempt = 1; attempt <= maxRetries; attempt++) { |
| 55 | try { |
| 56 | await sendCursorRequestInner(req, onChunk, externalSignal); |
| 57 | return; |
| 58 | } catch (err) { |
| 59 | // 外部主动中止不重试 |
| 60 | if (externalSignal?.aborted) throw err; |
| 61 | // ★ 退化循环中止不重试 — 已有的内容是有效的,重试也会重蹈覆辙 |
| 62 | if (err instanceof Error && err.message === 'DEGENERATE_LOOP_ABORTED') return; |
| 63 | const msg = err instanceof Error ? err.message : String(err); |
| 64 | console.error(`[Cursor] 请求失败 (${attempt}/${maxRetries}): ${msg.substring(0, 100)}`); |
| 65 | if (attempt < maxRetries) { |
| 66 | await new Promise(r => setTimeout(r, 2000)); |
| 67 | } else { |
| 68 | throw err; |
| 69 | } |
| 70 | } |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | async function sendCursorRequestInner( |
| 75 | req: CursorChatRequest, |
no test coverage detected