mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-18 03:57:06 +08:00
Desktop Cursor works through the local proxy because it drives the agent
over BidiAppend/RunSSE plus the already-mocked unary endpoints. The
cursor-agent CLI speaks the same agent protocol but calls additional
unary endpoints that had no local handlers, so every request fell into a
wildcard route and came back as HTTP 404, which the Connect client maps
to '[unimplemented] HTTP 404'.
Three independent breaks, one visible symptom:
1. Startup: ServerConfigService/GetServerConfig (only the AiService
variant was mocked), DashboardService/GetTeamAdminSettingsOrEmptyIfNotInTeam
and DashboardService/ListMarketplaces were missing, so the CLI aborted
during session init.
2. Git workspaces: the CLI resolves the repo path-encryption key from
indexingConfig.default{User,Team}PathEncryptionKey in GetServerConfig
when no IDE-stored repo keys exist. The mock returned no indexingConfig,
so repository identity init failed with 'No encryption key found'.
3. Tool execution: every fs tool executor (Ls/Grep/Glob/Shell) consults
the ignore service, which calls getRepoBlockExcludeGlobs() ->
DashboardService/GetTeamReposOrEmptyIfNotInTeam. The 404 propagated as
the tool result error, so model answers arrived but every tool call
returned '[unimplemented] HTTP 404'. Non-git workspaces skip the
repo-block path, which is why tools only failed inside git repos.
Also close the remaining tolerated 404 noise so a CLI session produces
zero unimplemented responses: model listing (GetUsableModels,
GetDefaultModelForCli, GetDefaultModel), dashboard/plugin housekeeping
(GetGlobalCommands, GetEffectiveUserPlugins, RegisterMarketplaceAndPlugins,
GetCliDownloadUrl) and telemetry (AnalyticsService/SubmitLogs,
AnalyticsService/TrackEvents, OTLP /v1/traces).
Additional logging: PolicyMiddleware now includes the request path in the
per-request log line, which is what made this diagnosable from app.log.
102 lines
2.4 KiB
Go
102 lines
2.4 KiB
Go
package server
|
|
|
|
import (
|
|
"cursor/internal/logger"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"runtime/debug"
|
|
"strings"
|
|
|
|
serverconfig "cursor/internal/backend/server/config"
|
|
legacyruntime "cursor/internal/runtime"
|
|
)
|
|
|
|
func Recover() Middleware {
|
|
return func(next HandlerFunc) HandlerFunc {
|
|
return func(ctx *Context) (err error) {
|
|
defer func() {
|
|
if recovered := recover(); recovered != nil {
|
|
err = fmt.Errorf("panic: %v\n%s", recovered, string(debug.Stack()))
|
|
}
|
|
}()
|
|
return next(ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
func ServerContext() Middleware {
|
|
return func(next HandlerFunc) HandlerFunc {
|
|
return func(ctx *Context) error {
|
|
if ctx == nil {
|
|
return fmt.Errorf("server context is nil")
|
|
}
|
|
if err := ctx.ParseUpstreamURL(); err != nil {
|
|
return err
|
|
}
|
|
return next(ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
func PolicyMiddleware(configs *serverconfig.Manager) Middleware {
|
|
return func(next HandlerFunc) HandlerFunc {
|
|
return func(ctx *Context) error {
|
|
ctx.Mode = parseExecutionMode(configs.RouteMode(ctx.UpstreamURL != nil))
|
|
path := ""
|
|
if ctx.Request != nil && ctx.Request.URL != nil {
|
|
path = ctx.Request.URL.Path
|
|
}
|
|
logger.Infof("ctx.Mode=%s upstream=%t path=%s", ctx.Mode, ctx.UpstreamURL != nil, path)
|
|
return next(ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
func ErrorEncoder() Middleware {
|
|
return func(next HandlerFunc) HandlerFunc {
|
|
return func(ctx *Context) error {
|
|
if ctx != nil {
|
|
ctx.LastError = nil
|
|
}
|
|
if err := next(ctx); err != nil {
|
|
if ctx != nil {
|
|
ctx.LastError = err
|
|
}
|
|
if ctx == nil || ctx.Writer == nil {
|
|
return err
|
|
}
|
|
writeServerError(ctx.Writer, err)
|
|
return nil
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func writeServerError(writer http.ResponseWriter, err error) {
|
|
if responseWriterHasWrittenHeader(writer) {
|
|
return
|
|
}
|
|
status := http.StatusBadGateway
|
|
message := "bad gateway"
|
|
switch {
|
|
case err == nil:
|
|
status = http.StatusOK
|
|
message = ""
|
|
case strings.TrimSpace(err.Error()) == "empty raw url":
|
|
status = http.StatusBadRequest
|
|
message = "invalid raw url"
|
|
case errors.Is(err, ErrInvalidBidiAppendPayload):
|
|
status = http.StatusBadRequest
|
|
message = "invalid bidi append payload"
|
|
case errors.Is(err, legacyruntime.ErrInvalidSystemSetting):
|
|
status = http.StatusInternalServerError
|
|
message = "invalid system setting"
|
|
case errors.Is(err, legacyruntime.ErrChannelNotAvailable):
|
|
status = http.StatusServiceUnavailable
|
|
message = "no available channel"
|
|
}
|
|
http.Error(writer, message, status)
|
|
}
|