@cloudcli-ai/cloudcli 1.33.3 → 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 +26 -6
- 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-models.provider.js +5 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.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/agent.js +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 +28 -6
- 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-models.provider.ts +5 -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/agent.js +1 -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-9n7A21AT.js +0 -1361
- package/dist/modelConstants.js +0 -848
- 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 -848
- /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
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
captureScreenshot,
|
|
6
|
+
executor,
|
|
7
|
+
type ClickButton,
|
|
8
|
+
type ExecutorTarget,
|
|
9
|
+
type Point,
|
|
10
|
+
type ScrollDirection,
|
|
11
|
+
} from '@/modules/computer-use/computer-executor.js';
|
|
12
|
+
import type { SemanticAdapter } from '@/modules/computer-use/semantics/adapters/semantic-adapter.js';
|
|
13
|
+
import { createMacOsSemanticAdapter } from '@/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js';
|
|
14
|
+
import { createWindowsSemanticAdapter } from '@/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js';
|
|
15
|
+
import { resolveSemanticHelper } from '@/modules/computer-use/semantics/helpers/semantic-helper-resolver.js';
|
|
16
|
+
import { semanticSessionStore } from '@/modules/computer-use/semantics/semantic-session-store.js';
|
|
17
|
+
import type { SemanticAppState, SemanticElement } from '@/modules/computer-use/semantics/semantic-types.js';
|
|
18
|
+
|
|
19
|
+
const execFileAsync = promisify(execFile);
|
|
20
|
+
const MAX_APP_STATE_ELEMENTS = 250;
|
|
21
|
+
let helperAdapter: SemanticAdapter | null | undefined;
|
|
22
|
+
|
|
23
|
+
function readString(value: unknown): string {
|
|
24
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function requireApp(input: Record<string, unknown>): string {
|
|
28
|
+
const app = readString(input.app);
|
|
29
|
+
if (!app) {
|
|
30
|
+
throw new Error('app is required.');
|
|
31
|
+
}
|
|
32
|
+
return app;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readNumber(value: unknown): number | undefined {
|
|
36
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readButton(value: unknown): ClickButton {
|
|
40
|
+
return value === 'right' || value === 'middle' ? value : 'left';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readClickCount(value: unknown): number {
|
|
44
|
+
const count = readNumber(value);
|
|
45
|
+
if (count === undefined) {
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
return Math.max(1, Math.min(5, Math.trunc(count)));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function readDirection(value: unknown): ScrollDirection {
|
|
52
|
+
return value === 'up' || value === 'left' || value === 'right' ? value : 'down';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readSessionId(input: Record<string, unknown>): string {
|
|
56
|
+
return readString(input.sessionId) || 'default';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function centerOf(element: SemanticElement): Point | null {
|
|
60
|
+
const bounds = element.bounds;
|
|
61
|
+
if (!bounds) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
x: Math.round(bounds.x + bounds.width / 2),
|
|
66
|
+
y: Math.round(bounds.y + bounds.height / 2),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getCachedElement(sessionId: string, app: string, index: string, stateId?: string): SemanticElement | null {
|
|
71
|
+
return semanticSessionStore.getElement(sessionId, app, index, stateId);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getPoint(input: Record<string, unknown>, sessionId: string, app: string): Point | undefined {
|
|
75
|
+
const x = readNumber(input.x);
|
|
76
|
+
const y = readNumber(input.y);
|
|
77
|
+
if (x !== undefined && y !== undefined) {
|
|
78
|
+
return { x, y };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const elementIndex = readString(input.element_index);
|
|
82
|
+
if (!elementIndex) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const element = getCachedElement(sessionId, app, elementIndex, readString(input.stateId) || undefined);
|
|
86
|
+
return element ? centerOf(element) || undefined : undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getHelperAdapter(): SemanticAdapter | null {
|
|
90
|
+
if (helperAdapter !== undefined) {
|
|
91
|
+
return helperAdapter;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (process.platform !== 'darwin' && process.platform !== 'win32') {
|
|
95
|
+
helperAdapter = null;
|
|
96
|
+
return helperAdapter;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const resolution = resolveSemanticHelper();
|
|
100
|
+
if (!resolution.available) {
|
|
101
|
+
helperAdapter = null;
|
|
102
|
+
return helperAdapter;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
helperAdapter = process.platform === 'darwin'
|
|
106
|
+
? createMacOsSemanticAdapter()
|
|
107
|
+
: createWindowsSemanticAdapter();
|
|
108
|
+
return helperAdapter;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function shouldFallbackFromHelper(error: unknown): boolean {
|
|
112
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
113
|
+
return /not implemented|unavailable|not found|does not exist|timed out|not running|exited with code|failed to start/i.test(message);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function withHelperState(
|
|
117
|
+
sessionId: string,
|
|
118
|
+
operation: (adapter: SemanticAdapter) => Promise<SemanticAppState>,
|
|
119
|
+
): Promise<SemanticAppState | null> {
|
|
120
|
+
const adapter = getHelperAdapter();
|
|
121
|
+
if (!adapter) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
return semanticSessionStore.save(sessionId, await operation(adapter));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (shouldFallbackFromHelper(error)) {
|
|
128
|
+
console.warn('[ComputerSemantics] Falling back from helper:', error instanceof Error ? error.message : String(error));
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function run(command: string, args: string[], timeout = 5000): Promise<string> {
|
|
136
|
+
const { stdout } = await execFileAsync(command, args, {
|
|
137
|
+
timeout,
|
|
138
|
+
windowsHide: true,
|
|
139
|
+
maxBuffer: 1024 * 1024 * 4,
|
|
140
|
+
});
|
|
141
|
+
return stdout;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function listMacApps(): Promise<Array<Record<string, unknown>>> {
|
|
145
|
+
const script = [
|
|
146
|
+
'tell application "System Events"',
|
|
147
|
+
'set appRows to {}',
|
|
148
|
+
'repeat with p in (application processes whose background only is false)',
|
|
149
|
+
'set end of appRows to (name of p as text)',
|
|
150
|
+
'end repeat',
|
|
151
|
+
'return appRows',
|
|
152
|
+
'end tell',
|
|
153
|
+
].join('\n');
|
|
154
|
+
const output = await run('osascript', ['-e', script]);
|
|
155
|
+
return output.split(', ')
|
|
156
|
+
.map((name) => name.trim())
|
|
157
|
+
.filter(Boolean)
|
|
158
|
+
.map((name) => ({ name, running: true }));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function listWindowsApps(): Promise<Array<Record<string, unknown>>> {
|
|
162
|
+
const script = [
|
|
163
|
+
'Get-Process | Where-Object { $_.MainWindowTitle } |',
|
|
164
|
+
'Select-Object ProcessName, Id, MainWindowTitle | ConvertTo-Json -Depth 3',
|
|
165
|
+
].join(' ');
|
|
166
|
+
const output = await run('powershell.exe', ['-NoProfile', '-Command', script]);
|
|
167
|
+
const parsed = JSON.parse(output || '[]');
|
|
168
|
+
const rows = Array.isArray(parsed) ? parsed : [parsed];
|
|
169
|
+
return rows.map((row) => ({
|
|
170
|
+
name: row.ProcessName,
|
|
171
|
+
pid: row.Id,
|
|
172
|
+
windowTitle: row.MainWindowTitle,
|
|
173
|
+
running: true,
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function listLinuxApps(): Promise<Array<Record<string, unknown>>> {
|
|
178
|
+
try {
|
|
179
|
+
const output = await run('wmctrl', ['-lx']);
|
|
180
|
+
return output.split(/\r?\n/)
|
|
181
|
+
.map((line) => line.trim())
|
|
182
|
+
.filter(Boolean)
|
|
183
|
+
.map((line) => {
|
|
184
|
+
const parts = line.split(/\s+/);
|
|
185
|
+
return {
|
|
186
|
+
windowId: parts[0],
|
|
187
|
+
desktop: parts[1],
|
|
188
|
+
host: parts[2],
|
|
189
|
+
className: parts[3],
|
|
190
|
+
windowTitle: parts.slice(4).join(' '),
|
|
191
|
+
running: true,
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
} catch {
|
|
195
|
+
const output = await run('ps', ['-eo', 'comm=']);
|
|
196
|
+
return [...new Set(output.split(/\r?\n/).map((name) => name.trim()).filter(Boolean))]
|
|
197
|
+
.slice(0, 200)
|
|
198
|
+
.map((name) => ({ name, running: true }));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function listApps(): Promise<Array<Record<string, unknown>>> {
|
|
203
|
+
if (process.platform === 'darwin') {
|
|
204
|
+
return listMacApps();
|
|
205
|
+
}
|
|
206
|
+
if (process.platform === 'win32') {
|
|
207
|
+
return listWindowsApps();
|
|
208
|
+
}
|
|
209
|
+
return listLinuxApps();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function macAccessibilityTree(app: string): Promise<SemanticElement[]> {
|
|
213
|
+
const escapedApp = app.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
214
|
+
const script = `
|
|
215
|
+
on safeText(v)
|
|
216
|
+
try
|
|
217
|
+
return v as text
|
|
218
|
+
on error
|
|
219
|
+
return ""
|
|
220
|
+
end try
|
|
221
|
+
end safeText
|
|
222
|
+
|
|
223
|
+
on emitElement(e, depth, maxDepth, counter)
|
|
224
|
+
if depth > maxDepth then return {}
|
|
225
|
+
set rows to {}
|
|
226
|
+
try
|
|
227
|
+
set roleText to my safeText(role of e)
|
|
228
|
+
on error
|
|
229
|
+
set roleText to "element"
|
|
230
|
+
end try
|
|
231
|
+
try
|
|
232
|
+
set titleText to my safeText(title of e)
|
|
233
|
+
on error
|
|
234
|
+
set titleText to ""
|
|
235
|
+
end try
|
|
236
|
+
try
|
|
237
|
+
set valueText to my safeText(value of e)
|
|
238
|
+
on error
|
|
239
|
+
set valueText to ""
|
|
240
|
+
end try
|
|
241
|
+
try
|
|
242
|
+
set posValue to position of e
|
|
243
|
+
set sizeValue to size of e
|
|
244
|
+
set boundsText to ((item 1 of posValue) as text) & "," & ((item 2 of posValue) as text) & "," & ((item 1 of sizeValue) as text) & "," & ((item 2 of sizeValue) as text)
|
|
245
|
+
on error
|
|
246
|
+
set boundsText to ""
|
|
247
|
+
end try
|
|
248
|
+
set end of rows to ((counter as text) & tab & roleText & tab & titleText & tab & valueText & tab & boundsText)
|
|
249
|
+
if counter > ${MAX_APP_STATE_ELEMENTS} then return rows
|
|
250
|
+
try
|
|
251
|
+
repeat with childElement in UI elements of e
|
|
252
|
+
set childRows to my emitElement(childElement, depth + 1, maxDepth, counter + (count of rows))
|
|
253
|
+
set rows to rows & childRows
|
|
254
|
+
if (count of rows) > ${MAX_APP_STATE_ELEMENTS} then return rows
|
|
255
|
+
end repeat
|
|
256
|
+
end try
|
|
257
|
+
return rows
|
|
258
|
+
end emitElement
|
|
259
|
+
|
|
260
|
+
tell application "System Events"
|
|
261
|
+
if not (exists process "${escapedApp}") then error "App is not running: ${escapedApp}"
|
|
262
|
+
tell process "${escapedApp}"
|
|
263
|
+
set rows to {}
|
|
264
|
+
repeat with w in windows
|
|
265
|
+
set rows to rows & my emitElement(w, 0, 4, (count of rows) + 1)
|
|
266
|
+
if (count of rows) > ${MAX_APP_STATE_ELEMENTS} then exit repeat
|
|
267
|
+
end repeat
|
|
268
|
+
return rows
|
|
269
|
+
end tell
|
|
270
|
+
end tell
|
|
271
|
+
`;
|
|
272
|
+
const output = await run('osascript', ['-e', script], 10000);
|
|
273
|
+
return output.split(/\r?\n|, /)
|
|
274
|
+
.map((line) => line.trim())
|
|
275
|
+
.filter(Boolean)
|
|
276
|
+
.map((line, index) => {
|
|
277
|
+
const [rawIndex, role, title, value, boundsText] = line.split('\t');
|
|
278
|
+
const boundsParts = (boundsText || '').split(',').map((part) => Number.parseFloat(part));
|
|
279
|
+
const hasBounds = boundsParts.length === 4 && boundsParts.every(Number.isFinite);
|
|
280
|
+
return {
|
|
281
|
+
index: rawIndex || String(index + 1),
|
|
282
|
+
role: role || 'element',
|
|
283
|
+
title: title || undefined,
|
|
284
|
+
value: value || undefined,
|
|
285
|
+
bounds: hasBounds
|
|
286
|
+
? { x: boundsParts[0], y: boundsParts[1], width: boundsParts[2], height: boundsParts[3] }
|
|
287
|
+
: undefined,
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function getAccessibilityTree(app: string): Promise<{ elements: SemanticElement[]; message?: string }> {
|
|
293
|
+
if (process.platform === 'darwin') {
|
|
294
|
+
try {
|
|
295
|
+
return { elements: await macAccessibilityTree(app) };
|
|
296
|
+
} catch (error) {
|
|
297
|
+
return { elements: [], message: error instanceof Error ? error.message : String(error) };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
elements: [],
|
|
303
|
+
message: 'Native accessibility tree capture is not implemented for this platform yet.',
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function getAppState(sessionId: string, app: string): Promise<SemanticAppState> {
|
|
308
|
+
if (!app) {
|
|
309
|
+
throw new Error('app is required.');
|
|
310
|
+
}
|
|
311
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.getAppState({ sessionId, app }));
|
|
312
|
+
if (helperState) {
|
|
313
|
+
return helperState;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const screenshot = await captureScreenshot();
|
|
317
|
+
const tree = await getAccessibilityTree(app);
|
|
318
|
+
const state: SemanticAppState = {
|
|
319
|
+
stateId: semanticSessionStore.createStateId(),
|
|
320
|
+
app,
|
|
321
|
+
platform: process.platform,
|
|
322
|
+
screenshotDataUrl: screenshot.dataUrl,
|
|
323
|
+
displaySize: screenshot.size,
|
|
324
|
+
elements: tree.elements,
|
|
325
|
+
accessibilityTree: tree.elements,
|
|
326
|
+
message: tree.message,
|
|
327
|
+
};
|
|
328
|
+
return semanticSessionStore.save(sessionId, state);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function targetFor(sessionId: string, app: string, stateId?: string): Promise<ExecutorTarget> {
|
|
332
|
+
const cached = semanticSessionStore.getState(sessionId, app, stateId);
|
|
333
|
+
return { displaySize: cached?.displaySize || (await captureScreenshot()).size };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export const computerSemanticsService = {
|
|
337
|
+
async callTool(name: string, input: Record<string, unknown>): Promise<unknown> {
|
|
338
|
+
const sessionId = readSessionId(input);
|
|
339
|
+
switch (name) {
|
|
340
|
+
case 'list_apps': {
|
|
341
|
+
const adapter = getHelperAdapter();
|
|
342
|
+
if (adapter) {
|
|
343
|
+
try {
|
|
344
|
+
return { apps: await adapter.listApps(), platform: process.platform };
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (!shouldFallbackFromHelper(error)) {
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
console.warn('[ComputerSemantics] Falling back from helper:', error instanceof Error ? error.message : String(error));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return { apps: await listApps(), platform: process.platform };
|
|
353
|
+
}
|
|
354
|
+
case 'get_app_state':
|
|
355
|
+
return getAppState(sessionId, readString(input.app));
|
|
356
|
+
case 'click':
|
|
357
|
+
case 'click_element': {
|
|
358
|
+
const app = requireApp(input);
|
|
359
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.clickElement({ ...input, sessionId, app }));
|
|
360
|
+
if (helperState) {
|
|
361
|
+
return helperState;
|
|
362
|
+
}
|
|
363
|
+
const stateId = readString(input.stateId) || undefined;
|
|
364
|
+
const point = getPoint(input, sessionId, app);
|
|
365
|
+
if (!point) {
|
|
366
|
+
throw new Error('click requires x/y or an element_index from computer_get_app_state.');
|
|
367
|
+
}
|
|
368
|
+
const target = await targetFor(sessionId, app, stateId);
|
|
369
|
+
const button = readButton(input.mouse_button ?? input.mouseButton);
|
|
370
|
+
const clickCount = readClickCount(input.click_count ?? input.clickCount);
|
|
371
|
+
for (let index = 0; index < clickCount; index += 1) {
|
|
372
|
+
await executor.click(target, button, point, false);
|
|
373
|
+
}
|
|
374
|
+
return getAppState(sessionId, app);
|
|
375
|
+
}
|
|
376
|
+
case 'drag': {
|
|
377
|
+
const app = requireApp(input);
|
|
378
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.drag({ ...input, sessionId, app }));
|
|
379
|
+
if (helperState) {
|
|
380
|
+
return helperState;
|
|
381
|
+
}
|
|
382
|
+
const stateId = readString(input.stateId) || undefined;
|
|
383
|
+
const fromX = readNumber(input.from_x);
|
|
384
|
+
const fromY = readNumber(input.from_y);
|
|
385
|
+
const toX = readNumber(input.to_x);
|
|
386
|
+
const toY = readNumber(input.to_y);
|
|
387
|
+
if (fromX === undefined || fromY === undefined || toX === undefined || toY === undefined) {
|
|
388
|
+
throw new Error('drag requires from_x/from_y/to_x/to_y.');
|
|
389
|
+
}
|
|
390
|
+
await executor.drag(await targetFor(sessionId, app, stateId), { x: fromX, y: fromY }, { x: toX, y: toY }, readButton(input.mouse_button ?? input.mouseButton));
|
|
391
|
+
return getAppState(sessionId, app);
|
|
392
|
+
}
|
|
393
|
+
case 'scroll':
|
|
394
|
+
case 'scroll_element': {
|
|
395
|
+
const app = requireApp(input);
|
|
396
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.scrollElement({ ...input, sessionId, app }));
|
|
397
|
+
if (helperState) {
|
|
398
|
+
return helperState;
|
|
399
|
+
}
|
|
400
|
+
const stateId = readString(input.stateId) || undefined;
|
|
401
|
+
const point = getPoint(input, sessionId, app);
|
|
402
|
+
if (!point) {
|
|
403
|
+
throw new Error('scroll requires x/y or an element_index from computer_get_app_state.');
|
|
404
|
+
}
|
|
405
|
+
await executor.scroll(await targetFor(sessionId, app, stateId), readDirection(input.direction), readNumber(input.pages) ?? 1, point);
|
|
406
|
+
return getAppState(sessionId, app);
|
|
407
|
+
}
|
|
408
|
+
case 'type_text': {
|
|
409
|
+
const app = requireApp(input);
|
|
410
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.typeText({ ...input, sessionId, app }));
|
|
411
|
+
if (helperState) {
|
|
412
|
+
return helperState;
|
|
413
|
+
}
|
|
414
|
+
await executor.type(readString(input.text));
|
|
415
|
+
return getAppState(sessionId, app);
|
|
416
|
+
}
|
|
417
|
+
case 'press_key': {
|
|
418
|
+
const app = requireApp(input);
|
|
419
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.pressKey({ ...input, sessionId, app }));
|
|
420
|
+
if (helperState) {
|
|
421
|
+
return helperState;
|
|
422
|
+
}
|
|
423
|
+
await executor.pressChord(readString(input.key));
|
|
424
|
+
return getAppState(sessionId, app);
|
|
425
|
+
}
|
|
426
|
+
case 'set_value': {
|
|
427
|
+
const app = requireApp(input);
|
|
428
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.setValue({ ...input, sessionId, app }));
|
|
429
|
+
if (helperState) {
|
|
430
|
+
return helperState;
|
|
431
|
+
}
|
|
432
|
+
const stateId = readString(input.stateId) || undefined;
|
|
433
|
+
const point = getPoint(input, sessionId, app);
|
|
434
|
+
if (!point) {
|
|
435
|
+
throw new Error('set_value requires x/y or an element_index from computer_get_app_state.');
|
|
436
|
+
}
|
|
437
|
+
await executor.click(await targetFor(sessionId, app, stateId), 'left', point, false);
|
|
438
|
+
await executor.pressChord(process.platform === 'darwin' ? 'cmd+a' : 'ctrl+a');
|
|
439
|
+
await executor.type(readString(input.value));
|
|
440
|
+
return getAppState(sessionId, app);
|
|
441
|
+
}
|
|
442
|
+
case 'perform_secondary_action': {
|
|
443
|
+
const app = requireApp(input);
|
|
444
|
+
const helperState = await withHelperState(sessionId, (adapter) => adapter.performSecondaryAction({ ...input, sessionId, app }));
|
|
445
|
+
if (helperState) {
|
|
446
|
+
return helperState;
|
|
447
|
+
}
|
|
448
|
+
const stateId = readString(input.stateId) || undefined;
|
|
449
|
+
const point = getPoint(input, sessionId, app);
|
|
450
|
+
if (!point) {
|
|
451
|
+
throw new Error('perform_secondary_action requires x/y or an element_index from computer_get_app_state.');
|
|
452
|
+
}
|
|
453
|
+
await executor.click(await targetFor(sessionId, app, stateId), 'right', point, false);
|
|
454
|
+
return getAppState(sessionId, app);
|
|
455
|
+
}
|
|
456
|
+
default:
|
|
457
|
+
throw new Error(`Unknown semantic Computer Use tool: ${name}`);
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
|
|
3
|
+
import { computerUseService } from '@/modules/computer-use/computer-use.service.js';
|
|
4
|
+
import { semanticOperationForMcpTool } from '@/modules/computer-use/semantics/semantic-tool-dispatcher.js';
|
|
5
|
+
|
|
6
|
+
const router = express.Router();
|
|
7
|
+
|
|
8
|
+
function readBearerToken(header: unknown): string | null {
|
|
9
|
+
if (typeof header !== 'string') {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const trimmed = header.trim();
|
|
13
|
+
const scheme = 'Bearer';
|
|
14
|
+
if (trimmed.slice(0, scheme.length).toLowerCase() !== scheme.toLowerCase()) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const separator = trimmed[scheme.length];
|
|
19
|
+
if (separator !== ' ' && separator !== '\t') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return trimmed.slice(scheme.length + 1).trimStart() || null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function toButton(value: unknown): 'left' | 'right' | 'middle' {
|
|
27
|
+
return value === 'right' || value === 'middle' ? value : 'left';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function toScrollDirection(value: unknown): 'up' | 'down' | 'left' | 'right' {
|
|
31
|
+
return value === 'down' || value === 'left' || value === 'right' ? value : 'up';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function point(input: Record<string, unknown>): { x: number; y: number } | undefined {
|
|
35
|
+
return typeof input.x === 'number' && typeof input.y === 'number'
|
|
36
|
+
? { x: input.x, y: input.y }
|
|
37
|
+
: undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function requireNumber(input: Record<string, unknown>, name: string): number {
|
|
41
|
+
const value = input[name];
|
|
42
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
43
|
+
throw new Error(`${name} is required and must be a finite number.`);
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function requirePoint(input: Record<string, unknown>): { x: number; y: number } {
|
|
49
|
+
return { x: requireNumber(input, 'x'), y: requireNumber(input, 'y') };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function requireNamedPoint(input: Record<string, unknown>, xName: string, yName: string): { x: number; y: number } {
|
|
53
|
+
return { x: requireNumber(input, xName), y: requireNumber(input, yName) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
router.use((req, res, next) => {
|
|
57
|
+
const expected = computerUseService.getMcpToken();
|
|
58
|
+
const token = readBearerToken(req.headers.authorization) || String(req.headers['x-computer-use-mcp-token'] || '');
|
|
59
|
+
if (!token || token !== expected) {
|
|
60
|
+
res.status(401).json({ success: false, error: 'Invalid Computer Use MCP token.' });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
next();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
router.post('/tools/:toolName', async (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const input = (req.body && typeof req.body === 'object' ? req.body : {}) as Record<string, unknown>;
|
|
69
|
+
const sessionId = typeof input.sessionId === 'string' ? input.sessionId : undefined;
|
|
70
|
+
const toolName = req.params.toolName;
|
|
71
|
+
const semanticOperation = semanticOperationForMcpTool(toolName);
|
|
72
|
+
let result: unknown;
|
|
73
|
+
|
|
74
|
+
if (semanticOperation) {
|
|
75
|
+
result = await computerUseService.callSemanticTool(semanticOperation, input);
|
|
76
|
+
res.json({ success: true, data: result });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
switch (toolName) {
|
|
81
|
+
case 'computer_screenshot':
|
|
82
|
+
result = await computerUseService.agentScreenshot(sessionId);
|
|
83
|
+
break;
|
|
84
|
+
case 'computer_cursor_position':
|
|
85
|
+
result = await computerUseService.agentCursorPosition(sessionId);
|
|
86
|
+
break;
|
|
87
|
+
case 'computer_mouse_move':
|
|
88
|
+
result = await computerUseService.agentMouseMove(sessionId, requirePoint(input));
|
|
89
|
+
break;
|
|
90
|
+
case 'computer_click':
|
|
91
|
+
result = await computerUseService.agentUnifiedClick(sessionId, {
|
|
92
|
+
button: toButton(input.mouseButton ?? input.mouse_button ?? input.button),
|
|
93
|
+
point: point(input),
|
|
94
|
+
clickCount: typeof input.clickCount === 'number'
|
|
95
|
+
? input.clickCount
|
|
96
|
+
: typeof input.click_count === 'number'
|
|
97
|
+
? input.click_count
|
|
98
|
+
: 1,
|
|
99
|
+
});
|
|
100
|
+
break;
|
|
101
|
+
case 'computer_drag': {
|
|
102
|
+
const from = requireNamedPoint(input, 'startX', 'startY');
|
|
103
|
+
const to = requireNamedPoint(input, 'endX', 'endY');
|
|
104
|
+
result = await computerUseService.agentDrag(sessionId, from, to, toButton(input.mouseButton ?? input.mouse_button ?? input.button));
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case 'computer_type':
|
|
108
|
+
result = await computerUseService.agentType(sessionId, String(input.text || ''));
|
|
109
|
+
break;
|
|
110
|
+
case 'computer_key':
|
|
111
|
+
result = await computerUseService.agentKey(sessionId, String(input.key || ''));
|
|
112
|
+
break;
|
|
113
|
+
case 'computer_scroll':
|
|
114
|
+
result = await computerUseService.agentScroll(sessionId, {
|
|
115
|
+
direction: toScrollDirection(input.direction),
|
|
116
|
+
amount: typeof input.amount === 'number' ? input.amount : undefined,
|
|
117
|
+
x: typeof input.x === 'number' ? input.x : undefined,
|
|
118
|
+
y: typeof input.y === 'number' ? input.y : undefined,
|
|
119
|
+
});
|
|
120
|
+
break;
|
|
121
|
+
case 'computer_wait':
|
|
122
|
+
result = await computerUseService.agentWait(sessionId, typeof input.timeoutMs === 'number' ? input.timeoutMs : undefined);
|
|
123
|
+
break;
|
|
124
|
+
case 'computer_close_session':
|
|
125
|
+
result = await computerUseService.agentStopSession(sessionId);
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
res.status(404).json({ success: false, error: `Unknown Computer Use MCP tool "${toolName}".` });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
res.json({ success: true, data: result });
|
|
133
|
+
} catch (error) {
|
|
134
|
+
res.status(400).json({
|
|
135
|
+
success: false,
|
|
136
|
+
error: error instanceof Error ? error.message : 'Computer Use MCP tool failed.',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
export default router;
|