@cloudcli-ai/cloudcli 1.29.5 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.tr.md +252 -0
- package/README.zh-CN.md +1 -1
- package/dist/assets/index-DF8TTVHl.css +32 -0
- package/dist/assets/index-DbZAcc9z.js +1262 -0
- package/dist/index.html +2 -2
- package/dist-server/server/claude-sdk.js +13 -8
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +7 -7
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +5 -5
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/gemini-response-handler.js +2 -2
- package/dist-server/server/gemini-response-handler.js.map +1 -1
- package/dist-server/server/index.js +139 -977
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/middleware/auth.js +1 -1
- package/dist-server/server/middleware/auth.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +125 -0
- package/dist-server/server/modules/database/connection.js.map +1 -0
- package/dist-server/server/modules/database/index.js +13 -0
- package/dist-server/server/modules/database/index.js.map +1 -0
- package/dist-server/server/modules/database/init-db.js +18 -0
- package/dist-server/server/modules/database/init-db.js.map +1 -0
- package/dist-server/server/modules/database/migrations.js +370 -0
- package/dist-server/server/modules/database/migrations.js.map +1 -0
- package/dist-server/server/modules/database/repositories/api-keys.js +72 -0
- package/dist-server/server/modules/database/repositories/api-keys.js.map +1 -0
- package/dist-server/server/modules/database/repositories/app-config.js +47 -0
- package/dist-server/server/modules/database/repositories/app-config.js.map +1 -0
- package/dist-server/server/modules/database/repositories/credentials.js +68 -0
- package/dist-server/server/modules/database/repositories/credentials.js.map +1 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js +54 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js.map +1 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js +72 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js +64 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.js +160 -0
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js +49 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js.map +1 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js +31 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +109 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/users.js +88 -0
- package/dist-server/server/modules/database/repositories/users.js.map +1 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js +38 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +143 -0
- package/dist-server/server/modules/database/schema.js.map +1 -0
- package/dist-server/server/modules/projects/index.js +4 -0
- package/dist-server/server/modules/projects/index.js.map +1 -0
- package/dist-server/server/modules/projects/projects.routes.js +188 -0
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +219 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js +65 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js +93 -0
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-star.service.js +60 -0
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js +167 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +169 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +126 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js +85 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js +95 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js +87 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/index.js +4 -0
- package/dist-server/server/modules/providers/index.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +103 -0
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js +103 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +63 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +403 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +15 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +84 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js +107 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +71 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +494 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +15 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +118 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js +80 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +127 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +369 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +15 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +131 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +82 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +296 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +474 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +15 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -0
- package/dist-server/server/modules/providers/provider.registry.js +31 -0
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -0
- package/dist-server/server/modules/providers/provider.routes.js +331 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -0
- package/dist-server/server/modules/providers/services/mcp.service.js +69 -0
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js +25 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +868 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +56 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +234 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions.service.js +104 -0
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js +14 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js +102 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +250 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -0
- package/dist-server/server/modules/websocket/index.js +3 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +188 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +52 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +334 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js +36 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js +14 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js.map +1 -0
- package/dist-server/server/openai-codex.js +5 -5
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/routes/agent.js +10 -17
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/auth.js +3 -1
- package/dist-server/server/routes/auth.js.map +1 -1
- package/dist-server/server/routes/commands.js +1 -45
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/cursor.js +9 -489
- package/dist-server/server/routes/cursor.js.map +1 -1
- package/dist-server/server/routes/gemini.js +2 -2
- package/dist-server/server/routes/gemini.js.map +1 -1
- package/dist-server/server/routes/git.js +33 -29
- package/dist-server/server/routes/git.js.map +1 -1
- package/dist-server/server/routes/mcp-utils.js +1 -18
- package/dist-server/server/routes/mcp-utils.js.map +1 -1
- package/dist-server/server/routes/settings.js +11 -1
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +97 -569
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/dist-server/server/routes/user.js +1 -1
- package/dist-server/server/routes/user.js.map +1 -1
- package/dist-server/server/services/notification-orchestrator.js +2 -2
- package/dist-server/server/services/notification-orchestrator.js.map +1 -1
- package/dist-server/server/services/vapid-keys.js +2 -1
- package/dist-server/server/services/vapid-keys.js.map +1 -1
- package/dist-server/server/shared/interfaces.js +2 -0
- package/dist-server/server/shared/interfaces.js.map +1 -0
- package/dist-server/server/shared/types.js +2 -0
- package/dist-server/server/shared/types.js.map +1 -0
- package/dist-server/server/shared/utils.js +551 -0
- package/dist-server/server/shared/utils.js.map +1 -0
- package/dist-server/server/utils/mcp-detector.js +0 -49
- package/dist-server/server/utils/mcp-detector.js.map +1 -1
- package/dist-server/server/utils/taskmaster-websocket.js +18 -12
- package/dist-server/server/utils/taskmaster-websocket.js.map +1 -1
- package/dist-server/shared/modelConstants.js +3 -1
- package/dist-server/shared/modelConstants.js.map +1 -1
- package/package.json +7 -2
- package/server/claude-sdk.js +14 -8
- package/server/cursor-cli.js +7 -7
- package/server/gemini-cli.js +5 -5
- package/server/gemini-response-handler.js +2 -2
- package/server/index.js +173 -1035
- package/server/middleware/auth.js +1 -1
- package/server/modules/database/connection.ts +143 -0
- package/server/modules/database/index.ts +12 -0
- package/server/modules/database/init-db.ts +17 -0
- package/server/modules/database/migrations.ts +442 -0
- package/server/modules/database/repositories/api-keys.ts +119 -0
- package/server/modules/database/repositories/app-config.ts +53 -0
- package/server/modules/database/repositories/credentials.ts +106 -0
- package/server/modules/database/repositories/github-tokens.ts +100 -0
- package/server/modules/database/repositories/notification-preferences.ts +103 -0
- package/server/modules/database/repositories/projects.db.integration.test.ts +72 -0
- package/server/modules/database/repositories/projects.db.ts +183 -0
- package/server/modules/database/repositories/push-subscriptions.ts +80 -0
- package/server/modules/database/repositories/scan-state.db.ts +42 -0
- package/server/modules/database/repositories/sessions.db.ts +174 -0
- package/server/modules/database/repositories/users.ts +140 -0
- package/server/modules/database/repositories/vapid-keys.ts +57 -0
- package/server/modules/database/schema.ts +152 -0
- package/server/modules/projects/index.ts +6 -0
- package/server/modules/projects/projects.routes.ts +247 -0
- package/server/modules/projects/services/project-clone.service.ts +321 -0
- package/server/modules/projects/services/project-delete.service.ts +75 -0
- package/server/modules/projects/services/project-management.service.ts +150 -0
- package/server/modules/projects/services/project-star.service.ts +78 -0
- package/server/modules/projects/services/projects-has-taskmaster.service.ts +248 -0
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +285 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +183 -0
- package/server/modules/projects/tests/project-management.service.test.ts +117 -0
- package/server/modules/projects/tests/project-star.service.test.ts +123 -0
- package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +105 -0
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +123 -0
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +110 -0
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +475 -0
- package/server/modules/providers/list/claude/claude.provider.ts +17 -0
- package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -0
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -0
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +119 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +564 -0
- package/server/modules/providers/list/codex/codex.provider.ts +17 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -0
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -0
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +176 -0
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +17 -0
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +151 -0
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -0
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +401 -0
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +541 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +17 -0
- package/server/modules/providers/provider.registry.ts +36 -0
- package/server/modules/providers/provider.routes.ts +425 -0
- package/server/modules/providers/services/mcp.service.ts +94 -0
- package/server/modules/providers/services/provider-auth.service.ts +26 -0
- package/server/modules/providers/services/session-conversations-search.service.ts +1167 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +74 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +283 -0
- package/server/modules/providers/services/sessions.service.ts +130 -0
- package/server/modules/providers/shared/base/abstract.provider.ts +27 -0
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -0
- package/server/modules/providers/tests/mcp.test.ts +293 -0
- package/server/modules/websocket/README.md +267 -0
- package/server/modules/websocket/index.ts +2 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +271 -0
- package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +453 -0
- package/server/modules/websocket/services/websocket-auth.service.ts +54 -0
- package/server/modules/websocket/services/websocket-server.service.ts +58 -0
- package/server/modules/websocket/services/websocket-state.service.ts +16 -0
- package/server/modules/websocket/services/websocket-writer.service.ts +38 -0
- package/server/openai-codex.js +5 -5
- package/server/routes/agent.js +11 -16
- package/server/routes/auth.js +4 -2
- package/server/routes/commands.js +1 -50
- package/server/routes/cursor.js +15 -527
- package/server/routes/gemini.js +3 -2
- package/server/routes/git.js +33 -28
- package/server/routes/mcp-utils.js +2 -19
- package/server/routes/settings.js +11 -1
- package/server/routes/taskmaster.js +107 -602
- package/server/routes/user.js +1 -1
- package/server/services/notification-orchestrator.js +3 -2
- package/server/services/vapid-keys.js +2 -1
- package/server/shared/interfaces.ts +92 -0
- package/server/shared/types.ts +327 -0
- package/server/shared/utils.ts +669 -0
- package/server/tsconfig.json +6 -3
- package/server/utils/mcp-detector.js +0 -51
- package/server/utils/taskmaster-websocket.js +19 -13
- package/shared/modelConstants.js +3 -1
- package/dist/assets/index-BBAE1OJ_.css +0 -32
- package/dist/assets/index-Ci1GOyV7.js +0 -1262
- package/dist-server/server/database/db.js +0 -535
- package/dist-server/server/database/db.js.map +0 -1
- package/dist-server/server/database/schema.js +0 -97
- package/dist-server/server/database/schema.js.map +0 -1
- package/dist-server/server/projects.js +0 -2325
- package/dist-server/server/projects.js.map +0 -1
- package/dist-server/server/providers/claude/adapter.js +0 -266
- package/dist-server/server/providers/claude/adapter.js.map +0 -1
- package/dist-server/server/providers/claude/status.js +0 -121
- package/dist-server/server/providers/claude/status.js.map +0 -1
- package/dist-server/server/providers/codex/adapter.js +0 -232
- package/dist-server/server/providers/codex/adapter.js.map +0 -1
- package/dist-server/server/providers/codex/status.js +0 -70
- package/dist-server/server/providers/codex/status.js.map +0 -1
- package/dist-server/server/providers/cursor/adapter.js +0 -342
- package/dist-server/server/providers/cursor/adapter.js.map +0 -1
- package/dist-server/server/providers/cursor/status.js +0 -118
- package/dist-server/server/providers/cursor/status.js.map +0 -1
- package/dist-server/server/providers/gemini/adapter.js +0 -174
- package/dist-server/server/providers/gemini/adapter.js.map +0 -1
- package/dist-server/server/providers/gemini/status.js +0 -104
- package/dist-server/server/providers/gemini/status.js.map +0 -1
- package/dist-server/server/providers/registry.js +0 -58
- package/dist-server/server/providers/registry.js.map +0 -1
- package/dist-server/server/providers/types.js +0 -117
- package/dist-server/server/providers/types.js.map +0 -1
- package/dist-server/server/providers/utils.js +0 -28
- package/dist-server/server/providers/utils.js.map +0 -1
- package/dist-server/server/routes/cli-auth.js +0 -25
- package/dist-server/server/routes/cli-auth.js.map +0 -1
- package/dist-server/server/routes/codex.js +0 -294
- package/dist-server/server/routes/codex.js.map +0 -1
- package/dist-server/server/routes/mcp.js +0 -480
- package/dist-server/server/routes/mcp.js.map +0 -1
- package/dist-server/server/routes/messages.js +0 -56
- package/dist-server/server/routes/messages.js.map +0 -1
- package/dist-server/server/routes/projects.js +0 -505
- package/dist-server/server/routes/projects.js.map +0 -1
- package/server/database/db.js +0 -593
- package/server/database/schema.js +0 -102
- package/server/projects.js +0 -2555
- package/server/providers/claude/adapter.js +0 -278
- package/server/providers/claude/status.js +0 -136
- package/server/providers/codex/adapter.js +0 -248
- package/server/providers/codex/status.js +0 -78
- package/server/providers/cursor/adapter.js +0 -348
- package/server/providers/cursor/status.js +0 -128
- package/server/providers/gemini/adapter.js +0 -186
- package/server/providers/gemini/status.js +0 -111
- package/server/providers/registry.js +0 -67
- package/server/providers/types.js +0 -132
- package/server/providers/utils.js +0 -29
- package/server/routes/cli-auth.js +0 -27
- package/server/routes/codex.js +0 -329
- package/server/routes/mcp.js +0 -552
- package/server/routes/messages.js +0 -61
- package/server/routes/projects.js +0 -548
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { scanStateDb } from '@/modules/database/index.js';
|
|
2
|
+
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
3
|
+
import type { LLMProvider } from '@/shared/types.js';
|
|
4
|
+
|
|
5
|
+
type SessionSynchronizeResult = {
|
|
6
|
+
processedByProvider: Record<LLMProvider, number>;
|
|
7
|
+
failures: string[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Orchestrates provider-specific session indexers and indexed-session lifecycle operations.
|
|
12
|
+
*/
|
|
13
|
+
export const sessionSynchronizerService = {
|
|
14
|
+
/**
|
|
15
|
+
* Runs all provider synchronizers and updates scan_state.last_scanned_at.
|
|
16
|
+
*/
|
|
17
|
+
async synchronizeSessions(): Promise<SessionSynchronizeResult> {
|
|
18
|
+
const lastScanAt = scanStateDb.getLastScannedAt();
|
|
19
|
+
const scanBoundary = new Date();
|
|
20
|
+
const processedByProvider: Record<LLMProvider, number> = {
|
|
21
|
+
claude: 0,
|
|
22
|
+
codex: 0,
|
|
23
|
+
cursor: 0,
|
|
24
|
+
gemini: 0,
|
|
25
|
+
};
|
|
26
|
+
const failures: string[] = [];
|
|
27
|
+
|
|
28
|
+
const results = await Promise.allSettled(
|
|
29
|
+
providerRegistry.listProviders().map(async (provider) => ({
|
|
30
|
+
provider: provider.id,
|
|
31
|
+
processed: await provider.sessionSynchronizer.synchronize(lastScanAt ?? undefined),
|
|
32
|
+
}))
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
for (const result of results) {
|
|
36
|
+
if (result.status === 'fulfilled') {
|
|
37
|
+
processedByProvider[result.value.provider] = result.value.processed;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
42
|
+
failures.push(reason);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (failures.length === 0) {
|
|
46
|
+
scanStateDb.updateLastScannedAt(scanBoundary);
|
|
47
|
+
} else {
|
|
48
|
+
console.warn(
|
|
49
|
+
`[Sessions] Skipping scan_state cursor advance because ${failures.length} provider sync(s) failed.`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
processedByProvider,
|
|
55
|
+
failures,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Indexes one provider artifact file without running a full provider rescan.
|
|
61
|
+
*/
|
|
62
|
+
async synchronizeProviderFile(
|
|
63
|
+
provider: LLMProvider,
|
|
64
|
+
filePath: string
|
|
65
|
+
): Promise<{ provider: LLMProvider; indexed: boolean; sessionId: string | null }> {
|
|
66
|
+
const resolvedProvider = providerRegistry.resolveProvider(provider);
|
|
67
|
+
const sessionId = await resolvedProvider.sessionSynchronizer.synchronizeFile(filePath);
|
|
68
|
+
return {
|
|
69
|
+
provider,
|
|
70
|
+
indexed: Boolean(sessionId),
|
|
71
|
+
sessionId,
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { promises as fsPromises } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
import chokidar, { type FSWatcher } from 'chokidar';
|
|
6
|
+
|
|
7
|
+
import { sessionSynchronizerService } from '@/modules/providers/services/session-synchronizer.service.js';
|
|
8
|
+
import { WS_OPEN_STATE, connectedClients } from '@/modules/websocket/index.js';
|
|
9
|
+
import type { LLMProvider } from '@/shared/types.js';
|
|
10
|
+
import { getProjectsWithSessions } from '@/modules/projects/index.js';
|
|
11
|
+
|
|
12
|
+
type WatcherEventType = 'add' | 'change';
|
|
13
|
+
|
|
14
|
+
const PROVIDER_WATCH_PATHS: Array<{ provider: LLMProvider; rootPath: string }> = [
|
|
15
|
+
{
|
|
16
|
+
provider: 'claude',
|
|
17
|
+
rootPath: path.join(os.homedir(), '.claude', 'projects'),
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
provider: 'cursor',
|
|
21
|
+
rootPath: path.join(os.homedir(), '.cursor', 'chats'),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
provider: 'codex',
|
|
25
|
+
rootPath: path.join(os.homedir(), '.codex', 'sessions'),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
provider: 'gemini',
|
|
29
|
+
rootPath: path.join(os.homedir(), '.gemini', 'sessions'),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
provider: 'gemini',
|
|
33
|
+
rootPath: path.join(os.homedir(), '.gemini', 'tmp'),
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const WATCHER_IGNORED_PATTERNS = [
|
|
38
|
+
'**/node_modules/**',
|
|
39
|
+
'**/.git/**',
|
|
40
|
+
'**/dist/**',
|
|
41
|
+
'**/build/**',
|
|
42
|
+
'**/*.tmp',
|
|
43
|
+
'**/*.swp',
|
|
44
|
+
'**/.DS_Store',
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const PROJECTS_UPDATE_DEBOUNCE_MS = 500;
|
|
48
|
+
const PROJECTS_UPDATE_MAX_WAIT_MS = 2_000;
|
|
49
|
+
|
|
50
|
+
const watchers: FSWatcher[] = [];
|
|
51
|
+
|
|
52
|
+
type PendingWatcherUpdate = {
|
|
53
|
+
providers: Set<LLMProvider>;
|
|
54
|
+
changeTypes: Set<WatcherEventType>;
|
|
55
|
+
updatedSessionIds: Set<string>;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let pendingWatcherUpdate: PendingWatcherUpdate | null = null;
|
|
59
|
+
let pendingWatcherUpdateStartedAt: number | null = null;
|
|
60
|
+
let pendingWatcherFlushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
61
|
+
let watcherRefreshInFlight = false;
|
|
62
|
+
let watcherRescheduleAfterRefresh = false;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Filters watcher events to provider-specific session artifact file types.
|
|
66
|
+
*/
|
|
67
|
+
function isWatcherTargetFile(provider: LLMProvider, filePath: string): boolean {
|
|
68
|
+
if (provider === 'gemini') {
|
|
69
|
+
return filePath.endsWith('.json') || filePath.endsWith('.jsonl');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return filePath.endsWith('.jsonl');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function clearPendingWatcherFlushTimer(): void {
|
|
76
|
+
if (pendingWatcherFlushTimer) {
|
|
77
|
+
clearTimeout(pendingWatcherFlushTimer);
|
|
78
|
+
pendingWatcherFlushTimer = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function schedulePendingWatcherFlush(): void {
|
|
83
|
+
if (!pendingWatcherUpdate) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
if (pendingWatcherUpdateStartedAt === null) {
|
|
89
|
+
pendingWatcherUpdateStartedAt = now;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const elapsed = now - pendingWatcherUpdateStartedAt;
|
|
93
|
+
const remainingMaxWait = Math.max(0, PROJECTS_UPDATE_MAX_WAIT_MS - elapsed);
|
|
94
|
+
const delay = Math.min(PROJECTS_UPDATE_DEBOUNCE_MS, remainingMaxWait);
|
|
95
|
+
|
|
96
|
+
clearPendingWatcherFlushTimer();
|
|
97
|
+
pendingWatcherFlushTimer = setTimeout(() => {
|
|
98
|
+
void flushPendingWatcherUpdate();
|
|
99
|
+
}, delay);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function queuePendingWatcherUpdate(
|
|
103
|
+
eventType: WatcherEventType,
|
|
104
|
+
provider: LLMProvider,
|
|
105
|
+
updatedSessionId: string | null
|
|
106
|
+
): void {
|
|
107
|
+
if (!pendingWatcherUpdate) {
|
|
108
|
+
pendingWatcherUpdate = {
|
|
109
|
+
providers: new Set<LLMProvider>(),
|
|
110
|
+
changeTypes: new Set<WatcherEventType>(),
|
|
111
|
+
updatedSessionIds: new Set<string>(),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pendingWatcherUpdate.providers.add(provider);
|
|
116
|
+
pendingWatcherUpdate.changeTypes.add(eventType);
|
|
117
|
+
if (updatedSessionId) {
|
|
118
|
+
pendingWatcherUpdate.updatedSessionIds.add(updatedSessionId);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
schedulePendingWatcherFlush();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function flushPendingWatcherUpdate(): Promise<void> {
|
|
125
|
+
clearPendingWatcherFlushTimer();
|
|
126
|
+
|
|
127
|
+
if (!pendingWatcherUpdate) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (watcherRefreshInFlight) {
|
|
132
|
+
watcherRescheduleAfterRefresh = true;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const queuedUpdate = pendingWatcherUpdate;
|
|
137
|
+
pendingWatcherUpdate = null;
|
|
138
|
+
pendingWatcherUpdateStartedAt = null;
|
|
139
|
+
watcherRefreshInFlight = true;
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const updatedProjects = await getProjectsWithSessions({ skipSynchronization: true });
|
|
143
|
+
const changeTypes = Array.from(queuedUpdate.changeTypes);
|
|
144
|
+
const watchProviders = Array.from(queuedUpdate.providers);
|
|
145
|
+
const updatedSessionIds = Array.from(queuedUpdate.updatedSessionIds);
|
|
146
|
+
|
|
147
|
+
// Backward-compatible fields stay populated with the first queued values.
|
|
148
|
+
const updateMessage = JSON.stringify({
|
|
149
|
+
type: 'projects_updated',
|
|
150
|
+
projects: updatedProjects,
|
|
151
|
+
timestamp: new Date().toISOString(),
|
|
152
|
+
changeType: changeTypes[0] ?? 'change',
|
|
153
|
+
updatedSessionId: updatedSessionIds[0] ?? undefined,
|
|
154
|
+
watchProvider: watchProviders[0] ?? undefined,
|
|
155
|
+
changeTypes,
|
|
156
|
+
updatedSessionIds,
|
|
157
|
+
watchProviders,
|
|
158
|
+
batched: true,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
connectedClients.forEach(client => {
|
|
162
|
+
if (client.readyState === WS_OPEN_STATE) {
|
|
163
|
+
client.send(updateMessage);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
168
|
+
console.error('Session watcher refresh failed while broadcasting projects_updated', { error: message });
|
|
169
|
+
} finally {
|
|
170
|
+
watcherRefreshInFlight = false;
|
|
171
|
+
|
|
172
|
+
if (pendingWatcherUpdate || watcherRescheduleAfterRefresh) {
|
|
173
|
+
watcherRescheduleAfterRefresh = false;
|
|
174
|
+
schedulePendingWatcherFlush();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Handles file watcher updates and triggers provider file-level synchronization.
|
|
181
|
+
*/
|
|
182
|
+
async function onUpdate(
|
|
183
|
+
eventType: WatcherEventType,
|
|
184
|
+
filePath: string,
|
|
185
|
+
provider: LLMProvider
|
|
186
|
+
): Promise<void> {
|
|
187
|
+
if (!isWatcherTargetFile(provider, filePath)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const result = await sessionSynchronizerService.synchronizeProviderFile(provider, filePath);
|
|
193
|
+
if (!result.indexed) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
console.log(`Session synchronization triggered by ${eventType} event for provider "${provider}"`, {
|
|
198
|
+
filePath,
|
|
199
|
+
sessionId: result.sessionId,
|
|
200
|
+
});
|
|
201
|
+
queuePendingWatcherUpdate(eventType, provider, result.sessionId);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
204
|
+
console.error(`Session watcher sync failed for provider "${provider}"`, {
|
|
205
|
+
eventType,
|
|
206
|
+
filePath,
|
|
207
|
+
error: message,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Starts provider filesystem watchers and performs initial DB synchronization.
|
|
214
|
+
*/
|
|
215
|
+
export async function initializeSessionsWatcher(): Promise<void> {
|
|
216
|
+
console.log('Setting up session watchers');
|
|
217
|
+
|
|
218
|
+
const initialSync = await sessionSynchronizerService.synchronizeSessions();
|
|
219
|
+
console.log('Initial session synchronization complete', {
|
|
220
|
+
processedByProvider: initialSync.processedByProvider,
|
|
221
|
+
failures: initialSync.failures,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
for (const { provider, rootPath } of PROVIDER_WATCH_PATHS) {
|
|
225
|
+
try {
|
|
226
|
+
await fsPromises.mkdir(rootPath, { recursive: true });
|
|
227
|
+
|
|
228
|
+
const watcher = chokidar.watch(rootPath, {
|
|
229
|
+
ignored: WATCHER_IGNORED_PATTERNS,
|
|
230
|
+
persistent: true,
|
|
231
|
+
ignoreInitial: true,
|
|
232
|
+
followSymlinks: false,
|
|
233
|
+
depth: 6,
|
|
234
|
+
usePolling: true,
|
|
235
|
+
interval: 6_000,
|
|
236
|
+
binaryInterval: 6_000,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
watcher
|
|
240
|
+
.on('add', (filePath: string) => {
|
|
241
|
+
void onUpdate('add', filePath, provider);
|
|
242
|
+
})
|
|
243
|
+
.on('change', (filePath: string) => {
|
|
244
|
+
void onUpdate('change', filePath, provider);
|
|
245
|
+
})
|
|
246
|
+
.on('error', (error: unknown) => {
|
|
247
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
248
|
+
console.error(`Session watcher error for provider "${provider}"`, { error: message });
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
watchers.push(watcher);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
254
|
+
console.error(`Failed to initialize session watcher for provider "${provider}"`, {
|
|
255
|
+
rootPath,
|
|
256
|
+
error: message,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Stops all active provider session watchers.
|
|
264
|
+
*/
|
|
265
|
+
export async function closeSessionsWatcher(): Promise<void> {
|
|
266
|
+
clearPendingWatcherFlushTimer();
|
|
267
|
+
|
|
268
|
+
await Promise.all(
|
|
269
|
+
watchers.map(async (watcher) => {
|
|
270
|
+
try {
|
|
271
|
+
await watcher.close();
|
|
272
|
+
} catch (error) {
|
|
273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
274
|
+
console.error('Failed to close session watcher', { error: message });
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
watchers.length = 0;
|
|
279
|
+
pendingWatcherUpdate = null;
|
|
280
|
+
pendingWatcherUpdateStartedAt = null;
|
|
281
|
+
watcherRefreshInFlight = false;
|
|
282
|
+
watcherRescheduleAfterRefresh = false;
|
|
283
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import { sessionsDb } from '@/modules/database/index.js';
|
|
4
|
+
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
5
|
+
import type {
|
|
6
|
+
FetchHistoryOptions,
|
|
7
|
+
FetchHistoryResult,
|
|
8
|
+
LLMProvider,
|
|
9
|
+
NormalizedMessage,
|
|
10
|
+
} from '@/shared/types.js';
|
|
11
|
+
import { AppError } from '@/shared/utils.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Removes one file if it exists.
|
|
15
|
+
*/
|
|
16
|
+
async function removeFileIfExists(filePath: string): Promise<boolean> {
|
|
17
|
+
try {
|
|
18
|
+
await fsp.unlink(filePath);
|
|
19
|
+
return true;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
22
|
+
if (code === 'ENOENT') {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Application service for provider-backed session message operations.
|
|
31
|
+
*
|
|
32
|
+
* Callers pass a provider id and this service resolves the concrete provider
|
|
33
|
+
* class, keeping normalization/history call sites decoupled from implementation
|
|
34
|
+
* file layout.
|
|
35
|
+
*/
|
|
36
|
+
export const sessionsService = {
|
|
37
|
+
/**
|
|
38
|
+
* Lists provider ids that can load session history and normalize live messages.
|
|
39
|
+
*/
|
|
40
|
+
listProviderIds(): LLMProvider[] {
|
|
41
|
+
return providerRegistry.listProviders().map((provider) => provider.id);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Normalizes one provider-native event into frontend session message events.
|
|
46
|
+
*/
|
|
47
|
+
normalizeMessage(
|
|
48
|
+
providerName: string,
|
|
49
|
+
raw: unknown,
|
|
50
|
+
sessionId: string | null,
|
|
51
|
+
): NormalizedMessage[] {
|
|
52
|
+
return providerRegistry.resolveProvider(providerName).sessions.normalizeMessage(raw, sessionId);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Fetches persisted history by session id.
|
|
57
|
+
*
|
|
58
|
+
* Provider and provider-specific lookup hints are resolved from the indexed
|
|
59
|
+
* session metadata in the database.
|
|
60
|
+
*/
|
|
61
|
+
fetchHistory(
|
|
62
|
+
sessionId: string,
|
|
63
|
+
options: Pick<FetchHistoryOptions, 'limit' | 'offset'> = {},
|
|
64
|
+
): Promise<FetchHistoryResult> {
|
|
65
|
+
const session = sessionsDb.getSessionById(sessionId);
|
|
66
|
+
if (!session) {
|
|
67
|
+
throw new AppError(`Session "${sessionId}" was not found.`, {
|
|
68
|
+
code: 'SESSION_NOT_FOUND',
|
|
69
|
+
statusCode: 404,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const provider = session.provider as LLMProvider;
|
|
74
|
+
return providerRegistry.resolveProvider(provider).sessions.fetchHistory(sessionId, {
|
|
75
|
+
limit: options.limit ?? null,
|
|
76
|
+
offset: options.offset ?? 0,
|
|
77
|
+
projectPath: session.project_path ?? '',
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Deletes one persisted session row by id.
|
|
83
|
+
*
|
|
84
|
+
* When `deletedFromDisk` is true and a session `jsonl_path` exists, the path
|
|
85
|
+
* is deleted from disk before the DB row is removed.
|
|
86
|
+
*/
|
|
87
|
+
async deleteSessionById(
|
|
88
|
+
sessionId: string,
|
|
89
|
+
deletedFromDisk = false,
|
|
90
|
+
): Promise<{ sessionId: string; deletedFromDisk: boolean }> {
|
|
91
|
+
const session = sessionsDb.getSessionById(sessionId);
|
|
92
|
+
if (!session) {
|
|
93
|
+
throw new AppError(`Session "${sessionId}" was not found.`, {
|
|
94
|
+
code: 'SESSION_NOT_FOUND',
|
|
95
|
+
statusCode: 404,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let removedFromDisk = false;
|
|
100
|
+
if (deletedFromDisk && session.jsonl_path) {
|
|
101
|
+
removedFromDisk = await removeFileIfExists(session.jsonl_path);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const deleted = sessionsDb.deleteSessionById(sessionId);
|
|
105
|
+
if (!deleted) {
|
|
106
|
+
throw new AppError(`Session "${sessionId}" was not found.`, {
|
|
107
|
+
code: 'SESSION_NOT_FOUND',
|
|
108
|
+
statusCode: 404,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { sessionId, deletedFromDisk: removedFromDisk };
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Renames one session by id without requiring the caller to pass provider.
|
|
117
|
+
*/
|
|
118
|
+
renameSessionById(sessionId: string, summary: string): { sessionId: string; summary: string } {
|
|
119
|
+
const session = sessionsDb.getSessionById(sessionId);
|
|
120
|
+
if (!session) {
|
|
121
|
+
throw new AppError(`Session "${sessionId}" was not found.`, {
|
|
122
|
+
code: 'SESSION_NOT_FOUND',
|
|
123
|
+
statusCode: 404,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
sessionsDb.updateSessionCustomName(sessionId, summary);
|
|
128
|
+
return { sessionId, summary };
|
|
129
|
+
},
|
|
130
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IProvider,
|
|
3
|
+
IProviderAuth,
|
|
4
|
+
IProviderMcp,
|
|
5
|
+
IProviderSessionSynchronizer,
|
|
6
|
+
IProviderSessions,
|
|
7
|
+
} from '@/shared/interfaces.js';
|
|
8
|
+
import type { LLMProvider } from '@/shared/types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shared provider base.
|
|
12
|
+
*
|
|
13
|
+
* Concrete providers must expose auth/MCP handlers and implement message
|
|
14
|
+
* normalization/history loading because those behaviors depend on native
|
|
15
|
+
* SDK/CLI formats.
|
|
16
|
+
*/
|
|
17
|
+
export abstract class AbstractProvider implements IProvider {
|
|
18
|
+
readonly id: LLMProvider;
|
|
19
|
+
abstract readonly mcp: IProviderMcp;
|
|
20
|
+
abstract readonly auth: IProviderAuth;
|
|
21
|
+
abstract readonly sessions: IProviderSessions;
|
|
22
|
+
abstract readonly sessionSynchronizer: IProviderSessionSynchronizer;
|
|
23
|
+
|
|
24
|
+
protected constructor(id: LLMProvider) {
|
|
25
|
+
this.id = id;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import type { IProviderMcp } from '@/shared/interfaces.js';
|
|
4
|
+
import type { LLMProvider, McpScope, McpTransport, ProviderMcpServer, UpsertProviderMcpServerInput } from '@/shared/types.js';
|
|
5
|
+
import { AppError } from '@/shared/utils.js';
|
|
6
|
+
|
|
7
|
+
const resolveWorkspacePath = (workspacePath?: string): string =>
|
|
8
|
+
path.resolve(workspacePath ?? process.cwd());
|
|
9
|
+
|
|
10
|
+
const normalizeServerName = (name: string): string => {
|
|
11
|
+
const normalized = name.trim();
|
|
12
|
+
if (!normalized) {
|
|
13
|
+
throw new AppError('MCP server name is required.', {
|
|
14
|
+
code: 'MCP_SERVER_NAME_REQUIRED',
|
|
15
|
+
statusCode: 400,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return normalized;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Shared MCP provider for provider-specific config readers/writers.
|
|
24
|
+
*/
|
|
25
|
+
export abstract class McpProvider implements IProviderMcp {
|
|
26
|
+
protected readonly provider: LLMProvider;
|
|
27
|
+
protected readonly supportedScopes: McpScope[];
|
|
28
|
+
protected readonly supportedTransports: McpTransport[];
|
|
29
|
+
|
|
30
|
+
protected constructor(
|
|
31
|
+
provider: LLMProvider,
|
|
32
|
+
supportedScopes: McpScope[],
|
|
33
|
+
supportedTransports: McpTransport[],
|
|
34
|
+
) {
|
|
35
|
+
this.provider = provider;
|
|
36
|
+
this.supportedScopes = supportedScopes;
|
|
37
|
+
this.supportedTransports = supportedTransports;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async listServers(options?: { workspacePath?: string }): Promise<Record<McpScope, ProviderMcpServer[]>> {
|
|
41
|
+
const grouped: Record<McpScope, ProviderMcpServer[]> = {
|
|
42
|
+
user: [],
|
|
43
|
+
local: [],
|
|
44
|
+
project: [],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
for (const scope of this.supportedScopes) {
|
|
48
|
+
grouped[scope] = await this.listServersForScope(scope, options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return grouped;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async listServersForScope(
|
|
55
|
+
scope: McpScope,
|
|
56
|
+
options?: { workspacePath?: string },
|
|
57
|
+
): Promise<ProviderMcpServer[]> {
|
|
58
|
+
if (!this.supportedScopes.includes(scope)) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const workspacePath = resolveWorkspacePath(options?.workspacePath);
|
|
63
|
+
const scopedServers = await this.readScopedServers(scope, workspacePath);
|
|
64
|
+
return Object.entries(scopedServers)
|
|
65
|
+
.map(([name, rawConfig]) => this.normalizeServerConfig(scope, name, rawConfig))
|
|
66
|
+
.filter((entry): entry is ProviderMcpServer => entry !== null);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async upsertServer(input: UpsertProviderMcpServerInput): Promise<ProviderMcpServer> {
|
|
70
|
+
const scope = input.scope ?? 'project';
|
|
71
|
+
this.assertScopeAndTransport(scope, input.transport);
|
|
72
|
+
|
|
73
|
+
const workspacePath = resolveWorkspacePath(input.workspacePath);
|
|
74
|
+
const normalizedName = normalizeServerName(input.name);
|
|
75
|
+
const scopedServers = await this.readScopedServers(scope, workspacePath);
|
|
76
|
+
scopedServers[normalizedName] = this.buildServerConfig(input);
|
|
77
|
+
await this.writeScopedServers(scope, workspacePath, scopedServers);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
provider: this.provider,
|
|
81
|
+
name: normalizedName,
|
|
82
|
+
scope,
|
|
83
|
+
transport: input.transport,
|
|
84
|
+
command: input.command,
|
|
85
|
+
args: input.args,
|
|
86
|
+
env: input.env,
|
|
87
|
+
cwd: input.cwd,
|
|
88
|
+
url: input.url,
|
|
89
|
+
headers: input.headers,
|
|
90
|
+
envVars: input.envVars,
|
|
91
|
+
bearerTokenEnvVar: input.bearerTokenEnvVar,
|
|
92
|
+
envHttpHeaders: input.envHttpHeaders,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async removeServer(
|
|
97
|
+
input: { name: string; scope?: McpScope; workspacePath?: string },
|
|
98
|
+
): Promise<{ removed: boolean; provider: LLMProvider; name: string; scope: McpScope }> {
|
|
99
|
+
const scope = input.scope ?? 'project';
|
|
100
|
+
this.assertScope(scope);
|
|
101
|
+
|
|
102
|
+
const workspacePath = resolveWorkspacePath(input.workspacePath);
|
|
103
|
+
const normalizedName = normalizeServerName(input.name);
|
|
104
|
+
const scopedServers = await this.readScopedServers(scope, workspacePath);
|
|
105
|
+
const removed = Object.prototype.hasOwnProperty.call(scopedServers, normalizedName);
|
|
106
|
+
if (removed) {
|
|
107
|
+
delete scopedServers[normalizedName];
|
|
108
|
+
await this.writeScopedServers(scope, workspacePath, scopedServers);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { removed, provider: this.provider, name: normalizedName, scope };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected abstract readScopedServers(
|
|
115
|
+
scope: McpScope,
|
|
116
|
+
workspacePath: string,
|
|
117
|
+
): Promise<Record<string, unknown>>;
|
|
118
|
+
|
|
119
|
+
protected abstract writeScopedServers(
|
|
120
|
+
scope: McpScope,
|
|
121
|
+
workspacePath: string,
|
|
122
|
+
servers: Record<string, unknown>,
|
|
123
|
+
): Promise<void>;
|
|
124
|
+
|
|
125
|
+
protected abstract buildServerConfig(input: UpsertProviderMcpServerInput): Record<string, unknown>;
|
|
126
|
+
|
|
127
|
+
protected abstract normalizeServerConfig(
|
|
128
|
+
scope: McpScope,
|
|
129
|
+
name: string,
|
|
130
|
+
rawConfig: unknown,
|
|
131
|
+
): ProviderMcpServer | null;
|
|
132
|
+
|
|
133
|
+
protected assertScope(scope: McpScope): void {
|
|
134
|
+
if (!this.supportedScopes.includes(scope)) {
|
|
135
|
+
throw new AppError(`Provider "${this.provider}" does not support "${scope}" MCP scope.`, {
|
|
136
|
+
code: 'MCP_SCOPE_NOT_SUPPORTED',
|
|
137
|
+
statusCode: 400,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
protected assertScopeAndTransport(scope: McpScope, transport: McpTransport): void {
|
|
143
|
+
this.assertScope(scope);
|
|
144
|
+
if (!this.supportedTransports.includes(transport)) {
|
|
145
|
+
throw new AppError(`Provider "${this.provider}" does not support "${transport}" MCP transport.`, {
|
|
146
|
+
code: 'MCP_TRANSPORT_NOT_SUPPORTED',
|
|
147
|
+
statusCode: 400,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|