@cloudcli-ai/cloudcli 1.34.0 → 1.35.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 +9 -1
- package/README.ja.md +8 -0
- package/README.ko.md +9 -1
- package/README.md +16 -3
- package/README.ru.md +9 -1
- package/README.tr.md +8 -1
- package/README.zh-CN.md +9 -1
- package/README.zh-TW.md +9 -1
- package/dist/api-docs.html +36 -18
- package/dist/assets/index-CtwxqzGG.js +1407 -0
- package/dist/assets/index-DlvtxFyt.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/browser-use-mcp.js +350 -0
- package/dist-server/server/browser-use-mcp.js.map +1 -0
- package/dist-server/server/claude-sdk.js +25 -5
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cli.js +14 -6
- package/dist-server/server/cli.js.map +1 -1
- package/dist-server/server/computer-use-agent.js +242 -0
- package/dist-server/server/computer-use-agent.js.map +1 -0
- package/dist-server/server/computer-use-mcp.js +519 -0
- package/dist-server/server/computer-use-mcp.js.map +1 -0
- package/dist-server/server/cursor-cli.js +27 -11
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +17 -2
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/index.js +149 -40
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/load-env.js +1 -1
- package/dist-server/server/load-env.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use-mcp.routes.js +107 -0
- package/dist-server/server/modules/browser-use/browser-use-mcp.routes.js.map +1 -0
- package/dist-server/server/modules/browser-use/browser-use.routes.js +93 -0
- package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -0
- package/dist-server/server/modules/browser-use/browser-use.service.js +688 -0
- package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -0
- package/dist-server/server/modules/browser-use/tests/browser-use.service.test.js +8 -0
- package/dist-server/server/modules/browser-use/tests/browser-use.service.test.js.map +1 -0
- package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js +55 -0
- package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js.map +1 -0
- package/dist-server/server/modules/computer-use/actions/raw-action-types.js +2 -0
- package/dist-server/server/modules/computer-use/actions/raw-action-types.js.map +1 -0
- package/dist-server/server/modules/computer-use/computer-executor.js +212 -0
- package/dist-server/server/modules/computer-use/computer-executor.js.map +1 -0
- package/dist-server/server/modules/computer-use/computer-semantics.service.js +422 -0
- package/dist-server/server/modules/computer-use/computer-semantics.service.js.map +1 -0
- package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js +126 -0
- package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js.map +1 -0
- package/dist-server/server/modules/computer-use/computer-use.routes.js +196 -0
- package/dist-server/server/modules/computer-use/computer-use.routes.js.map +1 -0
- package/dist-server/server/modules/computer-use/computer-use.service.js +759 -0
- package/dist-server/server/modules/computer-use/computer-use.service.js.map +1 -0
- package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js +126 -0
- package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js.map +1 -0
- package/dist-server/server/modules/computer-use/index.js +3 -0
- package/dist-server/server/modules/computer-use/index.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js +59 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js +5 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js +2 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js +5 -0
- package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js +106 -0
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js +78 -0
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js +68 -0
- package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js +16 -0
- package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js.map +1 -0
- package/dist-server/server/modules/computer-use/semantics/semantic-types.js +2 -0
- package/dist-server/server/modules/computer-use/semantics/semantic-types.js.map +1 -0
- package/dist-server/server/modules/database/index.js +1 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +23 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/notification-channel-endpoints.js +100 -0
- package/dist-server/server/modules/database/repositories/notification-channel-endpoints.js.map +1 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js +8 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -1
- package/dist-server/server/modules/database/repositories/sessions.db.js +173 -17
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +26 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +78 -0
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -0
- package/dist-server/server/modules/database/{repositories → tests}/sessions.db.integration.test.js +10 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/notifications/index.js +4 -0
- package/dist-server/server/modules/notifications/index.js.map +1 -0
- package/dist-server/server/modules/notifications/notifications.routes.js +115 -0
- package/dist-server/server/modules/notifications/notifications.routes.js.map +1 -0
- package/dist-server/server/modules/notifications/services/desktop-notification-clients.service.js +97 -0
- package/dist-server/server/modules/notifications/services/desktop-notification-clients.service.js.map +1 -0
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +229 -0
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -0
- package/dist-server/server/modules/notifications/websocket/desktop-notifications-websocket.service.js +79 -0
- package/dist-server/server/modules/notifications/websocket/desktop-notifications-websocket.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js +0 -4
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +10 -38
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +1 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +24 -1
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +9 -12
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +7 -0
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +7 -3
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +3 -9
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +7 -0
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +13 -22
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +7 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +5 -8
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +6 -6
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +7 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +17 -3
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +9 -10
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +123 -1
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/mcp.service.js +23 -0
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +65 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-models.service.js +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +62 -23
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +62 -4
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/skills.service.js +11 -0
- package/dist-server/server/modules/providers/services/skills.service.js.map +1 -1
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js +163 -1
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +45 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/skills.test.js +160 -0
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +1 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +252 -0
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +105 -0
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +218 -156
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js +39 -0
- package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +83 -31
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/websocket-server.service.js +10 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +216 -0
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -0
- package/dist-server/server/openai-codex.js +24 -14
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +21 -8
- package/dist-server/server/opencode-cli.js.map +1 -1
- package/dist-server/server/opencode-cli.test.js +19 -1
- package/dist-server/server/opencode-cli.test.js.map +1 -1
- package/dist-server/server/routes/settings.js +1 -1
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/services/notification-orchestrator.js +1 -176
- package/dist-server/server/services/notification-orchestrator.js.map +1 -1
- package/dist-server/server/services/tests/notification-orchestrator.test.js +64 -0
- package/dist-server/server/services/tests/notification-orchestrator.test.js.map +1 -0
- package/dist-server/server/shared/{claude-cli-path.test.js → tests/claude-cli-path.test.js} +1 -1
- package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -0
- package/dist-server/server/shared/tests/slice-tail-page.test.js +35 -0
- package/dist-server/server/shared/tests/slice-tail-page.test.js.map +1 -0
- package/dist-server/server/shared/utils.js +73 -1
- package/dist-server/server/shared/utils.js.map +1 -1
- package/dist-server/server/voice-proxy.js +226 -0
- package/dist-server/server/voice-proxy.js.map +1 -0
- package/electron/assets/logo-macos.icns +0 -0
- package/electron/assets/logo-macos.png +0 -0
- package/electron/assets/logo-windows.ico +0 -0
- package/electron/cloud.js +260 -0
- package/electron/computerAgent.js +290 -0
- package/electron/desktopNotifications.js +378 -0
- package/electron/desktopWindow.js +781 -0
- package/electron/launcher/index.html +14 -0
- package/electron/launcher/launcher.css +801 -0
- package/electron/launcher/launcher.js +829 -0
- package/electron/localServer.js +550 -0
- package/electron/main.js +1060 -0
- package/electron/preload.cjs +63 -0
- package/electron/scripts/generate-macos-icon.js +62 -0
- package/electron/serverInstaller.js +277 -0
- package/electron/tabs.js +87 -0
- package/electron/viewHost.js +331 -0
- package/package.json +86 -5
- package/public/api-docs.html +36 -18
- package/scripts/build-computer-semantics.mjs +133 -0
- package/scripts/copy-computer-semantics-bin.mjs +24 -0
- package/scripts/release/build-server-bundle.js +176 -0
- package/scripts/release/prepare-desktop-app.js +158 -0
- package/server/browser-use-mcp.ts +384 -0
- package/server/claude-sdk.js +27 -5
- package/server/cli.js +15 -6
- package/server/computer-use-agent.ts +279 -0
- package/server/computer-use-mcp.ts +574 -0
- package/server/cursor-cli.js +27 -11
- package/server/gemini-cli.js +17 -2
- package/server/index.js +151 -47
- package/server/load-env.js +1 -1
- package/server/modules/browser-use/browser-use-mcp.routes.ts +120 -0
- package/server/modules/browser-use/browser-use.routes.ts +96 -0
- package/server/modules/browser-use/browser-use.service.ts +836 -0
- package/server/modules/browser-use/tests/browser-use.service.test.ts +10 -0
- package/server/modules/computer-use/actions/raw-action-dispatcher.ts +67 -0
- package/server/modules/computer-use/actions/raw-action-types.ts +28 -0
- package/server/modules/computer-use/computer-executor.ts +242 -0
- package/server/modules/computer-use/computer-semantics.service.ts +460 -0
- package/server/modules/computer-use/computer-use-mcp.routes.ts +141 -0
- package/server/modules/computer-use/computer-use.routes.ts +211 -0
- package/server/modules/computer-use/computer-use.service.ts +920 -0
- package/server/modules/computer-use/desktop-agent-relay.service.ts +158 -0
- package/server/modules/computer-use/index.ts +2 -0
- package/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.ts +82 -0
- package/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.ts +5 -0
- package/server/modules/computer-use/semantics/adapters/semantic-adapter.ts +23 -0
- package/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.ts +5 -0
- package/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
- package/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
- package/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
- package/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
- package/server/modules/computer-use/semantics/helpers/macos/CloudCLISemantics.swift +467 -0
- package/server/modules/computer-use/semantics/helpers/semantic-helper-process.ts +124 -0
- package/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.ts +97 -0
- package/server/modules/computer-use/semantics/helpers/windows/CloudCLISemantics.csproj +11 -0
- package/server/modules/computer-use/semantics/helpers/windows/Program.cs +534 -0
- package/server/modules/computer-use/semantics/semantic-session-store.ts +87 -0
- package/server/modules/computer-use/semantics/semantic-tool-dispatcher.ts +17 -0
- package/server/modules/computer-use/semantics/semantic-types.ts +58 -0
- package/server/modules/database/index.ts +1 -0
- package/server/modules/database/migrations.ts +25 -0
- package/server/modules/database/repositories/notification-channel-endpoints.ts +153 -0
- package/server/modules/database/repositories/notification-preferences.ts +12 -1
- package/server/modules/database/repositories/sessions.db.ts +227 -23
- package/server/modules/database/schema.ts +27 -0
- package/server/modules/database/tests/sessions-provider-mapping.test.ts +108 -0
- package/server/modules/database/{repositories → tests}/sessions.db.integration.test.ts +12 -0
- package/server/modules/notifications/index.ts +13 -0
- package/server/modules/notifications/notifications.routes.ts +127 -0
- package/server/modules/notifications/services/desktop-notification-clients.service.ts +124 -0
- package/server/modules/notifications/services/notification-orchestrator.service.js +288 -0
- package/server/modules/notifications/websocket/desktop-notifications-websocket.service.ts +109 -0
- package/server/modules/projects/services/project-management.service.ts +0 -8
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +12 -53
- package/server/modules/providers/README.md +1 -1
- package/server/modules/providers/index.ts +1 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +26 -1
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +9 -14
- package/server/modules/providers/list/claude/claude-skills.provider.ts +8 -0
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +7 -3
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +3 -12
- package/server/modules/providers/list/codex/codex-skills.provider.ts +8 -0
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +13 -22
- package/server/modules/providers/list/cursor/cursor-skills.provider.ts +8 -0
- package/server/modules/providers/list/gemini/gemini-models.provider.ts +5 -8
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +6 -6
- package/server/modules/providers/list/gemini/gemini-skills.provider.ts +8 -0
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +18 -4
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +9 -12
- package/server/modules/providers/provider.routes.ts +167 -1
- package/server/modules/providers/services/mcp.service.ts +26 -0
- package/server/modules/providers/services/provider-capabilities.service.ts +91 -0
- package/server/modules/providers/services/provider-models.service.ts +1 -1
- package/server/modules/providers/services/sessions-watcher.service.ts +71 -25
- package/server/modules/providers/services/sessions.service.ts +79 -4
- package/server/modules/providers/services/skills.service.ts +25 -1
- package/server/modules/providers/shared/skills/skills.provider.ts +223 -0
- package/server/modules/providers/tests/opencode-sessions.test.ts +49 -0
- package/server/modules/providers/tests/skills.test.ts +212 -0
- package/server/modules/websocket/README.md +33 -27
- package/server/modules/websocket/index.ts +1 -0
- package/server/modules/websocket/services/chat-run-registry.service.ts +327 -0
- package/server/modules/websocket/services/chat-session-writer.service.ts +145 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +292 -207
- package/server/modules/websocket/services/desktop-agent-websocket.service.ts +51 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +108 -32
- package/server/modules/websocket/services/websocket-server.service.ts +12 -0
- package/server/modules/websocket/tests/chat-run-registry.test.ts +244 -0
- package/server/openai-codex.js +24 -14
- package/server/opencode-cli.js +21 -8
- package/server/opencode-cli.test.js +20 -1
- package/server/routes/settings.js +6 -1
- package/server/services/notification-orchestrator.js +3 -224
- package/server/services/tests/notification-orchestrator.test.js +80 -0
- package/server/shared/interfaces.ts +15 -0
- package/server/shared/tests/slice-tail-page.test.ts +42 -0
- package/server/shared/types.ts +84 -1
- package/server/shared/utils.ts +95 -1
- package/server/voice-proxy.js +224 -0
- package/dist/assets/index-73Xw3i9v.css +0 -32
- package/dist/assets/index-BIHaviaA.js +0 -1361
- package/dist/modelConstants.js +0 -854
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +0 -1
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +0 -1
- package/dist-server/server/shared/claude-cli-path.test.js.map +0 -1
- package/public/modelConstants.js +0 -854
- /package/dist-server/server/modules/database/{repositories → tests}/projects.db.integration.test.js +0 -0
- /package/server/modules/database/{repositories → tests}/projects.db.integration.test.ts +0 -0
- /package/server/shared/{claude-cli-path.test.ts → tests/claude-cli-path.test.ts} +0 -0
|
@@ -33,4 +33,12 @@ export class GeminiSkillsProvider extends SkillsProvider {
|
|
|
33
33
|
},
|
|
34
34
|
];
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
protected async getGlobalSkillSource(): Promise<ProviderSkillSource> {
|
|
38
|
+
return {
|
|
39
|
+
scope: 'user',
|
|
40
|
+
rootDir: path.join(os.homedir(), '.gemini', 'skills'),
|
|
41
|
+
commandPrefix: '/',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
36
44
|
}
|
|
@@ -112,7 +112,21 @@ export class OpenCodeSessionSynchronizer implements IProviderSessionSynchronizer
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const fallbackTitle = 'Untitled OpenCode Session';
|
|
115
|
-
const
|
|
115
|
+
const pendingAppSession = sessionsDb.getSessionByProviderSessionId(sessionId)
|
|
116
|
+
?? sessionsDb.getSessionById(sessionId)
|
|
117
|
+
?? sessionsDb.findLatestPendingAppSession(this.provider, projectPath);
|
|
118
|
+
if (pendingAppSession && !pendingAppSession.provider_session_id) {
|
|
119
|
+
// Slow networks can let the sqlite watcher index opencode.db before the
|
|
120
|
+
// runtime reports its provider id back through the websocket mapping.
|
|
121
|
+
// Bind that id to the fresh app row first so the watcher does not create
|
|
122
|
+
// a temporary provider-id sidebar entry for the same session.
|
|
123
|
+
sessionsDb.assignProviderSessionId(pendingAppSession.session_id, sessionId);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// App-created sessions are keyed by an app id, so disk-discovered provider
|
|
127
|
+
// ids must be resolved through the provider-id mapping first.
|
|
128
|
+
const existingSession = sessionsDb.getSessionByProviderSessionId(sessionId)
|
|
129
|
+
?? sessionsDb.getSessionById(sessionId);
|
|
116
130
|
const existingName = existingSession?.custom_name;
|
|
117
131
|
const nextName = existingName && existingName !== fallbackTitle
|
|
118
132
|
? existingName
|
|
@@ -120,7 +134,9 @@ export class OpenCodeSessionSynchronizer implements IProviderSessionSynchronizer
|
|
|
120
134
|
|
|
121
135
|
// OpenCode stores every session in one shared sqlite database, so jsonl_path
|
|
122
136
|
// must stay null to avoid deleting opencode.db when one app session is removed.
|
|
123
|
-
|
|
137
|
+
// Return the canonical stored row id so watcher-triggered sidebar updates
|
|
138
|
+
// stay on the app session once provider_session_id has already been mapped.
|
|
139
|
+
return sessionsDb.createSession(
|
|
124
140
|
sessionId,
|
|
125
141
|
this.provider,
|
|
126
142
|
projectPath,
|
|
@@ -129,8 +145,6 @@ export class OpenCodeSessionSynchronizer implements IProviderSessionSynchronizer
|
|
|
129
145
|
normalizeProviderTimestamp(row.time_updated ?? row.time_created),
|
|
130
146
|
null,
|
|
131
147
|
);
|
|
132
|
-
|
|
133
|
-
return sessionId;
|
|
134
148
|
}
|
|
135
149
|
|
|
136
150
|
private readFirstUserText(db: Database.Database, sessionId: string): string | undefined {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
readObjectRecord,
|
|
13
13
|
readJsonRecord,
|
|
14
14
|
readOptionalString,
|
|
15
|
+
sliceTailPage,
|
|
15
16
|
} from '@/shared/utils.js';
|
|
16
17
|
|
|
17
18
|
const PROVIDER = 'opencode';
|
|
@@ -325,6 +326,9 @@ export class OpenCodeSessionsProvider implements IProviderSessions {
|
|
|
325
326
|
options: FetchHistoryOptions = {},
|
|
326
327
|
): Promise<FetchHistoryResult> {
|
|
327
328
|
const { limit = null, offset = 0 } = options;
|
|
329
|
+
// OpenCode's shared sqlite database keys messages by the provider-native
|
|
330
|
+
// session id, not the app-facing id this method is addressed with.
|
|
331
|
+
const providerSessionId = options.providerSessionId ?? sessionId;
|
|
328
332
|
const db = openOpenCodeDatabase();
|
|
329
333
|
if (!db) {
|
|
330
334
|
return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
|
|
@@ -349,27 +353,20 @@ export class OpenCodeSessionsProvider implements IProviderSessions {
|
|
|
349
353
|
m.id,
|
|
350
354
|
COALESCE(p.time_created, 0),
|
|
351
355
|
p.id
|
|
352
|
-
`).all(
|
|
356
|
+
`).all(providerSessionId) as OpenCodeHistoryRow[];
|
|
353
357
|
|
|
354
358
|
const normalized = this.normalizeHistoryRows(rows, sessionId);
|
|
355
|
-
const tokenUsage = aggregateOpenCodeSessionTokenUsage(db,
|
|
359
|
+
const tokenUsage = aggregateOpenCodeSessionTokenUsage(db, providerSessionId);
|
|
356
360
|
|
|
357
361
|
const normalizedOffset = Math.max(0, offset);
|
|
358
362
|
const normalizedLimit = limit === null ? null : Math.max(0, limit);
|
|
359
363
|
const total = normalized.length;
|
|
360
|
-
const
|
|
361
|
-
? normalized
|
|
362
|
-
: normalized.slice(
|
|
363
|
-
Math.max(0, total - normalizedOffset - normalizedLimit),
|
|
364
|
-
Math.max(0, total - normalizedOffset),
|
|
365
|
-
);
|
|
364
|
+
const { page, hasMore } = sliceTailPage(normalized, normalizedLimit, normalizedOffset);
|
|
366
365
|
|
|
367
366
|
return {
|
|
368
|
-
messages,
|
|
367
|
+
messages: page,
|
|
369
368
|
total,
|
|
370
|
-
hasMore
|
|
371
|
-
? false
|
|
372
|
-
: Math.max(0, total - normalizedOffset - normalizedLimit) > 0,
|
|
369
|
+
hasMore,
|
|
373
370
|
offset: normalizedOffset,
|
|
374
371
|
limit: normalizedLimit,
|
|
375
372
|
tokenUsage,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import express, { type Request, type Response } from 'express';
|
|
2
2
|
|
|
3
3
|
import { providerAuthService } from '@/modules/providers/services/provider-auth.service.js';
|
|
4
|
+
import { providerCapabilitiesService } from '@/modules/providers/services/provider-capabilities.service.js';
|
|
4
5
|
import { providerMcpService } from '@/modules/providers/services/mcp.service.js';
|
|
5
6
|
import { providerModelsService } from '@/modules/providers/services/provider-models.service.js';
|
|
6
7
|
import { providerSkillsService } from '@/modules/providers/services/skills.service.js';
|
|
@@ -11,6 +12,8 @@ import type {
|
|
|
11
12
|
McpScope,
|
|
12
13
|
McpTransport,
|
|
13
14
|
ProviderChangeActiveModelInput,
|
|
15
|
+
ProviderSkillCreateFile,
|
|
16
|
+
ProviderSkillCreateInput,
|
|
14
17
|
UpsertProviderMcpServerInput,
|
|
15
18
|
} from '@/shared/types.js';
|
|
16
19
|
import { AppError, asyncHandler, createApiSuccessResponse } from '@/shared/utils.js';
|
|
@@ -178,6 +181,104 @@ const parseMcpUpsertPayload = (payload: unknown): UpsertProviderMcpServerInput =
|
|
|
178
181
|
};
|
|
179
182
|
};
|
|
180
183
|
|
|
184
|
+
const parseProviderSkillCreatePayload = (payload: unknown): ProviderSkillCreateInput => {
|
|
185
|
+
if (!payload || typeof payload !== 'object') {
|
|
186
|
+
throw new AppError('Request body must be an object.', {
|
|
187
|
+
code: 'INVALID_REQUEST_BODY',
|
|
188
|
+
statusCode: 400,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const body = payload as Record<string, unknown>;
|
|
193
|
+
const rawEntries = Array.isArray(body.entries)
|
|
194
|
+
? body.entries
|
|
195
|
+
: typeof body.content === 'string'
|
|
196
|
+
? [{
|
|
197
|
+
content: body.content,
|
|
198
|
+
directoryName: body.directoryName,
|
|
199
|
+
fileName: body.fileName,
|
|
200
|
+
files: body.files,
|
|
201
|
+
}]
|
|
202
|
+
: null;
|
|
203
|
+
|
|
204
|
+
if (!rawEntries || rawEntries.length === 0) {
|
|
205
|
+
throw new AppError('At least one skill entry is required.', {
|
|
206
|
+
code: 'PROVIDER_SKILLS_REQUIRED',
|
|
207
|
+
statusCode: 400,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const entries = rawEntries.map((entry, index) => {
|
|
212
|
+
if (!entry || typeof entry !== 'object') {
|
|
213
|
+
throw new AppError(`Skill entry ${index + 1} must be an object.`, {
|
|
214
|
+
code: 'INVALID_REQUEST_BODY',
|
|
215
|
+
statusCode: 400,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const record = entry as Record<string, unknown>;
|
|
220
|
+
const content = typeof record.content === 'string' ? record.content : '';
|
|
221
|
+
const directoryName = readOptionalQueryString(record.directoryName);
|
|
222
|
+
const fileName = readOptionalQueryString(record.fileName);
|
|
223
|
+
const rawFiles = record.files;
|
|
224
|
+
|
|
225
|
+
if (!content.trim()) {
|
|
226
|
+
throw new AppError(`Skill entry ${index + 1} must include markdown content.`, {
|
|
227
|
+
code: 'PROVIDER_SKILL_CONTENT_REQUIRED',
|
|
228
|
+
statusCode: 400,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (rawFiles !== undefined && !Array.isArray(rawFiles)) {
|
|
233
|
+
throw new AppError(`Skill entry ${index + 1} files must be an array.`, {
|
|
234
|
+
code: 'INVALID_REQUEST_BODY',
|
|
235
|
+
statusCode: 400,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const files: ProviderSkillCreateFile[] | undefined = rawFiles?.map((file, fileIndex) => {
|
|
240
|
+
if (!file || typeof file !== 'object') {
|
|
241
|
+
throw new AppError(`Skill entry ${index + 1} file ${fileIndex + 1} must be an object.`, {
|
|
242
|
+
code: 'INVALID_REQUEST_BODY',
|
|
243
|
+
statusCode: 400,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const fileRecord = file as Record<string, unknown>;
|
|
248
|
+
const relativePath = readOptionalQueryString(fileRecord.relativePath);
|
|
249
|
+
const fileContent = typeof fileRecord.content === 'string' ? fileRecord.content : null;
|
|
250
|
+
const encoding = fileRecord.encoding === 'utf8' || fileRecord.encoding === 'base64'
|
|
251
|
+
? fileRecord.encoding
|
|
252
|
+
: null;
|
|
253
|
+
|
|
254
|
+
if (!relativePath || fileContent === null || !encoding) {
|
|
255
|
+
throw new AppError(
|
|
256
|
+
`Skill entry ${index + 1} file ${fileIndex + 1} requires relativePath, content, and encoding.`,
|
|
257
|
+
{
|
|
258
|
+
code: 'INVALID_REQUEST_BODY',
|
|
259
|
+
statusCode: 400,
|
|
260
|
+
},
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
relativePath,
|
|
266
|
+
content: fileContent,
|
|
267
|
+
encoding,
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
content,
|
|
273
|
+
directoryName,
|
|
274
|
+
fileName,
|
|
275
|
+
files,
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
return { entries };
|
|
280
|
+
};
|
|
281
|
+
|
|
181
282
|
const parseProvider = (value: unknown): LLMProvider => {
|
|
182
283
|
const normalized = normalizeProviderParam(value);
|
|
183
284
|
if (
|
|
@@ -319,6 +420,27 @@ router.get(
|
|
|
319
420
|
}),
|
|
320
421
|
);
|
|
321
422
|
|
|
423
|
+
router.post(
|
|
424
|
+
'/:provider/skills',
|
|
425
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
426
|
+
const provider = parseProvider(req.params.provider);
|
|
427
|
+
const input = parseProviderSkillCreatePayload(req.body);
|
|
428
|
+
const skills = await providerSkillsService.addProviderSkills(provider, input);
|
|
429
|
+
res.json(createApiSuccessResponse({ provider, skills }));
|
|
430
|
+
}),
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
router.delete(
|
|
434
|
+
'/:provider/skills/:directoryName',
|
|
435
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
436
|
+
const provider = parseProvider(req.params.provider);
|
|
437
|
+
const result = await providerSkillsService.removeProviderSkill(provider, {
|
|
438
|
+
directoryName: readPathParam(req.params.directoryName, 'directoryName'),
|
|
439
|
+
});
|
|
440
|
+
res.json(createApiSuccessResponse(result));
|
|
441
|
+
}),
|
|
442
|
+
);
|
|
443
|
+
|
|
322
444
|
// ----------------- MCP routes -----------------
|
|
323
445
|
router.get(
|
|
324
446
|
'/:provider/mcp/servers',
|
|
@@ -382,7 +504,51 @@ router.post(
|
|
|
382
504
|
}),
|
|
383
505
|
);
|
|
384
506
|
|
|
507
|
+
router.get(
|
|
508
|
+
'/capabilities',
|
|
509
|
+
asyncHandler(async (_req: Request, res: Response) => {
|
|
510
|
+
res.json(createApiSuccessResponse({
|
|
511
|
+
providers: providerCapabilitiesService.listAllProviderCapabilities(),
|
|
512
|
+
}));
|
|
513
|
+
}),
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
router.get(
|
|
517
|
+
'/:provider/capabilities',
|
|
518
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
519
|
+
const provider = parseProvider(req.params.provider);
|
|
520
|
+
res.json(createApiSuccessResponse(
|
|
521
|
+
providerCapabilitiesService.getProviderCapabilities(provider),
|
|
522
|
+
));
|
|
523
|
+
}),
|
|
524
|
+
);
|
|
525
|
+
|
|
385
526
|
// ----------------- Session routes -----------------
|
|
527
|
+
/**
|
|
528
|
+
* Session gateway entry point: allocates the stable app-facing session id for
|
|
529
|
+
* a brand-new chat. The frontend must call this before the first `chat.send`
|
|
530
|
+
* so the session id in the URL, the store, and the websocket all agree from
|
|
531
|
+
* the very first message — there is no client-visible session-id handoff.
|
|
532
|
+
*/
|
|
533
|
+
router.post(
|
|
534
|
+
'/sessions',
|
|
535
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
536
|
+
const body = (req.body ?? {}) as Record<string, unknown>;
|
|
537
|
+
const provider = parseProvider(body.provider);
|
|
538
|
+
const projectPath = typeof body.projectPath === 'string' ? body.projectPath : '';
|
|
539
|
+
const result = sessionsService.createAppSession(provider, projectPath);
|
|
540
|
+
res.status(201).json(createApiSuccessResponse(result));
|
|
541
|
+
}),
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
router.get(
|
|
545
|
+
'/sessions/running',
|
|
546
|
+
asyncHandler(async (_req: Request, res: Response) => {
|
|
547
|
+
const sessions = sessionsService.listRunningSessions();
|
|
548
|
+
res.json(createApiSuccessResponse({ sessions }));
|
|
549
|
+
}),
|
|
550
|
+
);
|
|
551
|
+
|
|
386
552
|
router.get(
|
|
387
553
|
'/sessions/archived',
|
|
388
554
|
asyncHandler(async (_req: Request, res: Response) => {
|
|
@@ -459,7 +625,7 @@ router.get(
|
|
|
459
625
|
limit,
|
|
460
626
|
offset,
|
|
461
627
|
});
|
|
462
|
-
res.json(result);
|
|
628
|
+
res.json(createApiSuccessResponse(result));
|
|
463
629
|
}),
|
|
464
630
|
);
|
|
465
631
|
|
|
@@ -80,4 +80,30 @@ export const providerMcpService = {
|
|
|
80
80
|
|
|
81
81
|
return results;
|
|
82
82
|
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Removes one MCP server from every provider. Mirrors `addMcpServerToAllProviders`
|
|
86
|
+
* by iterating the live provider registry, so callers stay in sync with which
|
|
87
|
+
* providers exist instead of maintaining their own provider list.
|
|
88
|
+
*/
|
|
89
|
+
async removeMcpServerFromAllProviders(
|
|
90
|
+
input: { name: string; scope?: McpScope; workspacePath?: string },
|
|
91
|
+
): Promise<Array<{ provider: LLMProvider; removed: boolean; error?: string }>> {
|
|
92
|
+
const results: Array<{ provider: LLMProvider; removed: boolean; error?: string }> = [];
|
|
93
|
+
const providers = providerRegistry.listProviders();
|
|
94
|
+
for (const provider of providers) {
|
|
95
|
+
try {
|
|
96
|
+
const result = await provider.mcp.removeServer(input);
|
|
97
|
+
results.push({ provider: provider.id, removed: result.removed });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
results.push({
|
|
100
|
+
provider: provider.id,
|
|
101
|
+
removed: false,
|
|
102
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return results;
|
|
108
|
+
},
|
|
83
109
|
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { LLMProvider } from '@/shared/types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Static, backend-owned description of what one provider integration supports.
|
|
5
|
+
*
|
|
6
|
+
* The frontend renders its composer UI (permission mode picker, image upload,
|
|
7
|
+
* abort button, ...) purely from this shape, which is what keeps the frontend
|
|
8
|
+
* free of per-provider conditionals. New provider features should be exposed
|
|
9
|
+
* here instead of branching on the provider id in React components.
|
|
10
|
+
*/
|
|
11
|
+
type ProviderCapabilities = {
|
|
12
|
+
provider: LLMProvider;
|
|
13
|
+
/** Permission modes the provider runtime understands, in cycle order. */
|
|
14
|
+
permissionModes: string[];
|
|
15
|
+
defaultPermissionMode: string;
|
|
16
|
+
/** Whether image attachments can be included in a chat.send. */
|
|
17
|
+
supportsImages: boolean;
|
|
18
|
+
/** Whether an in-flight run can be cancelled via chat.abort. */
|
|
19
|
+
supportsAbort: boolean;
|
|
20
|
+
/** Whether interactive tool permission prompts can reach the UI. */
|
|
21
|
+
supportsPermissionRequests: boolean;
|
|
22
|
+
/** Whether the token-usage endpoint has data for this provider. */
|
|
23
|
+
supportsTokenUsage: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The capability matrix mirrors what each runtime actually implements today:
|
|
28
|
+
* - permission modes match the option sets accepted by each CLI/SDK.
|
|
29
|
+
* - only the Claude SDK integration surfaces interactive permission requests.
|
|
30
|
+
* - Cursor has no token usage endpoint support (its store.db has no usage rows).
|
|
31
|
+
*/
|
|
32
|
+
const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
33
|
+
claude: {
|
|
34
|
+
provider: 'claude',
|
|
35
|
+
permissionModes: ['default', 'auto', 'acceptEdits', 'bypassPermissions', 'plan'],
|
|
36
|
+
defaultPermissionMode: 'default',
|
|
37
|
+
supportsImages: true,
|
|
38
|
+
supportsAbort: true,
|
|
39
|
+
supportsPermissionRequests: true,
|
|
40
|
+
supportsTokenUsage: true,
|
|
41
|
+
},
|
|
42
|
+
cursor: {
|
|
43
|
+
provider: 'cursor',
|
|
44
|
+
permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'plan'],
|
|
45
|
+
defaultPermissionMode: 'default',
|
|
46
|
+
supportsImages: false,
|
|
47
|
+
supportsAbort: true,
|
|
48
|
+
supportsPermissionRequests: false,
|
|
49
|
+
supportsTokenUsage: false,
|
|
50
|
+
},
|
|
51
|
+
codex: {
|
|
52
|
+
provider: 'codex',
|
|
53
|
+
permissionModes: ['default', 'acceptEdits', 'bypassPermissions'],
|
|
54
|
+
defaultPermissionMode: 'default',
|
|
55
|
+
supportsImages: false,
|
|
56
|
+
supportsAbort: true,
|
|
57
|
+
supportsPermissionRequests: false,
|
|
58
|
+
supportsTokenUsage: true,
|
|
59
|
+
},
|
|
60
|
+
gemini: {
|
|
61
|
+
provider: 'gemini',
|
|
62
|
+
permissionModes: ['default', 'acceptEdits', 'bypassPermissions', 'plan'],
|
|
63
|
+
defaultPermissionMode: 'default',
|
|
64
|
+
supportsImages: false,
|
|
65
|
+
supportsAbort: true,
|
|
66
|
+
supportsPermissionRequests: false,
|
|
67
|
+
supportsTokenUsage: true,
|
|
68
|
+
},
|
|
69
|
+
opencode: {
|
|
70
|
+
provider: 'opencode',
|
|
71
|
+
permissionModes: ['default'],
|
|
72
|
+
defaultPermissionMode: 'default',
|
|
73
|
+
supportsImages: false,
|
|
74
|
+
supportsAbort: true,
|
|
75
|
+
supportsPermissionRequests: false,
|
|
76
|
+
supportsTokenUsage: true,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Application service exposing the provider capability matrix.
|
|
82
|
+
*/
|
|
83
|
+
export const providerCapabilitiesService = {
|
|
84
|
+
getProviderCapabilities(provider: LLMProvider): ProviderCapabilities {
|
|
85
|
+
return PROVIDER_CAPABILITIES[provider];
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
listAllProviderCapabilities(): ProviderCapabilities[] {
|
|
89
|
+
return Object.values(PROVIDER_CAPABILITIES);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
@@ -17,7 +17,7 @@ import { readProviderSessionActiveModelChange } from '@/shared/utils.js';
|
|
|
17
17
|
|
|
18
18
|
export const PROVIDER_MODELS_CACHE_TTL_MS = 3 * 24 * 60 * 60 * 1000;
|
|
19
19
|
const PROVIDER_MODELS_CACHE_VERSION = 1;
|
|
20
|
-
const UNCACHED_PROVIDERS = new Set<LLMProvider>(['claude']);
|
|
20
|
+
const UNCACHED_PROVIDERS = new Set<LLMProvider>(['claude', 'gemini']);
|
|
21
21
|
|
|
22
22
|
type ProviderModelsServiceDependencies = {
|
|
23
23
|
resolveProvider?: (provider: LLMProvider) => Pick<IProvider, 'models'>;
|
|
@@ -4,10 +4,11 @@ import { promises as fsPromises } from 'node:fs';
|
|
|
4
4
|
|
|
5
5
|
import chokidar, { type FSWatcher } from 'chokidar';
|
|
6
6
|
|
|
7
|
+
import { projectsDb, sessionsDb } from '@/modules/database/index.js';
|
|
7
8
|
import { sessionSynchronizerService } from '@/modules/providers/services/session-synchronizer.service.js';
|
|
8
9
|
import { WS_OPEN_STATE, connectedClients } from '@/modules/websocket/index.js';
|
|
9
10
|
import type { LLMProvider } from '@/shared/types.js';
|
|
10
|
-
import {
|
|
11
|
+
import { generateDisplayName } from '@/modules/projects/index.js';
|
|
11
12
|
|
|
12
13
|
type WatcherEventType = 'add' | 'change';
|
|
13
14
|
|
|
@@ -58,6 +59,11 @@ const watchers: FSWatcher[] = [];
|
|
|
58
59
|
type PendingWatcherUpdate = {
|
|
59
60
|
providers: Set<LLMProvider>;
|
|
60
61
|
changeTypes: Set<WatcherEventType>;
|
|
62
|
+
/**
|
|
63
|
+
* Provider-native session ids reported by the synchronizers. They are
|
|
64
|
+
* translated back to app-facing session rows at flush time, because the
|
|
65
|
+
* transcript file names on disk only ever contain provider ids.
|
|
66
|
+
*/
|
|
61
67
|
updatedSessionIds: Set<string>;
|
|
62
68
|
};
|
|
63
69
|
|
|
@@ -131,6 +137,50 @@ function queuePendingWatcherUpdate(
|
|
|
131
137
|
schedulePendingWatcherFlush();
|
|
132
138
|
}
|
|
133
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Builds one `session_upserted` delta event for a provider-native session id.
|
|
142
|
+
*
|
|
143
|
+
* The event carries everything a sidebar needs to upsert the session in place
|
|
144
|
+
* (session summary plus owning-project metadata), so clients never need a full
|
|
145
|
+
* project-list refetch when a transcript file changes on disk. Returns `null`
|
|
146
|
+
* when the id cannot be resolved to an indexed session row.
|
|
147
|
+
*/
|
|
148
|
+
async function buildSessionUpsertedEvent(updatedProviderSessionId: string): Promise<string | null> {
|
|
149
|
+
const row = sessionsDb.getSessionByProviderSessionId(updatedProviderSessionId)
|
|
150
|
+
?? sessionsDb.getSessionById(updatedProviderSessionId);
|
|
151
|
+
if (!row || row.isArchived) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const projectPath = row.project_path;
|
|
156
|
+
const project = projectPath ? projectsDb.getProjectPath(projectPath) : null;
|
|
157
|
+
const displayName = project?.custom_project_name?.trim()
|
|
158
|
+
? project.custom_project_name
|
|
159
|
+
: await generateDisplayName(path.basename(projectPath ?? '') || (projectPath ?? ''), projectPath);
|
|
160
|
+
|
|
161
|
+
return JSON.stringify({
|
|
162
|
+
kind: 'session_upserted',
|
|
163
|
+
sessionId: row.session_id,
|
|
164
|
+
provider: row.provider,
|
|
165
|
+
session: {
|
|
166
|
+
id: row.session_id,
|
|
167
|
+
summary: row.custom_name || '',
|
|
168
|
+
messageCount: 0,
|
|
169
|
+
lastActivity: row.updated_at ?? row.created_at ?? new Date().toISOString(),
|
|
170
|
+
},
|
|
171
|
+
project: project
|
|
172
|
+
? {
|
|
173
|
+
projectId: project.project_id,
|
|
174
|
+
path: project.project_path,
|
|
175
|
+
fullPath: project.project_path,
|
|
176
|
+
displayName,
|
|
177
|
+
isStarred: Boolean(project.isStarred),
|
|
178
|
+
}
|
|
179
|
+
: null,
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
134
184
|
async function flushPendingWatcherUpdate(): Promise<void> {
|
|
135
185
|
clearPendingWatcherFlushTimer();
|
|
136
186
|
|
|
@@ -149,33 +199,29 @@ async function flushPendingWatcherUpdate(): Promise<void> {
|
|
|
149
199
|
watcherRefreshInFlight = true;
|
|
150
200
|
|
|
151
201
|
try {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
projects: updatedProjects,
|
|
161
|
-
timestamp: new Date().toISOString(),
|
|
162
|
-
changeType: changeTypes[0] ?? 'change',
|
|
163
|
-
updatedSessionId: updatedSessionIds[0] ?? undefined,
|
|
164
|
-
watchProvider: watchProviders[0] ?? undefined,
|
|
165
|
-
changeTypes,
|
|
166
|
-
updatedSessionIds,
|
|
167
|
-
watchProviders,
|
|
168
|
-
batched: true,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
connectedClients.forEach(client => {
|
|
172
|
-
if (client.readyState === WS_OPEN_STATE) {
|
|
173
|
-
client.send(updateMessage);
|
|
202
|
+
// Per-session deltas instead of full project snapshots: an upsert of one
|
|
203
|
+
// session can never clobber unrelated client state, so the frontend needs
|
|
204
|
+
// no "suppress updates while a run is active" protection logic.
|
|
205
|
+
const events: string[] = [];
|
|
206
|
+
for (const updatedSessionId of queuedUpdate.updatedSessionIds) {
|
|
207
|
+
const event = await buildSessionUpsertedEvent(updatedSessionId);
|
|
208
|
+
if (event) {
|
|
209
|
+
events.push(event);
|
|
174
210
|
}
|
|
175
|
-
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (events.length > 0) {
|
|
214
|
+
connectedClients.forEach(client => {
|
|
215
|
+
if (client.readyState === WS_OPEN_STATE) {
|
|
216
|
+
for (const event of events) {
|
|
217
|
+
client.send(event);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
176
222
|
} catch (error) {
|
|
177
223
|
const message = error instanceof Error ? error.message : String(error);
|
|
178
|
-
console.error('Session watcher refresh failed while broadcasting
|
|
224
|
+
console.error('Session watcher refresh failed while broadcasting session_upserted', { error: message });
|
|
179
225
|
} finally {
|
|
180
226
|
watcherRefreshInFlight = false;
|
|
181
227
|
|