feat(cursor): add contributor profile link and improve UI for Cursor account card

- Integrated a tooltip with a button to open the contributor's GitHub profile in the CursorAccountCard component.
- Updated the layout of the account card for better visual organization.
- Removed routing mode options from the configuration view and adjusted related translations in multiple languages.
- Cleaned up unused routing mode logic in the app state management.
This commit is contained in:
leookun
2026-08-02 22:53:57 +08:00
parent 674de0b041
commit a5437e60fe
22 changed files with 293 additions and 753 deletions
+2 -3
View File
@@ -1,6 +1,6 @@
# Backend 架构说明
`internal/backend` 当前支持本地助手模式与直连上游模式
`internal/backend` 支持本地助手模式。
关于 backend agent「最小事实集合」的第一阶段研究文档,见 [`../../docs/backend-agent-minimum-facts-phase1.md`](../../docs/backend-agent-minimum-facts-phase1.md)。
@@ -34,7 +34,6 @@ internal/backend/
errors.go
local.go
middleware.go
policy.go
route.go
url.go
@@ -134,7 +133,7 @@ history/
## 请求流
1. 请求进入 backend 根路由。
2. `PolicyMiddleware` 根据 `routing.mode``X-Server-Upstream-URL` 选择本地或上游分支
2. `ServerContext` 解析 MITM 带入的原始目标地址,路由始终执行本地 action
3. `BidiAppend` / `RunSSE` 进入 `forwarder`
4. `forwarder` 先把当前 loop 状态写入 `state.json`,再把已发生语义事件追加到 `context.json`
5. 发给 LLM 的 prompt 只由 `context.json` 投射生成;`state.json` 不保存可投射历史。
+1 -1
View File
@@ -524,7 +524,7 @@ func (service *Service) applyProviderModelEvent(stream *ActiveStream, event mode
stream.mu.Unlock()
}
if shouldEmitSyntheticThinking {
if err := service.broker.Publish(requestID, StreamEvent{Message: buildThinkingDeltaMessage("Thinking is encrypted. Please wait a moment.", event.ThinkingStyle)}); err != nil {
if err := service.broker.Publish(requestID, StreamEvent{Message: buildThinkingDeltaMessage("The reasoning process is encrypted. Please wait a moment. (This message does not affect any functionality; it only indicates the current reasoning status.)", event.ThinkingStyle)}); err != nil {
return err
}
}
+43 -175
View File
@@ -281,7 +281,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
server.Use(
server.Recover(),
server.ServerContext(),
server.PolicyMiddleware(host.configs),
server.ErrorEncoder(),
),
server.Mount(ads.RoutePrefix, ads.NewHTTPHandler(appdata.AdsRootPath())),
@@ -294,17 +293,11 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
server.Name("bidi_append"),
server.ConnectUnary(),
server.Local(server.HTTPHandlerAction(agentModule.LocalBidiHandler)),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "bidi_append",
})),
),
server.POST(legacyRunSSEProcedure,
server.Name("run_sse"),
server.ConnectStream(),
server.Local(server.HTTPHandlerAction(agentModule.LocalRunSSE)),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "run_sse",
})),
),
server.POST("/aiserver.v1.AiService/ServerTime",
server.Name("server_time"),
@@ -315,9 +308,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.ServerTimeResponse",
MockBuilder: upstream.ServerTimeMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "server_time",
})),
),
server.POST("/aiserver.v1.AiService/GetServerConfig",
server.Name("server_config"),
@@ -328,9 +318,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetServerConfigResponse",
MockBuilder: upstream.ServerConfigMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "server_config",
})),
),
server.POST("/aiserver.v1.AiService/AvailableModels",
server.Name("available_models"),
@@ -341,9 +328,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.AvailableModelsResponse",
MockBuilder: upstream.AvailableModelsMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "available_models",
})),
),
server.POST("/aiserver.v1.AiService/GetDefaultModelNudgeData",
server.Name("default_model_nudge"),
@@ -354,9 +338,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetDefaultModelNudgeDataResponse",
MockBuilder: upstream.DefaultModelNudgeMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "default_model_nudge",
})),
),
server.POST("/aiserver.v1.AnalyticsService/BootstrapStatsig",
server.Name("bootstrap_statsig"),
@@ -367,9 +348,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.BootstrapStatsigResponse",
MockBuilder: upstream.BootstrapStatsigMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "bootstrap_statsig",
})),
),
server.POST("/aiserver.v1.AnalyticsService/GetFirstWindowStatsigDecision",
server.Name("first_window_statsig_decision"),
@@ -380,9 +358,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetFirstWindowStatsigDecisionResponse",
MockBuilder: upstream.FirstWindowStatsigDecisionMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "first_window_statsig_decision",
})),
),
server.POST("/oauth/token",
server.Name("oauth_token"),
@@ -391,9 +366,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
Name: "oauth_token",
StatusCode: http.StatusOK,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "oauth_token",
})),
),
server.POST("/aiserver.v1.AuthService/GetEmail",
server.Name("auth_service_get_email"),
@@ -402,50 +374,44 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
Name: "auth_service_get_email",
StatusCode: http.StatusOK,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "auth_service_get_email",
})),
),
tabServerUpstreamProcedure("/aiserver.v1.AiService/StreamCpp", "ai_stream_cpp", server.ConnectStream(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/StreamNextCursorPrediction", "ai_stream_next_cursor_prediction", server.ConnectStream(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/GetCppEditClassification", "ai_get_cpp_edit_classification", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/RefreshTabContext", "ai_refresh_tab_context", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/CppConfig", "ai_cpp_config", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/CppEditHistoryStatus", "ai_cpp_edit_history_status", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/CppAppend", "ai_cpp_append", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/CppEditHistoryAppend", "ai_cpp_edit_history_append", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/ReportAiCodeChangeMetrics", "ai_report_ai_code_change_metrics", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/WriteGitCommitMessage", "ai_write_git_commit_message", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.AiService/WriteGitBranchName", "ai_write_git_branch_name", server.ConnectUnary(), routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceFastRepoInitHandshakeV2Procedure, "repository_fast_repo_init_handshake_v2", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceFastRepoInitHandshakeProcedure, "repository_fast_repo_init_handshake", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceFastRepoSyncCompleteProcedure, "repository_fast_repo_sync_complete", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceSyncMerkleSubtreeV2Procedure, "repository_sync_merkle_subtree_v2", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceSyncMerkleSubtreeProcedure, "repository_sync_merkle_subtree", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceFastUpdateFileV2Procedure, "repository_fast_update_file_v2", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceFastUpdateFileProcedure, "repository_fast_update_file", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceEnsureIndexCreatedProcedure, "repository_ensure_index_created", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceGetCopyStatusProcedure, "repository_get_copy_status", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceGetUploadLimitsProcedure, "repository_get_upload_limits", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceGetNumFilesToSendProcedure, "repository_get_num_files_to_send", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceGetAvailableChunkingStrategiesProcedure, "repository_get_available_chunking_strategies", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceGetHighLevelFolderDescriptionProcedure, "repository_get_high_level_folder_description", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceRepositoryStatusProcedure, "repository_status", server.ConnectUnary(), agentModule, routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceBatchRepositoryStatusProcedure, "repository_batch_status", server.ConnectUnary(), agentModule, routeDeps),
uploadServiceProcedure(forwarder.UploadServiceUploadDocumentationProcedure, "upload_documentation", server.ConnectUnary(), agentModule, routeDeps),
uploadServiceProcedure(forwarder.UploadServiceGetDocProcedure, "upload_get_doc", server.ConnectUnary(), agentModule, routeDeps),
uploadServiceProcedure(forwarder.UploadServiceGetPagesProcedure, "upload_get_pages", server.ConnectUnary(), agentModule, routeDeps),
uploadServiceProcedure(forwarder.UploadServiceUploadedStatusProcedure, "upload_uploaded_status", server.ConnectUnary(), agentModule, routeDeps),
tabServerProcedure("/aiserver.v1.AiService/StreamCpp", "ai_stream_cpp", server.ConnectStream(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/StreamNextCursorPrediction", "ai_stream_next_cursor_prediction", server.ConnectStream(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/GetCppEditClassification", "ai_get_cpp_edit_classification", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/RefreshTabContext", "ai_refresh_tab_context", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/CppConfig", "ai_cpp_config", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/CppEditHistoryStatus", "ai_cpp_edit_history_status", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/CppAppend", "ai_cpp_append", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/CppEditHistoryAppend", "ai_cpp_edit_history_append", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/ReportAiCodeChangeMetrics", "ai_report_ai_code_change_metrics", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/WriteGitCommitMessage", "ai_write_git_commit_message", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.AiService/WriteGitBranchName", "ai_write_git_branch_name", server.ConnectUnary(), routeDeps),
repositoryServiceProcedure(forwarder.RepositoryServiceFastRepoInitHandshakeV2Procedure, "repository_fast_repo_init_handshake_v2", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceFastRepoInitHandshakeProcedure, "repository_fast_repo_init_handshake", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceFastRepoSyncCompleteProcedure, "repository_fast_repo_sync_complete", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceSyncMerkleSubtreeV2Procedure, "repository_sync_merkle_subtree_v2", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceSyncMerkleSubtreeProcedure, "repository_sync_merkle_subtree", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceFastUpdateFileV2Procedure, "repository_fast_update_file_v2", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceFastUpdateFileProcedure, "repository_fast_update_file", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceEnsureIndexCreatedProcedure, "repository_ensure_index_created", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceGetCopyStatusProcedure, "repository_get_copy_status", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceGetUploadLimitsProcedure, "repository_get_upload_limits", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceGetNumFilesToSendProcedure, "repository_get_num_files_to_send", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceGetAvailableChunkingStrategiesProcedure, "repository_get_available_chunking_strategies", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceGetHighLevelFolderDescriptionProcedure, "repository_get_high_level_folder_description", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceRepositoryStatusProcedure, "repository_status", server.ConnectUnary(), agentModule),
repositoryServiceProcedure(forwarder.RepositoryServiceBatchRepositoryStatusProcedure, "repository_batch_status", server.ConnectUnary(), agentModule),
uploadServiceProcedure(forwarder.UploadServiceUploadDocumentationProcedure, "upload_documentation", server.ConnectUnary(), agentModule),
uploadServiceProcedure(forwarder.UploadServiceGetDocProcedure, "upload_get_doc", server.ConnectUnary(), agentModule),
uploadServiceProcedure(forwarder.UploadServiceGetPagesProcedure, "upload_get_pages", server.ConnectUnary(), agentModule),
uploadServiceProcedure(forwarder.UploadServiceUploadedStatusProcedure, "upload_uploaded_status", server.ConnectUnary(), agentModule),
server.Any("/aiserver.v1.AiService/*",
server.Name("ai_service"),
server.HTTP(),
server.Local(server.HTTPHandlerAction(agentModule.AiHandler)),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "ai_service",
})),
),
tabServerUpstreamProcedure("/aiserver.v1.CppService/AvailableModels", "cpp_available_models", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.CppService/RecordCppFate", "cpp_record_cpp_fate", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.CppService/AvailableModels", "cpp_available_models", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.CppService/RecordCppFate", "cpp_record_cpp_fate", server.ConnectUnary(), routeDeps),
server.Any("/aiserver.v1.CppService/*",
server.Name("cpp_service"),
server.HTTP(),
@@ -453,14 +419,11 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
http.NotFound(ctx.Writer, ctx.Request)
return nil
}),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "cpp_service",
})),
),
tabServerUpstreamProcedure("/aiserver.v1.FileSyncService/FSSyncFile", "file_sync_sync_file", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.FileSyncService/FSIsEnabledForUser", "file_sync_is_enabled_for_user", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.FileSyncService/FSConfig", "file_sync_config", server.ConnectUnary(), routeDeps),
tabServerUpstreamProcedure("/aiserver.v1.FileSyncService/FSUploadFile", "file_sync_upload_file", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.FileSyncService/FSSyncFile", "file_sync_sync_file", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.FileSyncService/FSIsEnabledForUser", "file_sync_is_enabled_for_user", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.FileSyncService/FSConfig", "file_sync_config", server.ConnectUnary(), routeDeps),
tabServerProcedure("/aiserver.v1.FileSyncService/FSUploadFile", "file_sync_upload_file", server.ConnectUnary(), routeDeps),
server.Any("/aiserver.v1.FileSyncService/*",
server.Name("file_sync"),
server.HTTP(),
@@ -468,25 +431,16 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
http.NotFound(ctx.Writer, ctx.Request)
return nil
}),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "file_sync",
})),
),
server.POST("/aiserver.v1.DashboardService/GetTokenUsage",
server.Name("dashboard_token_usage"),
server.HTTP(),
server.Local(server.HTTPHandlerAction(agentModule.AiHandler)),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_token_usage",
})),
),
server.POST("/aiserver.v1.DashboardService/GetGlassEarlyPreviewEnrollment",
server.Name("dashboard_glass_early_preview_enrollment"),
server.ConnectUnary(),
server.Local(server.HTTPHandlerAction(agentModule.AiHandler)),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_glass_early_preview_enrollment",
})),
),
server.POST("/aiserver.v1.DashboardService/GetCurrentPeriodUsage",
server.Name("dashboard_current_period_usage"),
@@ -497,9 +451,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetCurrentPeriodUsageResponse",
MockBuilder: upstream.DashboardCurrentPeriodUsageMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_current_period_usage",
})),
),
server.POST("/aiserver.v1.DashboardService/GetTeams",
server.Name("dashboard_get_teams"),
@@ -510,9 +461,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetTeamsResponse",
MockBuilder: upstream.DashboardTeamsMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_get_teams",
})),
),
server.POST("/aiserver.v1.DashboardService/GetManagedSkills",
server.Name("dashboard_get_managed_skills"),
@@ -528,14 +476,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockBuilder: upstream.DashboardManagedSkillsMockBuilder,
}),
)),
server.Upstream(cursorControlPlaneAction(
host.controlPlaneAuth,
routeDeps,
"dashboard_get_managed_skills",
upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_get_managed_skills",
}),
)),
),
server.POST("/aiserver.v1.DashboardService/GetMe",
server.Name("dashboard_get_me"),
@@ -546,9 +486,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetMeResponse",
MockBuilder: upstream.DashboardGetMeMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_get_me",
})),
),
server.POST("/aiserver.v1.DashboardService/GetUserPrivacyMode",
server.Name("dashboard_user_privacy_mode"),
@@ -559,9 +496,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetUserPrivacyModeResponse",
MockBuilder: upstream.DashboardUserPrivacyModeMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_user_privacy_mode",
})),
),
server.POST("/aiserver.v1.DashboardService/GetPlanInfo",
server.Name("dashboard_plan_info"),
@@ -572,9 +506,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetPlanInfoResponse",
MockBuilder: upstream.DashboardPlanInfoMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_plan_info",
})),
),
server.POST("/aiserver.v1.DashboardService/GetUsageLimitStatusAndActiveGrants",
server.Name("dashboard_usage_limit_status"),
@@ -585,9 +516,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.GetUsageLimitStatusAndActiveGrantsResponse",
MockBuilder: upstream.DashboardUsageLimitStatusAndActiveGrantsMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_usage_limit_status",
})),
),
server.POST("/aiserver.v1.DashboardService/IsOnNewPricing",
server.Name("dashboard_is_on_new_pricing"),
@@ -598,9 +526,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
MockProtoType: "aiserver.v1.IsOnNewPricingResponse",
MockBuilder: upstream.DashboardIsOnNewPricingMockBuilder,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard_is_on_new_pricing",
})),
),
cursorControlPlaneProcedure("/aiserver.v1.DashboardService/AddMarketplace", "dashboard_add_marketplace", server.ConnectUnary(), host.controlPlaneAuth, routeDeps),
cursorControlPlaneProcedure("/aiserver.v1.DashboardService/AddMcpServersFromPlugin", "dashboard_add_mcp_servers_from_plugin", server.ConnectUnary(), host.controlPlaneAuth, routeDeps),
@@ -627,9 +552,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
http.NotFound(ctx.Writer, ctx.Request)
return nil
}),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "dashboard",
})),
),
server.Any("/aiserver.v1.NetworkService/*",
server.Name("network_service"),
@@ -638,9 +560,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
http.NotFound(ctx.Writer, ctx.Request)
return nil
}),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "network_service",
})),
),
server.Any("/aiserver.v1.InAppAdService/*",
server.Name("in_app_ad"),
@@ -649,9 +568,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
http.NotFound(ctx.Writer, ctx.Request)
return nil
}),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "in_app_ad",
})),
),
server.GET("/auth/full_stripe_profile",
server.Name("auth_full_stripe_profile"),
@@ -660,9 +576,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
Name: "auth_full_stripe_profile",
StatusCode: http.StatusOK,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "auth_full_stripe_profile",
})),
),
server.GET("/auth/stripe_profile",
server.Name("auth_stripe_profile"),
@@ -671,9 +584,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
Name: "auth_stripe_profile",
StatusCode: http.StatusOK,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "auth_stripe_profile",
})),
),
server.GET("/auth/has_valid_payment_method",
server.Name("auth_has_valid_payment_method"),
@@ -685,9 +595,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
"hasValidPaymentMethod": true,
},
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "auth_has_valid_payment_method",
})),
),
server.Any("/auth/poll",
server.Name("auth_poll"),
@@ -696,9 +603,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
Name: "auth_poll",
StatusCode: http.StatusOK,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "auth_poll",
})),
),
server.POST("/auth/logout",
server.Name("auth_logout"),
@@ -707,9 +611,6 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
Name: "auth_logout",
StatusCode: http.StatusNoContent,
})),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "auth_logout",
})),
),
server.Any("/auth/*",
server.Name("auth_proxy"),
@@ -718,58 +619,32 @@ func (host *Host) rebuildLocked(cfg serverconfig.Config) error {
http.NotFound(ctx.Writer, ctx.Request)
return nil
}),
server.Upstream(upstream.DirectAction(routeDeps, upstream.CompatRouteConfig{
Name: "auth_proxy",
})),
),
)
return nil
}
func directUpstreamProcedure(pattern string, name string, protocol server.RouteOption, deps upstream.Dependencies) server.Option {
direct := upstream.DirectAction(deps, upstream.CompatRouteConfig{Name: name})
action := func(ctx *server.Context) error {
if ctx != nil && ctx.UpstreamURL == nil && ctx.Request != nil && ctx.Request.URL != nil {
targetURL := *ctx.Request.URL
targetURL.Scheme = "https"
targetURL.Host = "api2.cursor.sh:443"
ctx.UpstreamURL = &targetURL
}
return direct(ctx)
}
return server.POST(pattern,
server.Name(name),
protocol,
server.Local(action),
server.Upstream(action),
)
}
func repositoryServiceProcedure(pattern string, name string, protocol server.RouteOption, module *forwarder.Module, deps upstream.Dependencies) server.Option {
func repositoryServiceProcedure(pattern string, name string, protocol server.RouteOption, module *forwarder.Module) server.Option {
localAction := server.HTTPHandlerAction(module.RepositoryServiceHandler)
upstreamAction := upstream.DirectAction(deps, upstream.CompatRouteConfig{Name: name})
return server.POST(pattern,
server.Name(name),
protocol,
server.Local(localAction),
server.Upstream(upstreamAction),
)
}
func uploadServiceProcedure(pattern string, name string, protocol server.RouteOption, module *forwarder.Module, deps upstream.Dependencies) server.Option {
func uploadServiceProcedure(pattern string, name string, protocol server.RouteOption, module *forwarder.Module) server.Option {
localAction := server.HTTPHandlerAction(module.UploadServiceHandler)
upstreamAction := upstream.DirectAction(deps, upstream.CompatRouteConfig{Name: name})
return server.POST(pattern,
server.Name(name),
protocol,
server.Local(localAction),
server.Upstream(upstreamAction),
)
}
func tabServerUpstreamProcedure(pattern string, name string, protocol server.RouteOption, deps upstream.Dependencies) server.Option {
direct := upstream.DirectAction(deps, upstream.CompatRouteConfig{Name: name})
func tabServerProcedure(pattern string, name string, protocol server.RouteOption, deps upstream.Dependencies) server.Option {
forward := upstream.ForwardAction(deps, upstream.CompatRouteConfig{Name: name})
action := func(ctx *server.Context) error {
if ctx != nil && ctx.Request != nil && ctx.Request.URL != nil {
baseURL, err := url.Parse(tabServerBaseURL)
@@ -781,13 +656,12 @@ func tabServerUpstreamProcedure(pattern string, name string, protocol server.Rou
targetURL.Host = baseURL.Host
ctx.UpstreamURL = &targetURL
}
return direct(ctx)
return forward(ctx)
}
return server.POST(pattern,
server.Name(name),
protocol,
server.Local(action),
server.Upstream(action),
)
}
@@ -806,12 +680,6 @@ func cursorControlPlaneProcedure(
server.Name(name),
protocol,
server.Local(cursorControlPlaneAction(authorizationProvider, deps, name, notFound)),
server.Upstream(cursorControlPlaneAction(
authorizationProvider,
deps,
name,
upstream.DirectAction(deps, upstream.CompatRouteConfig{Name: name}),
)),
)
}
@@ -821,7 +689,7 @@ func cursorControlPlaneAction(
name string,
fallback server.HandlerFunc,
) server.HandlerFunc {
direct := upstream.AuthenticatedDirectAction(deps, upstream.CompatRouteConfig{Name: name}, authorizationProvider)
forward := upstream.AuthenticatedForwardAction(deps, upstream.CompatRouteConfig{Name: name}, authorizationProvider)
return func(ctx *server.Context) error {
if authorizationProvider == nil || !authorizationProvider.SignedIn() {
return fallback(ctx)
@@ -833,7 +701,7 @@ func cursorControlPlaneAction(
targetURL.Scheme = "https"
targetURL.Host = "api2.cursor.sh:443"
ctx.UpstreamURL = &targetURL
return direct(ctx)
return forward(ctx)
}
}
-14
View File
@@ -171,20 +171,6 @@ func (manager *Manager) LegacyRuntimeSnapshot(_ context.Context) (legacyruntime.
}, nil
}
func (manager *Manager) RouteMode(hasUpstreamURL bool) string {
if !hasUpstreamURL {
return DefaultRoutingMode
}
if manager == nil {
return DefaultRoutingMode
}
mode := normalizeRoutingMode(manager.Current().Routing.Mode)
if mode == "" {
return DefaultRoutingMode
}
return mode
}
func (manager *Manager) setCurrent(cfg Config) {
next := cfg
manager.current.Store(&next)
+3
View File
@@ -136,6 +136,9 @@ func (store *Store) saveLocked(normalized Config) error {
}
func shouldPersistNormalizedConfig(raw []byte, current Config, normalized Config) bool {
if yamlHasKey(raw, "routing") {
return true
}
if !yamlHasKey(raw, "backendListenAddr") || !yamlHasKey(raw, "proxyListenAddr") {
return true
}
-24
View File
@@ -15,7 +15,6 @@ const (
DefaultBackendListenAddr = "127.0.0.1:18090"
DefaultProxyListenAddr = "127.0.0.1:18080"
DefaultFrontendBaseURL = "http://127.0.0.1"
DefaultRoutingMode = "local"
DefaultProviderStreamIdleTimeoutSeconds = 240
MinProviderStreamIdleTimeoutSeconds = 30
)
@@ -43,10 +42,6 @@ type ModelAdapterConfig struct {
ThinkingBudgetTokens int `json:"thinkingBudgetTokens" yaml:"thinkingBudgetTokens"`
}
type RoutingConfig struct {
Mode string `json:"mode" yaml:"mode"`
}
type HomeMetricsConfig struct {
IncludeCacheWriteInHitRate bool `json:"includeCacheWriteInHitRate" yaml:"includeCacheWriteInHitRate"`
}
@@ -57,7 +52,6 @@ type Config struct {
BackendListenAddr string `json:"backendListenAddr" yaml:"backendListenAddr"`
ProxyListenAddr string `json:"proxyListenAddr" yaml:"proxyListenAddr"`
ModelAdapters []ModelAdapterConfig `json:"modelAdapters" yaml:"modelAdapters"`
Routing RoutingConfig `json:"routing" yaml:"routing"`
HomeMetrics HomeMetricsConfig `json:"homeMetrics" yaml:"homeMetrics"`
LastAgentModelHash string `json:"lastAgentModelHash" yaml:"lastAgentModelHash"`
}
@@ -69,9 +63,6 @@ func DefaultConfig() Config {
BackendListenAddr: DefaultBackendListenAddr,
ProxyListenAddr: DefaultProxyListenAddr,
ModelAdapters: []ModelAdapterConfig{},
Routing: RoutingConfig{
Mode: DefaultRoutingMode,
},
}
}
@@ -91,10 +82,6 @@ func NormalizeConfig(input Config) (Config, error) {
output.ProxyListenAddr = proxyListenAddr
output.HomeMetrics.IncludeCacheWriteInHitRate = input.HomeMetrics.IncludeCacheWriteInHitRate
output.LastAgentModelHash = strings.TrimSpace(input.LastAgentModelHash)
output.Routing.Mode = normalizeRoutingMode(input.Routing.Mode)
if output.Routing.Mode == "" {
output.Routing.Mode = DefaultRoutingMode
}
adapters, err := NormalizeModelAdapterConfigs(input.ModelAdapters)
if err != nil {
return Config{}, err
@@ -280,14 +267,3 @@ func normalizeModelAdapterType(value string) string {
return ""
}
}
func normalizeRoutingMode(value string) string {
switch strings.ToLower(strings.TrimSpace(value)) {
case "", "local":
return "local"
case "upstream":
return "upstream"
default:
return ""
}
}
-2
View File
@@ -34,7 +34,6 @@ type Context struct {
StartedAt time.Time
UpstreamURL *url.URL
Mode ExecutionMode
LastError error
Logger *slog.Logger
@@ -48,7 +47,6 @@ func newContext(writer http.ResponseWriter, request *http.Request, route Route)
Protocol: route.Protocol,
StartedAt: time.Now(),
Logger: slog.Default(),
Mode: ModeLocal,
}
}
-12
View File
@@ -1,14 +1,12 @@
package server
import (
"cursor/internal/logger"
"errors"
"fmt"
"net/http"
"runtime/debug"
"strings"
serverconfig "cursor/internal/backend/server/config"
legacyruntime "cursor/internal/runtime"
)
@@ -39,16 +37,6 @@ func ServerContext() Middleware {
}
}
func PolicyMiddleware(configs *serverconfig.Manager) Middleware {
return func(next HandlerFunc) HandlerFunc {
return func(ctx *Context) error {
ctx.Mode = parseExecutionMode(configs.RouteMode(ctx.UpstreamURL != nil))
logger.Infof("ctx.Mode=%s upstream=%t", ctx.Mode, ctx.UpstreamURL != nil)
return next(ctx)
}
}
}
func ErrorEncoder() Middleware {
return func(next HandlerFunc) HandlerFunc {
return func(ctx *Context) error {
-19
View File
@@ -1,19 +0,0 @@
package server
type ExecutionMode string
const (
// ModeLocal 表示本地模式,适用于直接处理请求的情况。
ModeLocal ExecutionMode = "local"
// ModeUpstream 表示直连上游模式,适用于将请求转发到原始地址。
ModeUpstream ExecutionMode = "upstream"
)
func parseExecutionMode(value string) ExecutionMode {
switch value {
case string(ModeUpstream):
return ModeUpstream
default:
return ModeLocal
}
}
-21
View File
@@ -17,7 +17,6 @@ type Route struct {
Protocol ProtocolClass
Middleware []Middleware
Local HandlerFunc
Upstream HandlerFunc
}
type App struct {
@@ -129,12 +128,6 @@ func Local(action HandlerFunc) RouteOption {
}
}
func Upstream(action HandlerFunc) RouteOption {
return func(route *Route) {
route.Upstream = action
}
}
func (app *App) registerRoute(route Route) {
handler := app.buildRouteHandler(route)
if route.Method == "" {
@@ -148,12 +141,6 @@ func (app *App) buildRouteHandler(route Route) http.HandlerFunc {
chain := append([]Middleware{}, app.globalMiddlewares...)
chain = append(chain, route.Middleware...)
final := Chain(chain...)(func(ctx *Context) error {
if shouldUseUpstreamAction(ctx, route) && route.Upstream != nil {
return route.Upstream(ctx)
}
if shouldUseUpstreamAction(ctx, route) && ctx.UpstreamURL != nil {
return fmt.Errorf("route %s is missing upstream action while request targets upstream %s", route.Name, ctx.UpstreamURL.String())
}
if route.Local != nil {
return route.Local(ctx)
}
@@ -168,14 +155,6 @@ func (app *App) buildRouteHandler(route Route) http.HandlerFunc {
}
}
func shouldUseUpstreamAction(ctx *Context, route Route) bool {
_ = route
if ctx == nil {
return false
}
return ctx.Mode == ModeUpstream
}
func Chain(middlewares ...Middleware) Middleware {
return func(final HandlerFunc) HandlerFunc {
wrapped := final
+3 -4
View File
@@ -20,7 +20,7 @@ type CompatRouteConfig struct {
ConsoleLog bool
}
func DirectAction(deps Dependencies, cfg CompatRouteConfig) server.HandlerFunc {
func ForwardAction(deps Dependencies, cfg CompatRouteConfig) server.HandlerFunc {
return func(ctx *server.Context) error {
reqCtx, route, err := newCompatRouteObjects(ctx, deps, cfg)
if err != nil {
@@ -30,9 +30,9 @@ func DirectAction(deps Dependencies, cfg CompatRouteConfig) server.HandlerFunc {
}
}
// AuthenticatedDirectAction forwards a Cursor control-plane request with the
// AuthenticatedForwardAction forwards a Cursor control-plane request with the
// independent desktop account after the local-mode identity rewrite has run.
func AuthenticatedDirectAction(deps Dependencies, cfg CompatRouteConfig, authorizationProvider AuthorizationProvider) server.HandlerFunc {
func AuthenticatedForwardAction(deps Dependencies, cfg CompatRouteConfig, authorizationProvider AuthorizationProvider) server.HandlerFunc {
return func(ctx *server.Context) error {
reqCtx, _, err := newCompatRouteObjects(ctx, deps, cfg)
if err != nil {
@@ -162,7 +162,6 @@ func newCompatRouteObjects(ctx *server.Context, deps Dependencies, cfg CompatRou
Headers: ctx.Request.Header.Clone(),
ContentType: strings.TrimSpace(ctx.Request.Header.Get("content-type")),
RequestBody: body,
Mode: ctx.Mode,
Deps: &deps,
HTTPRequestID: resolveHTTPRequestID(ctx.Request),
}
+1 -2
View File
@@ -15,7 +15,6 @@ import (
"time"
"cursor/gen/aiserverv1"
"cursor/internal/backend/server"
"cursor/internal/logger"
"cursor/internal/netproxy"
legacyruntime "cursor/internal/runtime"
@@ -87,7 +86,7 @@ func buildUpstreamRequest(reqCtx *RequestContext, body []byte, options ForwardOp
}
upstreamRequest.Host = reqCtx.TargetURL.Host
if reqCtx.Mode == server.ModeLocal && shouldRewriteHost(reqCtx.TargetURL.Hostname()) {
if shouldRewriteHost(reqCtx.TargetURL.Hostname()) {
auth := formatBearerAuthorization(legacyruntime.LocalRelayToken)
if auth == "" {
return nil, nil, legacyruntime.ErrInvalidSystemSetting
@@ -48,7 +48,6 @@ type RequestContext struct {
Headers http.Header
ContentType string
RequestBody []byte
Mode server.ExecutionMode
Deps *Dependencies
HTTPRequestID string
}