import type { FastifyInstance } from 'fastify'; import type { Db } from './db/db.js'; import type { AppConfig } from './config/app-config.js'; import type { JwtService } from './crypto/jwt.service.js'; import type { AuthService } from './auth/auth.service.js'; import type { FeishuAuthService } from './auth/feishu-auth.service.js'; import type { EcpAuthService } from './auth/ecp-auth.service.js'; import type { UserService } from './user/user.service.js'; import type { MysqlUserRepository } from './user/user.repository.js'; import type { PermissionService } from './permission/permission.service.js'; import type { GitCredentialService } from './user/git-credential.service.js'; import type { EnvVariableService } from './env/env-variable.service.js'; import type { SessionRepository } from './session/session.repository.js'; import type { HttpToolService } from './http-tool/http-tool.service.js'; import type { HttpApiExecutor } from './harness/tool/impl/http-api-executor.js'; import type { FileService } from './file/file.service.js'; import type { AgentService } from './agent/agent.service.js'; import type { AgentExperienceService } from './agent/agent-experience.service.js'; import type { AgentSuggestedQuestionService } from './agent/agent-suggested-question.service.js'; import type { McpServerValidatorImpl } from './agent/mcp-validator.js'; import type { HttpToolValidatorImpl } from './http-tool/http-tool-validator.js'; import type { ModelService } from './model/model.service.js'; import type { SystemSettingService } from './settings/settings.service.js'; import type { UserCommandService } from './command/command.service.js'; import type { SkillLoader } from './harness/skill/skill-loader.js'; import type { SkillSyncService } from './harness/skill/skill-sync-service.js'; import type { UserWeixinPreferenceService } from './preference/weixin-preference.service.js'; import type { UserTaskPanelPreferenceService } from './preference/task-panel-preference.service.js'; import type { ToolService } from './tool/tool.service.js'; import type { AuditLogService } from './audit/audit.service.js'; import type { LlmCallService } from './usage/llm-call.service.js'; import type { SessionService } from './session/session.service.js'; import type { ActivityService } from './session/activity.service.js'; import type { MessageQueueService } from './session/message-queue.service.js'; import type { MysqlAgentRepository } from './agent/agent.repository.js'; import type { MysqlLlmModelRepository } from './model/model.repository.js'; import type { SessionTodoRepository } from './session/activity.repository.js'; import type { PathSandbox } from './harness/safety/path-sandbox.js'; import type { SubagentExecutionRepository } from './session/activity.repository.js'; import type { SessionCompactionEventService } from './session/session-compaction-event.service.js'; import type { SessionCompactionService } from './session/session-compaction.service.js'; import type { RunTraceService } from './session/run-trace.service.js'; import type { RunTraceRecordService } from './session/run-trace-record.service.js'; import type { ApprovalRegistry } from './harness/approval/approval-registry.js'; import type { AskUserQuestionsRegistry } from './harness/tool/ask-user-questions-registry.js'; import type { InFlightToolRegistry } from './harness/core/in-flight-tool-registry.js'; import type { SessionTreeSignalPublisher } from './harness/approval/session-tree-signal-publisher.js'; import type { WorkspaceBrowseService } from './file/workspace-browse.service.js'; import type { TerminalManager } from './harness/terminal/terminal-manager.js'; import type { WorkspaceGitService } from './file/workspace-git.service.js'; import type { GitCommitMessageService } from './file/git-commit-message.service.js'; import type { GitWriteOperationService } from './file/git-write-operation.service.js'; import type { RuntimeDataResolver } from './harness/runtime/runtime-data-resolver.js'; import type { OssStsService } from './oss/oss-sts.service.js'; import type { SkillBundleService } from './skill/skill-bundle.service.js'; import type { UserSkillService } from './skill/user-skill.service.js'; import type { SkillDocService } from './skill/skill-doc.service.js'; import type { ScheduledTaskService } from './schedule/scheduled-task.service.js'; import type { OpenRunService } from './openapi/open-run.service.js'; import type { ApiTokenService } from './openapi/api-token.service.js'; import type { WebhookTriggerService } from './openapi/webhook-trigger.service.js'; import type { OutboundSubscriptionService } from './openapi/outbound-subscription.service.js'; import type { FixedWindowRateLimiter } from './openapi/rate-limiter.js'; import type { OpenApiCallLogService } from './openapi/open-api-call-log.service.js'; import type { WatcherService } from './signal/watcher.service.js'; import type { MysqlSignalWatcherRepository } from './signal/signal.repository.js'; import type { AnalyticsService } from './analytics/analytics.service.js'; import type { StatisticsService } from './statistics/statistics.service.js'; import type { FeedbackService } from './feedback/feedback.service.js'; import type { MemoryService } from './memory/memory.service.js'; import type { ApprovalRuleService } from './approval-rule/approval-rule.service.js'; import type { InboxService } from './inbox/inbox.service.js'; import type { DigestService } from './digest/service.js'; import type { BudgetService } from './budget/budget.service.js'; import type { AdminAnalyticsService } from './admin/admin-analytics.service.js'; import type { AdminAnalyticsDbStore } from './admin/admin-analytics.service.js'; import type { MysqlUserCommandRepository } from './command/command.repository.js'; import type { McpServerService } from './harness/mcp/service/mcp-server.service.js'; import type { McpClientManager } from './harness/mcp/mcp-client-manager.js'; import type { UserMcpPreferenceService } from './harness/mcp/preference/service/user-mcp-preference.service.js'; import type { UserMcpLoadModeService } from './harness/mcp/preference/service/user-mcp-load-mode.service.js'; import type { McpLoadModeResolver } from './harness/mcp/workingset/mcp-load-mode.js'; import type { QrLoginService } from './weixin/qr-login.service.js'; import type { WeixinAccountRepository } from './weixin/account.repository.js'; import type { WeixinMonitorService } from './weixin/monitor.service.js'; import type { MysqlFeishuBotRepository } from './feishu/feishu_bot.repository.js'; import type { MysqlFeishuBindingRepository } from './feishu/binding.repository.js'; import type { FeishuMonitorService } from './feishu/monitor.service.js'; import type { TaskNotificationPreferenceService } from './notification/task/preference.service.js'; import type { StreamingWsHandler } from './session/ws/streaming-ws-handler.js'; import type { TerminalWsHandler } from './harness/terminal/terminal-ws-handler.js'; import type { RuntimeModeResolver } from './harness/container/runtime-mode.js'; import { validateRuntimeModeSave, RUNTIME_MODE_SETTING_KEY } from './harness/container/runtime-mode.js'; import type { ContainerHostsResolver } from './harness/container/container-hosts-resolver.js'; import type { ContainerAutoUpgradeResolver } from './harness/container/container-auto-upgrade.js'; import { CONTAINER_AUTO_UPGRADE_SETTING_KEY } from './common/container-auto-upgrade.js'; import type { ContainerOps } from './harness/container/container-registry.js'; import { registerAdminContainerRoutes } from './harness/container/container-admin.routes.js'; import { CONTAINER_EXTRA_HOSTS_KEY, CONTAINER_INHERIT_HOST_HOSTS_KEY } from './common/container-hosts.js'; import { sendOk } from './common/http-error.js'; import { registerAuthRoutes } from './auth/auth.routes.js'; import { registerEcpAuthRoutes } from './auth/ecp.routes.js'; import { registerCompanySsoRoutes } from './auth/company-sso.routes.js'; import { CompanySsoService } from './auth/company-sso.service.js'; import { CompanySsoClient } from './auth/company-sso.client.js'; import { CompanySsoIdentityRepository } from './auth/company-sso-identity.repository.js'; import { CompanySsoError } from './auth/company-sso.error.js'; import { registerUserRoutes } from './user/user.routes.js'; import { registerPermissionRoutes } from './permission/permission.routes.js'; import { registerAdminGitCredentialRoutes, registerGitCredentialRoutes } from './user/git-credential.routes.js'; import { registerAdminEnvVariableRoutes, registerEnvVariableRoutes } from './env/env-variable.routes.js'; import { registerAdminHttpToolRoutes } from './http-tool/http-tool-admin.routes.js'; import { registerAgentAvatarRoutes } from './agent/agent-avatar.js'; import { registerAgentRoutes } from './agent/agent.routes.js'; import { registerAgentExperienceLibraryRoutes } from './agent/agent-experience-library.routes.js'; import type { ExperienceIndexService } from './harness/experience/experience-index.service.js'; import { registerModelRoutes } from './model/model.routes.js'; import { registerSystemSettingRoutes } from './settings/settings.routes.js'; import { registerEmbedRoutes } from './embed/embed.routes.js'; import { registerCommandRoutes } from './command/command.routes.js'; import { registerPreferenceRoutes } from './preference/preference.routes.js'; import type { UserOnboardingPreferenceRepository } from './preference/types.js'; import { ReadinessService } from './settings/readiness.service.js'; import { registerReadinessRoutes } from './settings/readiness.routes.js'; import { registerToolRoutes } from './tool/tool.routes.js'; import { registerAuditLogRoutes } from './audit/audit.routes.js'; import { registerLlmCallRoutes } from './usage/llm-call.routes.js'; import { registerLlmUsageAdminRoutes } from './usage/llm-usage-admin.routes.js'; import { registerUploadRoutes } from './config/upload.routes.js'; import { registerSessionRoutes } from './session/session.routes.js'; import { registerSessionShareRoutes } from './session/session-share.routes.js'; import { MysqlSessionShareRepository } from './session/session-share.repository.js'; import { SessionShareService } from './session/session-share.service.js'; import { SessionExportService } from './session/session-export.service.js'; import { registerAdminSessionRoutes } from './session/admin-session.routes.js'; import type { FailedSessionRecoveryService } from './session/failed-session-recovery.service.js'; import { registerAdminSessionFilesRoutes } from './session/admin-session-files.routes.js'; import { registerTerminalRoutes } from './session/terminal.routes.js'; import { WorkspaceWriteService } from './file/workspace-write.service.js'; import { WorkspaceOverviewService } from './file/workspace-overview.service.js'; import { registerFileRoutes } from './file/file.routes.js'; import { registerOssRoutes } from './oss/oss.routes.js'; import { registerSkillBundleRoutes } from './skill/skill-bundle.routes.js'; import { registerSkillRoutes } from './skill/skill.routes.js'; import { registerScheduledTaskRoutes } from './schedule/scheduled-task.routes.js'; import { registerOpenApiRoutes } from './openapi/open.routes.js'; import { registerOpenApiCallLogRoutes } from './openapi/open-api-call-log.routes.js'; import { registerSignalWatcherRoutes } from './signal/watcher.routes.js'; import { registerSignalAdminRoutes } from './signal/signal-admin.routes.js'; import { registerAnalyticsRoutes } from './analytics/analytics.routes.js'; import { registerStatisticsRoutes } from './statistics/statistics.routes.js'; import { registerFeedbackRoutes } from './feedback/feedback.routes.js'; import { registerMemoryRoutes } from './memory/memory.routes.js'; import { registerApprovalRuleRoutes, type ApprovalRuleAuditInput } from './approval-rule/approval-rule.routes.js'; import { registerApprovalRuleAdminRoutes } from './approval-rule/approval-rule.admin.routes.js'; import { registerInboxRoutes } from './inbox/inbox.routes.js'; import { registerDigestRoutes } from './digest/routes.js'; import { registerBudgetRoutes } from './budget/budget.routes.js'; import { registerAdminAnalyticsRoutes, registerAdminRuntimeRoutes } from './admin/admin.routes.js'; import { registerAdminSystemCommandRoutes } from './command/admin-system-command.routes.js'; import { registerMcpServerRoutes } from './harness/mcp/controller/mcp-server.routes.js'; import { registerWeixinBotRoutes } from './weixin/weixin.routes.js'; import { registerFeishuBotRoutes } from './feishu/admin.routes.js'; import { registerFeishuBindingRoutes } from './feishu/binding.routes.js'; import { registerTaskNotificationPreferenceRoutes } from './notification/task/preference.routes.js'; import { attachWebSocket } from './session/ws/attach-websocket.js'; export interface RouteDeps { app: FastifyInstance; db: Db; cfg: AppConfig; apiPrefix: string; jwt: JwtService; agentRuntimeCfg: { wsIdleTimeoutMs: number }; // auth authService: AuthService; feishu: FeishuAuthService; ecpAuth: EcpAuthService; userService: UserService; userRepo: MysqlUserRepository; permissionService: PermissionService; gitCredentials: GitCredentialService; envVariableService: EnvVariableService; sessionRepo: SessionRepository; // http-tool httpToolService: HttpToolService; httpApiExecutor: HttpApiExecutor; httpToolStatsStore: AdminAnalyticsDbStore; // agent fileService: FileService; agentService: AgentService; experienceService: AgentExperienceService; experienceIndexService: ExperienceIndexService; suggestedQuestionService: AgentSuggestedQuestionService; mcpValidator: McpServerValidatorImpl; httpToolValidator: HttpToolValidatorImpl; // model modelService: ModelService; // settings settingService: SystemSettingService; activateContainerRuntime: () => Promise; runtimeModeResolver: RuntimeModeResolver; /** 容器 /etc/hosts 注入配置解析器(后台预览 + 保存后缓存失效);未注入时预览接口不注册。 */ containerHostsResolver?: ContainerHostsResolver; /** 容器自动升级总开关解析器(V178):保存后失效缓存,后台切换即时生效。 */ containerAutoUpgradeResolver?: ContainerAutoUpgradeResolver; /** 容器运维(列表 / 单用户重建);未注入时不注册容器管理接口。 */ containerOps?: ContainerOps; // command commandService: UserCommandService; commandRepo: MysqlUserCommandRepository; skillLoader: SkillLoader; skillSync: SkillSyncService; // preference weixinPref: UserWeixinPreferenceService; taskPanelPref: UserTaskPanelPreferenceService; onboardingPrefRepo: UserOnboardingPreferenceRepository; // tool restToolService: ToolService; // audit auditService: AuditLogService; // llm-call llmCallService: LlmCallService; // session sessionService: SessionService; activityService: ActivityService; messageQueueService: MessageQueueService; agentRepo: MysqlAgentRepository; modelRepo: MysqlLlmModelRepository; todoRepo: SessionTodoRepository; pathSandbox: PathSandbox; subagentExecutionRepo: SubagentExecutionRepository; sessionCompactionEventService: SessionCompactionEventService; sessionCompactionService: SessionCompactionService; runTraceService: RunTraceService; runTraceRecordService: RunTraceRecordService; approvalRegistry: ApprovalRegistry; askUserQuestionsRegistry: AskUserQuestionsRegistry; /** 正在执行的工具调用(内存态),管理后台会话详情补进行中卡片 */ inFlightToolRegistry: InFlightToolRegistry; /** 失败会话批量恢复:可选,未注入时管理端入口返回空清单/报错。 */ failedSessionRecovery?: FailedSessionRecoveryService; /** LOCAL 设备在线判定(技术方案 2026-11-05 §4.1-7):会话 VO 的 targetDeviceOnline 快照用。 */ hasDeviceConnection?: (userId: number, deviceId: string) => boolean; /** 当前执行连接的 clientType(electron / cli);会话 VO 的 executorClientType 快照用。 */ getExecutorClientType?: (userId: number, deviceId: string) => string | null; treeSignalPublisher: SessionTreeSignalPublisher; workspaceBrowse: WorkspaceBrowseService; terminalManager: TerminalManager; workspaceGit: WorkspaceGitService; gitCommitMsg: GitCommitMessageService; gitWrite: GitWriteOperationService; runtimeResolver: RuntimeDataResolver; // file ossSts: OssStsService; // skill skillBundleService: SkillBundleService; userSkillService: UserSkillService; skillDocService: SkillDocService; // schedule scheduledService: ScheduledTaskService; // openapi openRunService: OpenRunService; apiTokenService: ApiTokenService; webhookTriggerService: WebhookTriggerService; outboundSubscriptionService: OutboundSubscriptionService; openApiRateLimiter: FixedWindowRateLimiter; callLogService: OpenApiCallLogService; // signal watcherService: WatcherService; signalWatcherRepo: MysqlSignalWatcherRepository; // analytics analyticsService: AnalyticsService; statisticsService: StatisticsService; adminAnalytics: AdminAnalyticsService; // feedback feedbackService: FeedbackService; // memory memoryService: MemoryService; // approval-rule approvalRuleService: ApprovalRuleService; // inbox inboxService: InboxService; // digest digestService: DigestService; // budget budgetService: BudgetService; // mcp mcpServerService: McpServerService; mcpClient: McpClientManager; mcpPref: UserMcpPreferenceService; userMcpLoadModeService: UserMcpLoadModeService; mcpLoadModeResolver: McpLoadModeResolver; // weixin qrLogin: QrLoginService; weixinAccounts: WeixinAccountRepository; weixinMonitor: WeixinMonitorService; // feishu feishuBots: MysqlFeishuBotRepository; feishuBinding: MysqlFeishuBindingRepository; feishuMonitor: FeishuMonitorService; // dingtalk dingtalk: { registerRoutes(api: FastifyInstance): void }; // notification notifPref: TaskNotificationPreferenceService; // ws wsHandler: StreamingWsHandler; terminalWsHandler: TerminalWsHandler; } export async function registerAllRoutes(deps: RouteDeps): Promise { const { app, db, cfg, apiPrefix, jwt, agentRuntimeCfg, authService, feishu, ecpAuth, userService, userRepo, permissionService, gitCredentials, envVariableService, sessionRepo, httpToolService, httpApiExecutor, httpToolStatsStore, httpToolValidator, fileService, agentService, experienceService, experienceIndexService, suggestedQuestionService, mcpValidator, modelService, settingService, activateContainerRuntime, runtimeModeResolver, containerHostsResolver, containerAutoUpgradeResolver, containerOps, commandService, commandRepo, skillLoader, skillSync, weixinPref, taskPanelPref, restToolService, auditService, llmCallService, sessionService, activityService, messageQueueService, agentRepo, modelRepo, todoRepo, pathSandbox, subagentExecutionRepo, sessionCompactionEventService, sessionCompactionService, runTraceService, runTraceRecordService, approvalRegistry, askUserQuestionsRegistry, inFlightToolRegistry, treeSignalPublisher, workspaceBrowse, terminalManager, workspaceGit, gitCommitMsg, gitWrite, runtimeResolver, ossSts, skillBundleService, userSkillService, skillDocService, scheduledService, openRunService, apiTokenService, webhookTriggerService, outboundSubscriptionService, openApiRateLimiter, callLogService, watcherService, signalWatcherRepo, analyticsService, statisticsService, adminAnalytics, feedbackService, memoryService, approvalRuleService, inboxService, digestService, budgetService, mcpServerService, mcpClient, mcpPref, userMcpLoadModeService, mcpLoadModeResolver, qrLogin, weixinAccounts, weixinMonitor, feishuBots, feishuBinding, feishuMonitor, dingtalk, notifPref, wsHandler, terminalWsHandler, } = deps; await app.register(async (api) => { api.get('/swagger-ui.html', async (_req, reply) => reply.redirect(`${apiPrefix}/swagger-ui`)); api.get('/v3/api-docs', async (_req, reply) => { return reply.send(app.swagger()); }); api.get('/v1/health', async (_req, reply) => sendOk(reply, { status: 'UP' })); registerAuthRoutes(api, authService, feishu, ecpAuth); registerEcpAuthRoutes(api, ecpAuth); registerCompanySsoRoutes(api, new CompanySsoService( new CompanySsoClient(), new CompanySsoIdentityRepository(db), jwt, ), settingService, async (event) => { await auditService.record({ action: event.action === 'created' ? 'CREATE' : event.action === 'bound' ? 'UPDATE' : 'LOGIN', objectType: 'sso.identity', objectId: event.userId == null ? null : String(event.userId), userId: event.userId, method: 'POST', path: '/v1/auth/sso/exchange', ip: event.ip, status: event.outcome === 'success' ? 200 : new CompanySsoError(event.outcome).status, success: event.outcome === 'success' ? 1 : 0, errorMessage: event.outcome === 'success' ? null : event.outcome, queryString: JSON.stringify({ requestId: event.requestId, provider: event.provider, durationMs: event.durationMs }), }); }); registerUserRoutes(api, userService, userRepo, permissionService, containerOps); if (containerOps) { registerAdminContainerRoutes(api, { containerOps, permissionService, userLookup: { findByIds: (ids) => userRepo.findByIds(ids) }, agentLookup: { findByIds: (ids) => agentRepo.findByIds(ids) }, }); } registerPermissionRoutes(api, permissionService); registerGitCredentialRoutes(api, gitCredentials); registerAdminGitCredentialRoutes(api, { gitCredentialService: gitCredentials, permissionService }); registerEnvVariableRoutes(api, { envVariableService, sessionLookup: sessionRepo }); registerAdminEnvVariableRoutes(api, { envVariableService, permissionService, userLookup: { findById: (id: number) => userRepo.findById(id) }, }); registerAdminHttpToolRoutes(api, { service: httpToolService, stats: httpToolStatsStore, httpApiExecutor, permissionService, }); registerAgentAvatarRoutes(api, fileService, permissionService); registerAgentRoutes(api, { agentService, experienceService, suggestedQuestionService, userRepo, mcpServerValidator: mcpValidator, httpToolValidator, permissionService, }); registerAgentExperienceLibraryRoutes(api, { agentService, experienceIndexService, permissionService, }); registerModelRoutes(api, { modelService, permissionService }); registerSystemSettingRoutes(api, { systemSettingService: settingService, permissionService, adminEmailLookup: { findEmailById: (userId) => userRepo.findEmailById(userId) }, containerHostsPreview: containerHostsResolver ? () => containerHostsResolver.preview() : undefined, beforeSave: async (items) => { await validateRuntimeModeSave(items, activateContainerRuntime); }, afterSave: (keys) => { if (keys.includes(RUNTIME_MODE_SETTING_KEY)) runtimeModeResolver.invalidate(); if (containerHostsResolver && keys.some((key) => key === CONTAINER_INHERIT_HOST_HOSTS_KEY || key === CONTAINER_EXTRA_HOSTS_KEY)) { containerHostsResolver.invalidate(); } // 容器自动升级总开关(V178):保存后立即生效,无需等 5s TTL if (containerAutoUpgradeResolver && keys.includes(CONTAINER_AUTO_UPGRADE_SETTING_KEY)) { containerAutoUpgradeResolver.invalidate(); } }, }); registerEmbedRoutes(api, { systemSettingService: settingService, agentService }); registerCommandRoutes(api, { userCommandService: commandService, agentService, skillLoader, skillSyncService: skillSync as never, }); registerPreferenceRoutes(api, { weixinPreferenceService: weixinPref, taskPanelPreferenceService: taskPanelPref, weixinVoiceReplyDefault: cfg.weixin.bot.voiceReply, }); const readinessService = new ReadinessService({ userLookup: { findById: (id) => userRepo.findById(id) }, modelsConfiguredCount: () => modelRepo.countEnabledWithApiKey(), agentsEnabledCount: async () => (await agentRepo.selectList(null, false)).length, sessionHistory: sessionRepo, onboardingPreference: deps.onboardingPrefRepo, }); registerReadinessRoutes(api, { readinessService, permissionService }); registerToolRoutes(api, { toolService: restToolService }); registerAuditLogRoutes(api, { auditLogService: auditService, permissionService }); registerLlmCallRoutes(api, { llmCallService, permissionService }); registerLlmUsageAdminRoutes(api, { llmCallService, permissionService, userLookup: { findById: (id: number) => userRepo.findById(id) }, }); registerUploadRoutes(api, () => settingService.getUploadConfig()); registerSessionRoutes(api, { sessionService, activityService, messageQueueService, agentLookup: { findById: (id: number) => agentRepo.findById(id), findByIds: (ids: number[]) => agentRepo.findByIds(ids), requireDefaultAgent: () => agentService.requireDefaultAgent(), listOptions: async () => (await agentRepo.selectList(null, true)).map((a) => ({ id: a.id!, name: a.name })), } as never, modelLookup: { findById: (id: number) => modelRepo.findById(id), findByIds: (ids: number[]) => modelRepo.findByIds(ids), findDefault: () => modelRepo.findDefault(), } as never, todoRepo, pathSandbox, subagentExecutionRepo, sessionCompactionEventService, sessionCompactionService, runTraceService, runTraceRecordService, approvalRegistry, askUserQuestionsRegistry, treeSignalPublisher, existsSessionUnderWorkspace: (userId: number, path: string) => sessionRepo.existsSessionUnderWorkspace(userId, path), hasDeviceConnection: deps.hasDeviceConnection, getExecutorClientType: deps.getExecutorClientType, }); const sessionShareService = new SessionShareService( new MysqlSessionShareRepository(db), sessionService, userRepo, { findById: (id: number) => agentRepo.findById(id), findByIds: (ids: number[]) => agentRepo.findByIds(ids), requireDefaultAgent: () => agentService.requireDefaultAgent(), listOptions: async () => (await agentRepo.selectList(null, true)).map((a) => ({ id: a.id!, name: a.name })), } as never, auditService, () => settingService.shareTokenLinksEnabled(), ); registerSessionShareRoutes(api, { sessionService, shareService: sessionShareService, exportService: new SessionExportService(sessionService, { findById: (id: number) => agentRepo.findById(id), findByIds: (ids: number[]) => agentRepo.findByIds(ids), requireDefaultAgent: () => agentService.requireDefaultAgent(), listOptions: async () => (await agentRepo.selectList(null, true)).map((a) => ({ id: a.id!, name: a.name })), } as never), tokenLinksEnabled: () => settingService.shareTokenLinksEnabled(), }); registerAdminSessionRoutes(api, { sessionService, userLookup: { findByIds: (ids: number[]) => userRepo.findByIds(ids), listOptions: () => userRepo.listOptions(), } as never, agentLookup: { findById: (id: number) => agentRepo.findById(id), findByIds: (ids: number[]) => agentRepo.findByIds(ids), requireDefaultAgent: () => agentService.requireDefaultAgent(), listOptions: async () => (await agentRepo.selectList(null, true)).map((a) => ({ id: a.id!, name: a.name })), } as never, modelLookup: { findById: (id: number) => modelRepo.findById(id), findByIds: (ids: number[]) => modelRepo.findByIds(ids), findDefault: () => modelRepo.findDefault(), } as never, permissionService, askUserQuestionsRegistry, inFlightToolRegistry, failedSessionRecovery: deps.failedSessionRecovery, }); const experienceDirForAgent = (agentId: number) => experienceIndexService.dirFor(agentId); registerAdminSessionFilesRoutes(api, { sessionService, workspaceBrowseService: workspaceBrowse, permissionService, experienceDirForAgent, }); registerTerminalRoutes(api, { terminalManager, sessionService, permissionService, userLookup: { findById: (id: number) => userRepo.findById(id) as Promise<{ username: string } | null> }, }); const workspaceWrite = new WorkspaceWriteService(pathSandbox, activityService); const workspaceOverviewService = new WorkspaceOverviewService(pathSandbox, sessionRepo); registerFileRoutes(api, { fileService, sessionService, workspaceBrowseService: workspaceBrowse, workspaceGitService: workspaceGit, gitCommitMessageService: gitCommitMsg, gitWriteOperationService: gitWrite, workspaceWriteService: workspaceWrite, workspaceOverviewService, pathSandbox, experienceDirForAgent, getUploadBaseUrl: async () => (await settingService.getUploadConfig()).baseUrl, runtimeDataResolver: runtimeResolver, }); registerOssRoutes(api, { ossStsService: ossSts }); registerSkillBundleRoutes(api, { skillBundleService, permissionService }); registerSkillRoutes(api, { userSkillService, skillDocService, skillSyncService: skillSync, sessionService, agentService, agentLookup: { findById: (id: number) => agentRepo.findById(id), findByIds: (ids: number[]) => agentRepo.findByIds(ids), requireDefaultAgent: () => agentService.requireDefaultAgent(), listOptions: async () => (await agentRepo.selectList(null, true)).map((a) => ({ id: a.id!, name: a.name })), } as never, permissionService, userLookup: { findByIds: async (ids: number[]) => (await userRepo.findByIds(ids)).map((u) => ({ id: u.id!, username: u.username, displayName: u.displayName, })), listOptions: async () => (await userRepo.listOptions()).map((u) => ({ id: u.id!, username: u.username, displayName: u.displayName, })), }, }); registerScheduledTaskRoutes(api, { service: scheduledService, jwt, permission: permissionService }); registerOpenApiRoutes(api, { apiPrefix, openRun: openRunService, apiTokenService, triggerService: webhookTriggerService, subscriptionService: outboundSubscriptionService, rateLimiter: openApiRateLimiter, callLog: callLogService, }); registerOpenApiCallLogRoutes(api, { callLog: callLogService, permissionService }); registerSignalWatcherRoutes(api, { service: watcherService }); registerSignalAdminRoutes(api, { watcherRepo: signalWatcherRepo, service: watcherService, permissionService, userLookup: { findByIds: async (ids: number[]) => (await userRepo.findByIds(ids)).map((u) => ({ id: u.id, username: u.username, displayName: u.displayName, })), }, }); registerAnalyticsRoutes(api, { analytics: analyticsService, jwt, permissionService }); registerStatisticsRoutes(api, { statistics: statisticsService, jwt, permissionService }); registerFeedbackRoutes(api, { feedback: feedbackService, jwt, permissionService }); registerMemoryRoutes(api, { memoryService }); registerApprovalRuleRoutes(api, { approvalRuleService, audit: (input: ApprovalRuleAuditInput) => { void auditService.record({ action: input.action, objectType: 'approval.rule', objectId: String(input.objectId), method: input.request.method, path: input.request.url, userId: (input.request as { userId?: number }).userId ?? null, username: null, ip: input.request.ip ?? null, status: 200, success: 1, errorMessage: null, queryString: input.detail, }).catch((e) => console.error('Failed to record approval rule audit log', e)); }, }); registerApprovalRuleAdminRoutes(api, { approvalRuleService, permissionService }); registerInboxRoutes(api, { inboxService }); registerDigestRoutes(api, { service: digestService }); registerBudgetRoutes(api, { budgetService, permissionService }); const adminDeps = { jwt, analytics: adminAnalytics, sessionLister: sessionService as never, permissionService, memoryAuditor: memoryService, memoryUserLookup: { findByIds: async (ids: number[]) => (await userRepo.findByIds(ids)).map((u) => ({ id: u.id!, username: u.username, displayName: u.displayName, })), }, }; registerAdminAnalyticsRoutes(api, adminDeps); registerAdminRuntimeRoutes(api, adminDeps); registerAdminSystemCommandRoutes(api, { commandRepo, permissionService, userLookup: { findByIds: async (ids: number[]) => (await userRepo.findByIds(ids)).map((u) => ({ id: u.id!, username: u.username, displayName: u.displayName, })), }, }); registerMcpServerRoutes(api, { mcpServerService, mcpClientManager: mcpClient, userMcpPreferenceService: mcpPref, userMcpLoadModeService, mcpLoadModeResolver, permissionService, }); registerWeixinBotRoutes(api, { jwt, qrLogin, accountRepository: weixinAccounts, monitorService: weixinMonitor }); registerFeishuBotRoutes(api, { repository: feishuBots, secretKey: cfg.feishu.bot.appSecretKey, permissionService, monitorStatus: { getStatus: (botId: number) => feishuMonitor.getStatus(botId) }, monitorReconnect: { reconnect: (botId: number) => feishuMonitor.reconnect(botId) }, }); registerFeishuBindingRoutes(api, { jwt, repository: feishuBinding, auth: feishu }); dingtalk.registerRoutes(api); registerTaskNotificationPreferenceRoutes(api, { preference: notifPref, jwt }); await attachWebSocket(api, { handler: wsHandler, idleTimeoutMs: agentRuntimeCfg.wsIdleTimeoutMs, terminalHandler: terminalWsHandler, }); }, { prefix: apiPrefix }); }