mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-06-04 06:39:22 +08:00
Merge PR #525 (v6.9.27)
This commit is contained in:
@@ -4,11 +4,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -30,14 +30,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
cursorAPIURL = "https://api2.cursor.sh"
|
||||
cursorRunPath = "/agent.v1.AgentService/Run"
|
||||
cursorModelsPath = "/agent.v1.AgentService/GetUsableModels"
|
||||
cursorClientVersion = "cli-2026.02.13-41ac335"
|
||||
cursorAuthType = "cursor"
|
||||
cursorAPIURL = "https://api2.cursor.sh"
|
||||
cursorRunPath = "/agent.v1.AgentService/Run"
|
||||
cursorModelsPath = "/agent.v1.AgentService/GetUsableModels"
|
||||
cursorClientVersion = "cli-2026.02.13-41ac335"
|
||||
cursorAuthType = "cursor"
|
||||
cursorHeartbeatInterval = 5 * time.Second
|
||||
cursorSessionTTL = 5 * time.Minute
|
||||
cursorCheckpointTTL = 30 * time.Minute
|
||||
cursorSessionTTL = 5 * time.Minute
|
||||
cursorCheckpointTTL = 30 * time.Minute
|
||||
)
|
||||
|
||||
// CursorExecutor handles requests to the Cursor API via Connect+Protobuf protocol.
|
||||
@@ -63,9 +63,9 @@ type cursorSession struct {
|
||||
pending []pendingMcpExec
|
||||
cancel context.CancelFunc // cancels the session-scoped heartbeat (NOT tied to HTTP request)
|
||||
createdAt time.Time
|
||||
authID string // auth file ID that created this session (for multi-account isolation)
|
||||
toolResultCh chan []toolResultInfo // receives tool results from the next HTTP request
|
||||
resumeOutCh chan cliproxyexecutor.StreamChunk // output channel for resumed response
|
||||
authID string // auth file ID that created this session (for multi-account isolation)
|
||||
toolResultCh chan []toolResultInfo // receives tool results from the next HTTP request
|
||||
resumeOutCh chan cliproxyexecutor.StreamChunk // output channel for resumed response
|
||||
switchOutput func(ch chan cliproxyexecutor.StreamChunk) // callback to switch output channel
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ type cursorStatusErr struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e cursorStatusErr) Error() string { return e.msg }
|
||||
func (e cursorStatusErr) Error() string { return e.msg }
|
||||
func (e cursorStatusErr) StatusCode() int { return e.code }
|
||||
func (e cursorStatusErr) RetryAfter() *time.Duration { return nil } // no retry-after info from Cursor; conductor uses exponential backoff
|
||||
|
||||
@@ -786,7 +786,7 @@ func (e *CursorExecutor) resumeWithToolResults(
|
||||
func openCursorH2Stream(accessToken string) (*cursorproto.H2Stream, error) {
|
||||
headers := map[string]string{
|
||||
":path": cursorRunPath,
|
||||
"content-type": "application/connect+proto",
|
||||
"content-type": "application/connect+proto",
|
||||
"connect-protocol-version": "1",
|
||||
"te": "trailers",
|
||||
"authorization": "Bearer " + accessToken,
|
||||
@@ -876,21 +876,21 @@ func processH2SessionFrames(
|
||||
buf.Write(data)
|
||||
log.Debugf("cursor: processH2SessionFrames[%s]: buf total=%d", stream.ID(), buf.Len())
|
||||
|
||||
// Process all complete frames
|
||||
for {
|
||||
currentBuf := buf.Bytes()
|
||||
if len(currentBuf) == 0 {
|
||||
break
|
||||
}
|
||||
flags, payload, consumed, ok := cursorproto.ParseConnectFrame(currentBuf)
|
||||
if !ok {
|
||||
// Log detailed info about why parsing failed
|
||||
previewLen := min(20, len(currentBuf))
|
||||
log.Debugf("cursor: incomplete frame in buffer, waiting for more data (buf=%d bytes, first bytes: %x = %q)", len(currentBuf), currentBuf[:previewLen], string(currentBuf[:previewLen]))
|
||||
break
|
||||
}
|
||||
buf.Next(consumed)
|
||||
log.Debugf("cursor: parsed Connect frame flags=0x%02x payload=%d bytes consumed=%d", flags, len(payload), consumed)
|
||||
// Process all complete frames
|
||||
for {
|
||||
currentBuf := buf.Bytes()
|
||||
if len(currentBuf) == 0 {
|
||||
break
|
||||
}
|
||||
flags, payload, consumed, ok := cursorproto.ParseConnectFrame(currentBuf)
|
||||
if !ok {
|
||||
// Log detailed info about why parsing failed
|
||||
previewLen := min(20, len(currentBuf))
|
||||
log.Debugf("cursor: incomplete frame in buffer, waiting for more data (buf=%d bytes, first bytes: %x = %q)", len(currentBuf), currentBuf[:previewLen], string(currentBuf[:previewLen]))
|
||||
break
|
||||
}
|
||||
buf.Next(consumed)
|
||||
log.Debugf("cursor: parsed Connect frame flags=0x%02x payload=%d bytes consumed=%d", flags, len(payload), consumed)
|
||||
|
||||
if flags&cursorproto.ConnectEndStreamFlag != 0 {
|
||||
if err := cursorproto.ParseConnectEndStream(payload); err != nil {
|
||||
@@ -1080,15 +1080,15 @@ func processH2SessionFrames(
|
||||
// --- OpenAI request parsing ---
|
||||
|
||||
type parsedOpenAIRequest struct {
|
||||
Model string
|
||||
Messages []gjson.Result
|
||||
Tools []gjson.Result
|
||||
Stream bool
|
||||
Model string
|
||||
Messages []gjson.Result
|
||||
Tools []gjson.Result
|
||||
Stream bool
|
||||
SystemPrompt string
|
||||
UserText string
|
||||
Images []cursorproto.ImageData
|
||||
Turns []cursorproto.TurnData
|
||||
ToolResults []toolResultInfo
|
||||
UserText string
|
||||
Images []cursorproto.ImageData
|
||||
Turns []cursorproto.TurnData
|
||||
ToolResults []toolResultInfo
|
||||
}
|
||||
|
||||
type toolResultInfo struct {
|
||||
|
||||
Reference in New Issue
Block a user