fix(backend): cover cursor-agent CLI local-mode endpoint surface

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.
This commit is contained in:
warelik
2026-08-01 10:58:19 +03:00
parent 1f976b2a5e
commit 2c650b3765
5 changed files with 284 additions and 1 deletions
@@ -165,6 +165,18 @@ func DefaultModelNudgeMockBuilder(reqCtx *RequestContext) (map[string]any, error
return buildDefaultModelNudgeDataPayload(reqCtx)
}
func UsableModelsMockBuilder(reqCtx *RequestContext) (map[string]any, error) {
return buildUsableModelsPayload(reqCtx)
}
func DefaultModelForCliMockBuilder(reqCtx *RequestContext) (map[string]any, error) {
return buildDefaultModelForCliPayload(reqCtx)
}
func DefaultModelMockBuilder(reqCtx *RequestContext) (map[string]any, error) {
return buildDefaultModelPayload(reqCtx)
}
func BootstrapStatsigMockBuilder(reqCtx *RequestContext) (map[string]any, error) {
return buildBootstrapStatsigPayload(reqCtx)
}
@@ -185,6 +197,18 @@ func DashboardManagedSkillsMockBuilder(reqCtx *RequestContext) (map[string]any,
return buildDashboardManagedSkillsPayload(reqCtx)
}
// EmptyMockBuilder возвращает пустой proto-ответ для ручек, где клиенту
// достаточно успешного "пусто": нет team-настроек, нет репозиториев,
// нет маркетплейсов/плагинов/команд, телеметрия принята без обработки.
func EmptyMockBuilder(reqCtx *RequestContext) (map[string]any, error) {
return map[string]any{}, nil
}
// SubmitLogsMockBuilder подтверждает приём логов телеметрии без обработки.
func SubmitLogsMockBuilder(reqCtx *RequestContext) (map[string]any, error) {
return map[string]any{"success": true}, nil
}
func DashboardGetMeMockBuilder(reqCtx *RequestContext) (map[string]any, error) {
return buildDashboardGetMePayload(reqCtx)
}