@cloudcli-ai/cloudcli 1.36.3 → 1.37.1
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/dist/assets/index-D3jfzuKN.css +32 -0
- package/dist/assets/index-DTMWjJj8.js +1550 -0
- package/dist/index.html +2 -2
- package/dist-server/server/constants/config.js.map +1 -1
- package/dist-server/server/index.js +73 -1224
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/load-env.js +17 -4
- package/dist-server/server/load-env.js.map +1 -1
- package/dist-server/server/modules/agent/agent.module.js +37 -0
- package/dist-server/server/modules/agent/agent.module.js.map +1 -0
- package/dist-server/server/modules/agent/agent.routes.js +1161 -0
- package/dist-server/server/modules/agent/agent.routes.js.map +1 -0
- package/dist-server/server/modules/agent/index.js +3 -0
- package/dist-server/server/modules/agent/index.js.map +1 -0
- package/dist-server/server/modules/agent/tests/agent.routes.test.js +176 -0
- package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -0
- package/dist-server/server/modules/assets/assets.routes.js +56 -15
- package/dist-server/server/modules/assets/assets.routes.js.map +1 -1
- package/dist-server/server/modules/assets/index.js +2 -2
- package/dist-server/server/modules/assets/index.js.map +1 -1
- package/dist-server/server/modules/assets/services/image-assets.service.js +39 -1
- package/dist-server/server/modules/assets/services/image-assets.service.js.map +1 -1
- package/dist-server/server/modules/assets/tests/image-assets.service.test.js +21 -1
- package/dist-server/server/modules/assets/tests/image-assets.service.test.js.map +1 -1
- package/dist-server/server/{middleware/auth.js → modules/auth/auth.middleware.js} +30 -8
- package/dist-server/server/modules/auth/auth.middleware.js.map +1 -0
- package/dist-server/server/modules/auth/auth.module.js +29 -0
- package/dist-server/server/modules/auth/auth.module.js.map +1 -0
- package/dist-server/server/modules/auth/auth.routes.js +45 -0
- package/dist-server/server/modules/auth/auth.routes.js.map +1 -0
- package/dist-server/server/modules/auth/auth.service.js +113 -0
- package/dist-server/server/modules/auth/auth.service.js.map +1 -0
- package/dist-server/server/modules/auth/index.js +9 -0
- package/dist-server/server/modules/auth/index.js.map +1 -0
- package/dist-server/server/modules/auth/tests/auth.service.test.js +80 -0
- package/dist-server/server/modules/auth/tests/auth.service.test.js.map +1 -0
- package/dist-server/server/{browser-use-mcp.js → modules/browser-use/browser-use-mcp.js} +3 -1
- package/dist-server/server/modules/browser-use/browser-use-mcp.js.map +1 -0
- package/dist-server/server/modules/browser-use/browser-use.service.js +3 -4
- package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
- package/dist-server/server/modules/browser-use/index.js +9 -0
- package/dist-server/server/modules/browser-use/index.js.map +1 -0
- package/dist-server/server/modules/cli/cli.js +11 -0
- package/dist-server/server/modules/cli/cli.js.map +1 -0
- package/dist-server/server/modules/cli/cli.module.js +86 -0
- package/dist-server/server/modules/cli/cli.module.js.map +1 -0
- package/dist-server/server/modules/cli/cli.service.js +244 -0
- package/dist-server/server/modules/cli/cli.service.js.map +1 -0
- package/dist-server/server/modules/cli/index.js +3 -0
- package/dist-server/server/modules/cli/index.js.map +1 -0
- package/dist-server/server/modules/cli/sandbox.service.js +332 -0
- package/dist-server/server/modules/cli/sandbox.service.js.map +1 -0
- package/dist-server/server/modules/cli/tests/cli.service.test.js +78 -0
- package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -0
- package/dist-server/server/modules/cli/tests/sandbox.service.test.js +55 -0
- package/dist-server/server/modules/cli/tests/sandbox.service.test.js.map +1 -0
- package/dist-server/server/modules/commands/commands.module.js +20 -0
- package/dist-server/server/modules/commands/commands.module.js.map +1 -0
- package/dist-server/server/modules/commands/commands.routes.js +497 -0
- package/dist-server/server/modules/commands/commands.routes.js.map +1 -0
- package/dist-server/server/modules/commands/index.js +3 -0
- package/dist-server/server/modules/commands/index.js.map +1 -0
- package/dist-server/server/modules/commands/tests/commands.test.js +91 -0
- package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -0
- package/dist-server/server/modules/database/index.js +3 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +31 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/provider-models.js +112 -0
- package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +76 -7
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +29 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +102 -0
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -1
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +22 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js +95 -0
- package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -0
- package/dist-server/server/modules/file-tree/file-tree.routes.js +208 -0
- package/dist-server/server/modules/file-tree/file-tree.routes.js.map +1 -0
- package/dist-server/server/modules/file-tree/file-tree.service.js +576 -0
- package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -0
- package/dist-server/server/modules/file-tree/index.js +3 -0
- package/dist-server/server/modules/file-tree/index.js.map +1 -0
- package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js +123 -0
- package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js.map +1 -0
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +260 -0
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -0
- package/dist-server/server/modules/git/git-parsing.service.js +61 -0
- package/dist-server/server/modules/git/git-parsing.service.js.map +1 -0
- package/dist-server/server/modules/git/git.module.js +14 -0
- package/dist-server/server/modules/git/git.module.js.map +1 -0
- package/dist-server/server/modules/git/git.routes.js +1339 -0
- package/dist-server/server/modules/git/git.routes.js.map +1 -0
- package/dist-server/server/modules/git/index.js +3 -0
- package/dist-server/server/modules/git/index.js.map +1 -0
- package/dist-server/server/modules/git/tests/git-init.routes.test.js +48 -0
- package/dist-server/server/modules/git/tests/git-init.routes.test.js.map +1 -0
- package/dist-server/server/{routes → modules/git/tests}/git.test.js +1 -1
- package/dist-server/server/modules/git/tests/git.test.js.map +1 -0
- package/dist-server/server/modules/notifications/index.js +15 -1
- package/dist-server/server/modules/notifications/index.js.map +1 -1
- package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js +44 -0
- package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js.map +1 -0
- package/dist-server/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.js} +3 -2
- package/dist-server/server/modules/notifications/vapid-keys.service.js.map +1 -0
- package/dist-server/server/modules/plugins/index.js +9 -0
- package/dist-server/server/modules/plugins/index.js.map +1 -0
- package/dist-server/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.js} +3 -2
- package/dist-server/server/modules/plugins/plugin-process.service.js.map +1 -0
- package/dist-server/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.js} +2 -1
- package/dist-server/server/modules/plugins/plugin-registry.service.js.map +1 -0
- package/dist-server/server/modules/plugins/plugins.module.js +30 -0
- package/dist-server/server/modules/plugins/plugins.module.js.map +1 -0
- package/dist-server/server/modules/plugins/plugins.routes.js +70 -0
- package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -0
- package/dist-server/server/modules/plugins/plugins.service.js +123 -0
- package/dist-server/server/modules/plugins/plugins.service.js.map +1 -0
- package/dist-server/server/modules/plugins/tests/plugins.service.test.js +28 -0
- package/dist-server/server/modules/plugins/tests/plugins.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/index.js +5 -0
- package/dist-server/server/modules/projects/index.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +6 -0
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +47 -18
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +69 -55
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +6 -4
- 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 +23 -4
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +59 -75
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +48 -34
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +1 -54
- 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 +290 -6
- 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 +5 -0
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +33 -673
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
- package/dist-server/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +40 -28
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +21 -14
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +2 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +112 -333
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +38 -25
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -0
- package/dist-server/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +27 -4
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.test.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +0 -12
- 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 +11 -5
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +2 -0
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +169 -37
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +5 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +247 -184
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-runtime.service.js +66 -0
- package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js +268 -0
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +12 -4
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +2 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +116 -3
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js +2 -3
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +122 -0
- package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js +44 -0
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +76 -4
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +15 -126
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +5 -5
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/{provider-image-history.test.js → provider-attachment-history.test.js} +60 -2
- package/dist-server/server/modules/providers/tests/provider-attachment-history.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +241 -271
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js +126 -0
- package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +166 -0
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +126 -0
- package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions-details.test.js +62 -0
- package/dist-server/server/modules/providers/tests/sessions-details.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +87 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/skills.test.js +4 -2
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/settings/index.js +3 -0
- package/dist-server/server/modules/settings/index.js.map +1 -0
- package/dist-server/server/modules/settings/settings.module.js +35 -0
- package/dist-server/server/modules/settings/settings.module.js.map +1 -0
- package/dist-server/server/modules/settings/settings.routes.js +34 -0
- package/dist-server/server/modules/settings/settings.routes.js.map +1 -0
- package/dist-server/server/modules/settings/settings.service.js +116 -0
- package/dist-server/server/modules/settings/settings.service.js.map +1 -0
- package/dist-server/server/modules/settings/tests/settings.service.test.js +48 -0
- package/dist-server/server/modules/settings/tests/settings.service.test.js.map +1 -0
- package/dist-server/server/modules/system/index.js +3 -0
- package/dist-server/server/modules/system/index.js.map +1 -0
- package/dist-server/server/modules/system/system.module.js +44 -0
- package/dist-server/server/modules/system/system.module.js.map +1 -0
- package/dist-server/server/modules/system/system.routes.js +16 -0
- package/dist-server/server/modules/system/system.routes.js.map +1 -0
- package/dist-server/server/modules/system/system.service.js +45 -0
- package/dist-server/server/modules/system/system.service.js.map +1 -0
- package/dist-server/server/modules/system/tests/system.service.test.js +100 -0
- package/dist-server/server/modules/system/tests/system.service.test.js.map +1 -0
- package/dist-server/server/modules/taskmaster/index.js +3 -0
- package/dist-server/server/modules/taskmaster/index.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.module.js +20 -0
- package/dist-server/server/modules/taskmaster/taskmaster.module.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js +1372 -0
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.service.js +107 -0
- package/dist-server/server/modules/taskmaster/taskmaster.service.js.map +1 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +132 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js +85 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js.map +1 -0
- package/dist-server/server/modules/user/index.js +3 -0
- package/dist-server/server/modules/user/index.js.map +1 -0
- package/dist-server/server/modules/user/tests/user.service.test.js +53 -0
- package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -0
- package/dist-server/server/modules/user/user.module.js +47 -0
- package/dist-server/server/modules/user/user.module.js.map +1 -0
- package/dist-server/server/modules/user/user.routes.js +44 -0
- package/dist-server/server/modules/user/user.routes.js.map +1 -0
- package/dist-server/server/modules/user/user.service.js +59 -0
- package/dist-server/server/modules/user/user.service.js.map +1 -0
- package/dist-server/server/modules/voice/index.js +3 -0
- package/dist-server/server/modules/voice/index.js.map +1 -0
- package/dist-server/server/modules/voice/tests/voice.service.test.js +86 -0
- package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -0
- package/dist-server/server/modules/voice/voice.module.js +44 -0
- package/dist-server/server/modules/voice/voice.module.js.map +1 -0
- package/dist-server/server/modules/voice/voice.routes.js +87 -0
- package/dist-server/server/modules/voice/voice.routes.js.map +1 -0
- package/dist-server/server/modules/voice/voice.service.js +148 -0
- package/dist-server/server/modules/voice/voice.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +2 -2
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +44 -29
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +76 -8
- 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 +51 -20
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/{chat-image-filter.test.js → chat-attachment-filter.test.js} +10 -3
- package/dist-server/server/modules/websocket/tests/chat-attachment-filter.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +97 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js +61 -0
- package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/index.js +3 -0
- package/dist-server/server/modules/worktrees/index.js.map +1 -0
- package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js +37 -0
- package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js.map +1 -0
- package/dist-server/server/modules/worktrees/services/worktree-create.service.js +67 -0
- package/dist-server/server/modules/worktrees/services/worktree-create.service.js.map +1 -0
- package/dist-server/server/modules/worktrees/services/worktree-git.service.js +172 -0
- package/dist-server/server/modules/worktrees/services/worktree-git.service.js.map +1 -0
- package/dist-server/server/modules/worktrees/services/worktree-list.service.js +91 -0
- package/dist-server/server/modules/worktrees/services/worktree-list.service.js.map +1 -0
- package/dist-server/server/modules/worktrees/services/worktree-merge.service.js +118 -0
- package/dist-server/server/modules/worktrees/services/worktree-merge.service.js.map +1 -0
- package/dist-server/server/modules/worktrees/services/worktree-open.service.js +58 -0
- package/dist-server/server/modules/worktrees/services/worktree-open.service.js.map +1 -0
- package/dist-server/server/modules/worktrees/services/worktree-remove.service.js +65 -0
- package/dist-server/server/modules/worktrees/services/worktree-remove.service.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js +58 -0
- package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js +85 -0
- package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js +58 -0
- package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js +34 -0
- package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js +137 -0
- package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js +99 -0
- package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js +157 -0
- package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js +167 -0
- package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js.map +1 -0
- package/dist-server/server/modules/worktrees/worktrees.module.js +97 -0
- package/dist-server/server/modules/worktrees/worktrees.module.js.map +1 -0
- package/dist-server/server/modules/worktrees/worktrees.routes.js +84 -0
- package/dist-server/server/modules/worktrees/worktrees.routes.js.map +1 -0
- package/dist-server/server/shared/image-attachments.js +90 -17
- package/dist-server/server/shared/image-attachments.js.map +1 -1
- package/dist-server/server/shared/tests/image-attachments.test.js +43 -1
- package/dist-server/server/shared/tests/image-attachments.test.js.map +1 -1
- package/dist-server/server/shared/utils.js +66 -142
- package/dist-server/server/shared/utils.js.map +1 -1
- package/package.json +9 -5
- package/server/index.ts +401 -0
- package/server/{load-env.js → load-env.ts} +18 -5
- package/server/modules/agent/agent.module.ts +51 -0
- package/server/modules/agent/agent.routes.ts +1287 -0
- package/server/modules/agent/index.ts +2 -0
- package/server/modules/agent/tests/agent.routes.test.ts +205 -0
- package/server/modules/assets/assets.routes.ts +63 -16
- package/server/modules/assets/index.ts +2 -2
- package/server/modules/assets/services/image-assets.service.ts +46 -1
- package/server/modules/assets/tests/image-assets.service.test.ts +28 -0
- package/server/{middleware/auth.js → modules/auth/auth.middleware.ts} +38 -7
- package/server/modules/auth/auth.module.ts +38 -0
- package/server/modules/auth/auth.routes.ts +57 -0
- package/server/modules/auth/auth.service.ts +155 -0
- package/server/modules/auth/index.ts +9 -0
- package/server/modules/auth/tests/auth.service.test.ts +95 -0
- package/server/{browser-use-mcp.ts → modules/browser-use/browser-use-mcp.ts} +3 -1
- package/server/modules/browser-use/browser-use.service.ts +3 -4
- package/server/modules/browser-use/index.ts +8 -0
- package/server/modules/cli/cli.module.ts +96 -0
- package/server/modules/cli/cli.service.ts +281 -0
- package/server/modules/cli/cli.ts +13 -0
- package/server/modules/cli/index.ts +2 -0
- package/server/modules/cli/sandbox.service.ts +413 -0
- package/server/modules/cli/tests/cli.service.test.ts +91 -0
- package/server/modules/cli/tests/sandbox.service.test.ts +61 -0
- package/server/modules/commands/commands.module.ts +22 -0
- package/server/{routes/commands.js → modules/commands/commands.routes.ts} +46 -38
- package/server/modules/commands/index.ts +2 -0
- package/server/modules/commands/tests/commands.test.ts +114 -0
- package/server/modules/database/index.ts +3 -0
- package/server/modules/database/migrations.ts +35 -0
- package/server/modules/database/repositories/provider-models.ts +168 -0
- package/server/modules/database/repositories/sessions.db.ts +103 -8
- package/server/modules/database/schema.ts +30 -0
- package/server/modules/database/tests/provider-models.db.integration.test.ts +125 -0
- package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -1
- package/server/modules/database/tests/sessions.db.integration.test.ts +32 -0
- package/server/modules/file-tree/file-tree.module.ts +116 -0
- package/server/modules/file-tree/file-tree.routes.ts +258 -0
- package/server/modules/file-tree/file-tree.service.ts +664 -0
- package/server/modules/file-tree/index.ts +2 -0
- package/server/modules/file-tree/tests/file-tree.routes.test.ts +157 -0
- package/server/modules/file-tree/tests/file-tree.service.test.ts +317 -0
- package/server/modules/git/git-parsing.service.ts +66 -0
- package/server/modules/git/git.module.ts +22 -0
- package/server/{routes/git.js → modules/git/git.routes.ts} +91 -132
- package/server/modules/git/index.ts +2 -0
- package/server/modules/git/tests/git-init.routes.test.ts +52 -0
- package/server/{routes/git.test.js → modules/git/tests/git.test.ts} +3 -3
- package/server/modules/notifications/index.ts +9 -0
- package/server/modules/notifications/tests/notification-orchestrator.integration.test.ts +55 -0
- package/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.ts} +3 -1
- package/server/modules/plugins/index.ts +9 -0
- package/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.ts} +3 -1
- package/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.ts} +1 -0
- package/server/modules/plugins/plugins.module.ts +33 -0
- package/server/modules/plugins/plugins.routes.ts +66 -0
- package/server/modules/plugins/plugins.service.ts +147 -0
- package/server/modules/plugins/tests/plugins.service.test.ts +32 -0
- package/server/modules/projects/index.ts +5 -0
- package/server/modules/providers/README.md +41 -9
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +8 -0
- package/server/modules/providers/list/claude/claude-models.provider.ts +47 -26
- package/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +73 -59
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +6 -4
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +27 -4
- package/server/modules/providers/list/claude/claude.provider.ts +3 -0
- package/server/modules/providers/list/codex/codex-models.provider.ts +58 -108
- package/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +54 -36
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +1 -60
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +331 -7
- package/server/modules/providers/list/codex/codex-skills.provider.ts +5 -0
- package/server/modules/providers/list/codex/codex.provider.ts +3 -0
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +32 -719
- package/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +58 -27
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +26 -15
- package/server/modules/providers/list/cursor/cursor.provider.ts +3 -0
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +112 -423
- package/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +56 -28
- package/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +41 -3
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +0 -13
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +13 -5
- package/server/modules/providers/list/opencode/opencode.provider.ts +3 -0
- package/server/modules/providers/provider.routes.ts +228 -39
- package/server/modules/providers/services/provider-capabilities.service.ts +7 -1
- package/server/modules/providers/services/provider-models.service.ts +312 -269
- package/server/modules/providers/services/provider-runtime.service.ts +108 -0
- package/server/modules/providers/services/provider-token-usage.service.ts +357 -0
- package/server/modules/providers/services/session-conversations-search.service.ts +12 -4
- package/server/modules/providers/services/sessions-watcher.service.ts +2 -0
- package/server/modules/providers/services/sessions.service.ts +166 -3
- package/server/modules/providers/shared/base/abstract.provider.ts +4 -3
- package/server/modules/providers/tests/claude-auth.test.ts +150 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +61 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +83 -4
- package/server/modules/providers/tests/opencode-models.test.ts +34 -135
- package/server/modules/providers/tests/opencode-sessions.test.ts +5 -5
- package/server/modules/providers/tests/{provider-image-history.test.ts → provider-attachment-history.test.ts} +68 -1
- package/server/modules/providers/tests/provider-models.service.test.ts +300 -296
- package/server/modules/providers/tests/provider-runtime.service.test.ts +138 -0
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +186 -0
- package/server/modules/providers/tests/provider.routes.test.ts +183 -0
- package/server/modules/providers/tests/sessions-details.test.ts +73 -0
- package/server/modules/providers/tests/sessions.service.test.ts +127 -0
- package/server/modules/providers/tests/skills.test.ts +9 -2
- package/server/modules/settings/index.ts +2 -0
- package/server/modules/settings/settings.module.ts +50 -0
- package/server/modules/settings/settings.routes.ts +51 -0
- package/server/modules/settings/settings.service.ts +186 -0
- package/server/modules/settings/tests/settings.service.test.ts +54 -0
- package/server/modules/system/index.ts +2 -0
- package/server/modules/system/system.module.ts +62 -0
- package/server/modules/system/system.routes.ts +21 -0
- package/server/modules/system/system.service.ts +76 -0
- package/server/modules/system/tests/system.service.test.ts +118 -0
- package/server/modules/taskmaster/index.ts +2 -0
- package/server/modules/taskmaster/taskmaster.module.ts +24 -0
- package/server/modules/taskmaster/taskmaster.routes.ts +1482 -0
- package/server/modules/taskmaster/taskmaster.service.ts +132 -0
- package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +144 -0
- package/server/modules/taskmaster/tests/taskmaster.service.test.ts +97 -0
- package/server/modules/user/index.ts +2 -0
- package/server/modules/user/tests/user.service.test.ts +61 -0
- package/server/modules/user/user.module.ts +58 -0
- package/server/modules/user/user.routes.ts +50 -0
- package/server/modules/user/user.service.ts +85 -0
- package/server/modules/voice/index.ts +2 -0
- package/server/modules/voice/tests/voice.service.test.ts +98 -0
- package/server/modules/voice/voice.module.ts +47 -0
- package/server/modules/voice/voice.routes.ts +113 -0
- package/server/modules/voice/voice.service.ts +192 -0
- package/server/modules/websocket/README.md +7 -7
- package/server/modules/websocket/services/chat-session-writer.service.ts +2 -2
- package/server/modules/websocket/services/chat-websocket.service.ts +77 -61
- package/server/modules/websocket/services/shell-websocket.service.ts +88 -12
- package/server/modules/websocket/services/websocket-server.service.ts +63 -19
- package/server/modules/websocket/tests/{chat-image-filter.test.ts → chat-attachment-filter.test.ts} +17 -2
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +119 -0
- package/server/modules/websocket/tests/websocket-heartbeat.service.test.ts +78 -0
- package/server/modules/worktrees/index.ts +2 -0
- package/server/modules/worktrees/services/worktree-create-and-open.service.ts +53 -0
- package/server/modules/worktrees/services/worktree-create.service.ts +104 -0
- package/server/modules/worktrees/services/worktree-git.service.ts +207 -0
- package/server/modules/worktrees/services/worktree-list.service.ts +143 -0
- package/server/modules/worktrees/services/worktree-merge.service.ts +157 -0
- package/server/modules/worktrees/services/worktree-open.service.ts +85 -0
- package/server/modules/worktrees/services/worktree-remove.service.ts +88 -0
- package/server/modules/worktrees/tests/worktree-create-and-open.service.test.ts +68 -0
- package/server/modules/worktrees/tests/worktree-create.service.test.ts +142 -0
- package/server/modules/worktrees/tests/worktree-git.service.test.ts +84 -0
- package/server/modules/worktrees/tests/worktree-list.service.test.ts +45 -0
- package/server/modules/worktrees/tests/worktree-merge.service.test.ts +213 -0
- package/server/modules/worktrees/tests/worktree-open.service.test.ts +122 -0
- package/server/modules/worktrees/tests/worktree-remove.service.test.ts +227 -0
- package/server/modules/worktrees/tests/worktrees.routes.test.ts +199 -0
- package/server/modules/worktrees/worktrees.module.ts +113 -0
- package/server/modules/worktrees/worktrees.routes.ts +116 -0
- package/server/shared/image-attachments.ts +112 -18
- package/server/shared/interfaces.ts +33 -25
- package/server/shared/tests/image-attachments.test.ts +55 -0
- package/server/shared/types.ts +685 -36
- package/server/shared/utils.ts +77 -212
- package/dist/assets/index-CcCsKitq.js +0 -1423
- package/dist/assets/index-DLmhQ15L.css +0 -32
- package/dist-server/server/browser-use-mcp.js.map +0 -1
- package/dist-server/server/claude-sdk.js.map +0 -1
- package/dist-server/server/cli.js +0 -647
- package/dist-server/server/cli.js.map +0 -1
- package/dist-server/server/cursor-cli.js.map +0 -1
- package/dist-server/server/middleware/auth.js.map +0 -1
- package/dist-server/server/modules/providers/tests/provider-image-history.test.js.map +0 -1
- package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js.map +0 -1
- package/dist-server/server/openai-codex.js.map +0 -1
- package/dist-server/server/opencode-cli.js.map +0 -1
- package/dist-server/server/opencode-cli.test.js.map +0 -1
- package/dist-server/server/routes/agent.js +0 -1155
- package/dist-server/server/routes/agent.js.map +0 -1
- package/dist-server/server/routes/auth.js +0 -119
- package/dist-server/server/routes/auth.js.map +0 -1
- package/dist-server/server/routes/commands.js +0 -492
- package/dist-server/server/routes/commands.js.map +0 -1
- package/dist-server/server/routes/cursor.js +0 -49
- package/dist-server/server/routes/cursor.js.map +0 -1
- package/dist-server/server/routes/git.js +0 -1389
- package/dist-server/server/routes/git.js.map +0 -1
- package/dist-server/server/routes/git.test.js.map +0 -1
- package/dist-server/server/routes/mcp-utils.js +0 -29
- package/dist-server/server/routes/mcp-utils.js.map +0 -1
- package/dist-server/server/routes/plugins.js +0 -266
- package/dist-server/server/routes/plugins.js.map +0 -1
- package/dist-server/server/routes/settings.js +0 -249
- package/dist-server/server/routes/settings.js.map +0 -1
- package/dist-server/server/routes/taskmaster.js +0 -1362
- package/dist-server/server/routes/taskmaster.js.map +0 -1
- package/dist-server/server/routes/tests/commands.test.js +0 -76
- package/dist-server/server/routes/tests/commands.test.js.map +0 -1
- package/dist-server/server/routes/user.js +0 -116
- package/dist-server/server/routes/user.js.map +0 -1
- package/dist-server/server/services/notification-orchestrator.js +0 -2
- package/dist-server/server/services/notification-orchestrator.js.map +0 -1
- package/dist-server/server/services/tests/notification-orchestrator.test.js +0 -64
- package/dist-server/server/services/tests/notification-orchestrator.test.js.map +0 -1
- package/dist-server/server/services/vapid-keys.js.map +0 -1
- package/dist-server/server/utils/colors.js +0 -20
- package/dist-server/server/utils/colors.js.map +0 -1
- package/dist-server/server/utils/commandParser.js +0 -255
- package/dist-server/server/utils/commandParser.js.map +0 -1
- package/dist-server/server/utils/gitConfig.js +0 -37
- package/dist-server/server/utils/gitConfig.js.map +0 -1
- package/dist-server/server/utils/mcp-detector.js +0 -134
- package/dist-server/server/utils/mcp-detector.js.map +0 -1
- package/dist-server/server/utils/plugin-loader.js.map +0 -1
- package/dist-server/server/utils/plugin-process-manager.js.map +0 -1
- package/dist-server/server/utils/runtime-paths.js +0 -30
- package/dist-server/server/utils/runtime-paths.js.map +0 -1
- package/dist-server/server/utils/taskmaster-websocket.js +0 -124
- package/dist-server/server/utils/taskmaster-websocket.js.map +0 -1
- package/dist-server/server/utils/url-detection.js +0 -58
- package/dist-server/server/utils/url-detection.js.map +0 -1
- package/dist-server/server/voice-proxy.js +0 -226
- package/dist-server/server/voice-proxy.js.map +0 -1
- package/server/cli.js +0 -695
- package/server/index.js +0 -1651
- package/server/routes/agent.js +0 -1257
- package/server/routes/auth.js +0 -137
- package/server/routes/cursor.js +0 -52
- package/server/routes/mcp-utils.js +0 -31
- package/server/routes/plugins.js +0 -307
- package/server/routes/settings.js +0 -282
- package/server/routes/taskmaster.js +0 -1470
- package/server/routes/tests/commands.test.js +0 -82
- package/server/routes/user.js +0 -124
- package/server/services/notification-orchestrator.js +0 -7
- package/server/services/tests/notification-orchestrator.test.js +0 -80
- package/server/utils/colors.js +0 -21
- package/server/utils/commandParser.js +0 -305
- package/server/utils/gitConfig.js +0 -35
- package/server/utils/mcp-detector.js +0 -147
- package/server/utils/runtime-paths.js +0 -37
- package/server/utils/taskmaster-websocket.js +0 -135
- package/server/utils/url-detection.js +0 -71
- package/server/voice-proxy.js +0 -224
- /package/server/constants/{config.js → config.ts} +0 -0
|
@@ -3,13 +3,21 @@ import path from 'node:path';
|
|
|
3
3
|
import type { WebSocket } from 'ws';
|
|
4
4
|
|
|
5
5
|
import { sessionsDb } from '@/modules/database/index.js';
|
|
6
|
+
import { providerModelsService } from '@/modules/providers/index.js';
|
|
6
7
|
import { chatRunRegistry } from '@/modules/websocket/services/chat-run-registry.service.js';
|
|
7
8
|
import { connectedClients, WS_OPEN_STATE } from '@/modules/websocket/services/websocket-state.service.js';
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getGlobalImageAssetsDir,
|
|
11
|
+
isImageAttachmentDescriptor,
|
|
12
|
+
normalizeAttachmentDescriptors,
|
|
13
|
+
type ChatAttachmentDescriptor,
|
|
14
|
+
} from '@/shared/image-attachments.js';
|
|
9
15
|
import type {
|
|
10
16
|
AnyRecord,
|
|
11
17
|
AuthenticatedWebSocketRequest,
|
|
12
18
|
LLMProvider,
|
|
19
|
+
ProviderPermissionDecision,
|
|
20
|
+
ProviderRuntimeWriter,
|
|
13
21
|
} from '@/shared/types.js';
|
|
14
22
|
import { parseIncomingJsonObject } from '@/shared/utils.js';
|
|
15
23
|
|
|
@@ -23,10 +31,13 @@ import { parseIncomingJsonObject } from '@/shared/utils.js';
|
|
|
23
31
|
*
|
|
24
32
|
* Exported for tests; `assetsRootOverride` exists only for them.
|
|
25
33
|
*/
|
|
26
|
-
export function
|
|
34
|
+
export function filterAttachmentsToUploadStore(
|
|
35
|
+
attachments: unknown,
|
|
36
|
+
assetsRootOverride?: string,
|
|
37
|
+
): ChatAttachmentDescriptor[] {
|
|
27
38
|
const assetsRoot = path.resolve(assetsRootOverride ?? getGlobalImageAssetsDir());
|
|
28
39
|
|
|
29
|
-
return
|
|
40
|
+
return normalizeAttachmentDescriptors(attachments).filter((descriptor) => {
|
|
30
41
|
// Relative paths are anchored in the store; absolute ones must already be in it.
|
|
31
42
|
const resolved = path.resolve(assetsRoot, descriptor.path);
|
|
32
43
|
const relative = path.relative(assetsRoot, resolved);
|
|
@@ -38,43 +49,37 @@ export function filterImagesToUploadStore(images: unknown, assetsRootOverride?:
|
|
|
38
49
|
!relative.includes('/');
|
|
39
50
|
|
|
40
51
|
if (!isDirectChild) {
|
|
41
|
-
console.warn(`[Chat] Dropping
|
|
52
|
+
console.warn(`[Chat] Dropping attachment outside the upload store: ${descriptor.path}`);
|
|
42
53
|
}
|
|
43
54
|
return isDirectChild;
|
|
44
55
|
});
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
/** Backward-compatible image filter consumed by existing websocket tests. */
|
|
59
|
+
export function filterImagesToUploadStore(
|
|
60
|
+
images: unknown,
|
|
61
|
+
assetsRootOverride?: string,
|
|
62
|
+
): ChatAttachmentDescriptor[] {
|
|
63
|
+
return filterAttachmentsToUploadStore(images, assetsRootOverride);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Application boundary for dispatching provider runs and approvals. */
|
|
67
|
+
type ProviderRuntimeGateway = {
|
|
68
|
+
hasRuntime(provider: string): boolean;
|
|
69
|
+
run(
|
|
70
|
+
provider: LLMProvider,
|
|
71
|
+
command: string,
|
|
72
|
+
options: AnyRecord,
|
|
73
|
+
writer: ProviderRuntimeWriter,
|
|
74
|
+
): Promise<unknown>;
|
|
75
|
+
abort(provider: LLMProvider, sessionId: string): Promise<boolean>;
|
|
76
|
+
resolveToolApproval(requestId: string, payload: ProviderPermissionDecision): void;
|
|
77
|
+
getPendingApprovalsForSession(sessionId: string): unknown[];
|
|
78
|
+
};
|
|
57
79
|
|
|
58
80
|
type ChatWebSocketDependencies = {
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Abort functions keyed by provider id. They are addressed with the
|
|
63
|
-
* provider-native session id (that is how runtimes key their process maps).
|
|
64
|
-
* The Claude abort is async; the rest are sync — both shapes are accepted.
|
|
65
|
-
*/
|
|
66
|
-
abortFns: Record<LLMProvider, (providerSessionId: string) => boolean | Promise<boolean>>;
|
|
67
|
-
resolveToolApproval: (
|
|
68
|
-
requestId: string,
|
|
69
|
-
payload: {
|
|
70
|
-
allow: boolean;
|
|
71
|
-
updatedInput?: unknown;
|
|
72
|
-
message?: string;
|
|
73
|
-
rememberEntry?: unknown;
|
|
74
|
-
}
|
|
75
|
-
) => void;
|
|
76
|
-
/** Claude-only today: pending tool approvals included in `chat_subscribed`. */
|
|
77
|
-
getPendingApprovalsForSession: (providerSessionId: string) => unknown[];
|
|
81
|
+
/** Central dispatcher for every provider SDK/CLI runtime. */
|
|
82
|
+
runtime: ProviderRuntimeGateway;
|
|
78
83
|
};
|
|
79
84
|
|
|
80
85
|
/**
|
|
@@ -162,8 +167,7 @@ async function handleChatSend(
|
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
const provider = session.provider as LLMProvider;
|
|
165
|
-
|
|
166
|
-
if (!spawnFn) {
|
|
170
|
+
if (!dependencies.runtime.hasRuntime(provider)) {
|
|
167
171
|
sendProtocolError(ws, 'UNSUPPORTED_PROVIDER', `Provider "${provider}" is not available.`, sessionId);
|
|
168
172
|
return;
|
|
169
173
|
}
|
|
@@ -189,23 +193,46 @@ async function handleChatSend(
|
|
|
189
193
|
const clientOptions = (data.options ?? {}) as AnyRecord;
|
|
190
194
|
const command = typeof data.content === 'string' ? data.content : '';
|
|
191
195
|
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
+
// Record what this turn runs with so reopening the session later restores the
|
|
197
|
+
// same model and reasoning effort, and so the resume path has a
|
|
198
|
+
// session-scoped model answer to use.
|
|
199
|
+
if (typeof clientOptions.model === 'string' && clientOptions.model.trim()) {
|
|
200
|
+
providerModelsService.setSessionModel(provider, sessionId, clientOptions.model);
|
|
201
|
+
}
|
|
202
|
+
if (typeof clientOptions.effort === 'string' && clientOptions.effort.trim()) {
|
|
203
|
+
providerModelsService.setSessionEffort(provider, sessionId, clientOptions.effort);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const attachmentCandidates = [
|
|
207
|
+
...normalizeAttachmentDescriptors(clientOptions.images),
|
|
208
|
+
...normalizeAttachmentDescriptors(clientOptions.files),
|
|
209
|
+
...normalizeAttachmentDescriptors(clientOptions.attachments),
|
|
210
|
+
];
|
|
211
|
+
const verifiedAttachments = filterAttachmentsToUploadStore(attachmentCandidates);
|
|
212
|
+
const uniqueAttachments = verifiedAttachments.filter(
|
|
213
|
+
(descriptor, index, all) => all.findIndex((candidate) => candidate.path === descriptor.path) === index,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// The provider runtimes receive the stable app session id. When their
|
|
217
|
+
// CLI/SDK needs the provider-native id for resume, they resolve it from the
|
|
218
|
+
// session row themselves (sessionsService.resolveProviderSessionId).
|
|
219
|
+
// Brand-new sessions have no provider id yet, so the runtime starts fresh
|
|
220
|
+
// and announces one, which the gateway writer captures and maps back to the
|
|
221
|
+
// app session id.
|
|
196
222
|
const runtimeOptions: AnyRecord = {
|
|
197
223
|
...clientOptions,
|
|
198
|
-
//
|
|
199
|
-
// global upload store may reach
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
// Attachments are re-validated server-side: only direct children of the
|
|
225
|
+
// global upload store may reach provider runtimes or their file tools.
|
|
226
|
+
attachments: uniqueAttachments,
|
|
227
|
+
images: uniqueAttachments.filter(isImageAttachmentDescriptor),
|
|
228
|
+
files: uniqueAttachments.filter((descriptor) => !isImageAttachmentDescriptor(descriptor)),
|
|
229
|
+
sessionId,
|
|
203
230
|
cwd: clientOptions.cwd ?? session.project_path ?? undefined,
|
|
204
231
|
projectPath: session.project_path ?? clientOptions.projectPath,
|
|
205
232
|
};
|
|
206
233
|
|
|
207
234
|
try {
|
|
208
|
-
await
|
|
235
|
+
await dependencies.runtime.run(provider, command, runtimeOptions, run.writer);
|
|
209
236
|
} catch (error) {
|
|
210
237
|
const message = error instanceof Error ? error.message : String(error);
|
|
211
238
|
console.error(`[Chat] Provider runtime "${provider}" failed`, { sessionId, error: message });
|
|
@@ -241,11 +268,7 @@ async function handleChatAbort(
|
|
|
241
268
|
return;
|
|
242
269
|
}
|
|
243
270
|
|
|
244
|
-
const
|
|
245
|
-
let success = false;
|
|
246
|
-
if (abortFn && run.providerSessionId) {
|
|
247
|
-
success = Boolean(await abortFn(run.providerSessionId));
|
|
248
|
-
}
|
|
271
|
+
const success = await dependencies.runtime.abort(run.provider, sessionId);
|
|
249
272
|
|
|
250
273
|
chatRunRegistry.completeRun(sessionId, {
|
|
251
274
|
exitCode: success ? 0 : 1,
|
|
@@ -294,16 +317,9 @@ function handleChatSubscribe(
|
|
|
294
317
|
chatRunRegistry.attachConnection(sessionId, ws);
|
|
295
318
|
}
|
|
296
319
|
|
|
297
|
-
// Pending approvals are tracked under the
|
|
298
|
-
// Claude runtime
|
|
299
|
-
const pendingPermissions = (
|
|
300
|
-
? dependencies.getPendingApprovalsForSession(run.providerSessionId)
|
|
301
|
-
: []
|
|
302
|
-
).map((approval) =>
|
|
303
|
-
approval && typeof approval === 'object'
|
|
304
|
-
? { ...(approval as AnyRecord), sessionId }
|
|
305
|
-
: approval,
|
|
306
|
-
);
|
|
320
|
+
// Pending approvals are tracked under the app session id inside the
|
|
321
|
+
// Claude runtime, so they can be looked up directly.
|
|
322
|
+
const pendingPermissions = dependencies.runtime.getPendingApprovalsForSession(sessionId);
|
|
307
323
|
|
|
308
324
|
sendJson(ws, {
|
|
309
325
|
kind: 'chat_subscribed',
|
|
@@ -336,7 +352,7 @@ function handlePermissionResponse(data: AnyRecord, dependencies: ChatWebSocketDe
|
|
|
336
352
|
return;
|
|
337
353
|
}
|
|
338
354
|
|
|
339
|
-
dependencies.resolveToolApproval(data.requestId, {
|
|
355
|
+
dependencies.runtime.resolveToolApproval(data.requestId, {
|
|
340
356
|
allow: Boolean(data.allow),
|
|
341
357
|
updatedInput: data.updatedInput,
|
|
342
358
|
message: typeof data.message === 'string' ? data.message : undefined,
|
|
@@ -33,16 +33,79 @@ type PtySessionEntry = {
|
|
|
33
33
|
const ptySessionsMap = new Map<string, PtySessionEntry>();
|
|
34
34
|
const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
|
|
35
35
|
const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
|
|
36
|
+
const ANSI_ESCAPE_SEQUENCE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
|
|
37
|
+
const TRAILING_URL_PUNCTUATION_REGEX = /[)\]}>.,;:!?]+$/;
|
|
38
|
+
|
|
39
|
+
function stripAnsiSequences(value: string): string {
|
|
40
|
+
return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, '');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeDetectedUrl(url: string): string | null {
|
|
44
|
+
const cleanedUrl = url.trim().replace(TRAILING_URL_PUNCTUATION_REGEX, '');
|
|
45
|
+
if (!cleanedUrl) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const parsedUrl = new URL(cleanedUrl);
|
|
51
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return parsedUrl.toString();
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function extractUrlsFromText(value: string): string[] {
|
|
61
|
+
const directMatches = value.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/gi) ?? [];
|
|
62
|
+
|
|
63
|
+
// Terminal width can split a URL across lines, so valid URL characters on
|
|
64
|
+
// immediately following lines are joined before the URL is validated.
|
|
65
|
+
const wrappedMatches: string[] = [];
|
|
66
|
+
const urlContinuationPattern = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$/;
|
|
67
|
+
const lines = value.split(/\r?\n/);
|
|
68
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
69
|
+
const line = lines[lineIndex].trim();
|
|
70
|
+
const startMatch = line.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/i);
|
|
71
|
+
if (!startMatch) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let combinedUrl = startMatch[0];
|
|
76
|
+
let continuationIndex = lineIndex + 1;
|
|
77
|
+
while (continuationIndex < lines.length) {
|
|
78
|
+
const continuation = lines[continuationIndex].trim();
|
|
79
|
+
if (!continuation || !urlContinuationPattern.test(continuation)) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
combinedUrl += continuation;
|
|
83
|
+
continuationIndex += 1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
wrappedMatches.push(combinedUrl);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return Array.from(new Set([...directMatches, ...wrappedMatches]));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function shouldAutoOpenUrlFromOutput(value: string): boolean {
|
|
93
|
+
const normalizedOutput = value.toLowerCase();
|
|
94
|
+
return (
|
|
95
|
+
normalizedOutput.includes("browser didn't open") ||
|
|
96
|
+
normalizedOutput.includes('open this url') ||
|
|
97
|
+
normalizedOutput.includes('continue in your browser') ||
|
|
98
|
+
normalizedOutput.includes('press enter to open') ||
|
|
99
|
+
normalizedOutput.includes('open_url:')
|
|
100
|
+
);
|
|
101
|
+
}
|
|
36
102
|
|
|
37
103
|
type ShellWebSocketDependencies = {
|
|
38
104
|
resolveProviderSessionId: (
|
|
39
105
|
sessionId: string,
|
|
40
106
|
provider: string,
|
|
41
107
|
) => string | null | undefined;
|
|
42
|
-
|
|
43
|
-
normalizeDetectedUrl: (url: string) => string | null;
|
|
44
|
-
extractUrlsFromText: (content: string) => string[];
|
|
45
|
-
shouldAutoOpenUrlFromOutput: (content: string) => boolean;
|
|
108
|
+
spawnPty?: typeof pty.spawn;
|
|
46
109
|
};
|
|
47
110
|
|
|
48
111
|
/**
|
|
@@ -220,7 +283,8 @@ function prioritizeUserNpmGlobalBin(env: NodeJS.ProcessEnv): { key: string; valu
|
|
|
220
283
|
}
|
|
221
284
|
|
|
222
285
|
/**
|
|
223
|
-
*
|
|
286
|
+
* Used by this module's websocket gateway to connect the standalone Shell UI
|
|
287
|
+
* to a retained PTY while keeping process lifecycle ownership on the server.
|
|
224
288
|
*/
|
|
225
289
|
export function handleShellConnection(
|
|
226
290
|
ws: WebSocket,
|
|
@@ -284,6 +348,7 @@ export function handleShellConnection(
|
|
|
284
348
|
shellProcess = existingSession.pty;
|
|
285
349
|
if (existingSession.timeoutId) {
|
|
286
350
|
clearTimeout(existingSession.timeoutId);
|
|
351
|
+
existingSession.timeoutId = null;
|
|
287
352
|
}
|
|
288
353
|
|
|
289
354
|
ws.send(
|
|
@@ -334,7 +399,7 @@ export function handleShellConnection(
|
|
|
334
399
|
const termRows = readNumber(data.rows, 24);
|
|
335
400
|
const prioritizedPath = prioritizeUserNpmGlobalBin(process.env);
|
|
336
401
|
|
|
337
|
-
shellProcess = pty.spawn(shell, shellArgs, {
|
|
402
|
+
shellProcess = (dependencies.spawnPty ?? pty.spawn)(shell, shellArgs, {
|
|
338
403
|
name: 'xterm-256color',
|
|
339
404
|
cols: termCols,
|
|
340
405
|
rows: termRows,
|
|
@@ -376,7 +441,7 @@ export function handleShellConnection(
|
|
|
376
441
|
|
|
377
442
|
if (session.ws && session.ws.readyState === WebSocket.OPEN) {
|
|
378
443
|
let outputData = chunk;
|
|
379
|
-
const cleanChunk =
|
|
444
|
+
const cleanChunk = stripAnsiSequences(chunk);
|
|
380
445
|
urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
|
|
381
446
|
|
|
382
447
|
outputData = outputData.replace(
|
|
@@ -385,7 +450,7 @@ export function handleShellConnection(
|
|
|
385
450
|
);
|
|
386
451
|
|
|
387
452
|
const emitAuthUrl = (detectedUrl: string, autoOpen = false) => {
|
|
388
|
-
const normalizedUrl =
|
|
453
|
+
const normalizedUrl = normalizeDetectedUrl(detectedUrl);
|
|
389
454
|
if (!normalizedUrl) {
|
|
390
455
|
return;
|
|
391
456
|
}
|
|
@@ -403,8 +468,8 @@ export function handleShellConnection(
|
|
|
403
468
|
}
|
|
404
469
|
};
|
|
405
470
|
|
|
406
|
-
const normalizedDetectedUrls =
|
|
407
|
-
.map((url) =>
|
|
471
|
+
const normalizedDetectedUrls = extractUrlsFromText(urlDetectionBuffer)
|
|
472
|
+
.map((url) => normalizeDetectedUrl(url))
|
|
408
473
|
.filter((url): url is string => Boolean(url));
|
|
409
474
|
|
|
410
475
|
const dedupedDetectedUrls = Array.from(new Set(normalizedDetectedUrls)).filter(
|
|
@@ -415,7 +480,7 @@ export function handleShellConnection(
|
|
|
415
480
|
dedupedDetectedUrls.forEach((url) => emitAuthUrl(url, false));
|
|
416
481
|
|
|
417
482
|
if (
|
|
418
|
-
|
|
483
|
+
shouldAutoOpenUrlFromOutput(cleanChunk) &&
|
|
419
484
|
dedupedDetectedUrls.length > 0
|
|
420
485
|
) {
|
|
421
486
|
const bestUrl = dedupedDetectedUrls.reduce((longest, current) =>
|
|
@@ -522,9 +587,20 @@ export function handleShellConnection(
|
|
|
522
587
|
return;
|
|
523
588
|
}
|
|
524
589
|
|
|
590
|
+
// Mobile networks can deliver an old socket's close after its replacement
|
|
591
|
+
// has attached. Only the socket that currently owns the PTY may detach it.
|
|
592
|
+
if (session.ws !== ws) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
525
596
|
session.ws = null;
|
|
597
|
+
if (session.timeoutId) {
|
|
598
|
+
clearTimeout(session.timeoutId);
|
|
599
|
+
}
|
|
526
600
|
session.timeoutId = setTimeout(() => {
|
|
527
|
-
|
|
601
|
+
// A reconnect may win just as this timer becomes runnable. Re-check the
|
|
602
|
+
// active socket so a queued cleanup can never kill a reattached PTY.
|
|
603
|
+
if (ptySessionsMap.get(ptySessionKey as string) !== session || session.ws !== null) {
|
|
528
604
|
return;
|
|
529
605
|
}
|
|
530
606
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Server as HttpServer } from 'node:http';
|
|
2
2
|
|
|
3
|
-
import { WebSocketServer, type VerifyClientCallbackSync } from 'ws';
|
|
3
|
+
import { WebSocket, WebSocketServer, type VerifyClientCallbackSync } from 'ws';
|
|
4
4
|
|
|
5
5
|
import { handleChatConnection } from '@/modules/websocket/services/chat-websocket.service.js';
|
|
6
6
|
import { verifyWebSocketClient } from '@/modules/websocket/services/websocket-auth.service.js';
|
|
@@ -16,9 +16,69 @@ type WebSocketServerDependencies = {
|
|
|
16
16
|
getPluginPort: Parameters<typeof handlePluginWsProxy>[2];
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Used by this module's websocket gateway to keep active transports alive and
|
|
21
|
+
* close half-open connections so their route-specific clients can reconnect.
|
|
22
|
+
*/
|
|
23
|
+
export function attachWebSocketHeartbeat(
|
|
24
|
+
ws: WebSocket,
|
|
25
|
+
intervalMs = 30_000,
|
|
26
|
+
scheduler = {
|
|
27
|
+
setInterval,
|
|
28
|
+
clearInterval,
|
|
29
|
+
},
|
|
30
|
+
): () => void {
|
|
31
|
+
let isAlive = true;
|
|
32
|
+
let stopped = false;
|
|
33
|
+
|
|
34
|
+
const markAlive = () => {
|
|
35
|
+
isAlive = true;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const stopHeartbeat = () => {
|
|
39
|
+
if (stopped) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
stopped = true;
|
|
44
|
+
scheduler.clearInterval(heartbeat);
|
|
45
|
+
ws.off('pong', markAlive);
|
|
46
|
+
ws.off('close', stopHeartbeat);
|
|
47
|
+
ws.off('error', stopHeartbeat);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
ws.on('pong', markAlive);
|
|
51
|
+
ws.on('close', stopHeartbeat);
|
|
52
|
+
ws.on('error', stopHeartbeat);
|
|
53
|
+
|
|
54
|
+
const heartbeat = scheduler.setInterval(() => {
|
|
55
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// A socket that did not answer the previous ping is half-open from the
|
|
60
|
+
// server's perspective. Terminating it emits close and lets clients resume.
|
|
61
|
+
if (!isAlive) {
|
|
62
|
+
stopHeartbeat();
|
|
63
|
+
ws.terminate();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
isAlive = false;
|
|
68
|
+
try {
|
|
69
|
+
ws.ping();
|
|
70
|
+
} catch {
|
|
71
|
+
stopHeartbeat();
|
|
72
|
+
ws.terminate();
|
|
73
|
+
}
|
|
74
|
+
}, intervalMs);
|
|
75
|
+
|
|
76
|
+
return stopHeartbeat;
|
|
77
|
+
}
|
|
78
|
+
|
|
19
79
|
/**
|
|
20
80
|
* Creates and wires the server-wide websocket gateway used for chat, shell, and
|
|
21
|
-
* plugin proxy routes.
|
|
81
|
+
* plugin proxy routes. Exported through the websocket module for server startup.
|
|
22
82
|
*/
|
|
23
83
|
export function createWebSocketServer(
|
|
24
84
|
server: HttpServer,
|
|
@@ -32,23 +92,7 @@ export function createWebSocketServer(
|
|
|
32
92
|
});
|
|
33
93
|
|
|
34
94
|
wss.on('connection', (ws, request) => {
|
|
35
|
-
|
|
36
|
-
// AWS ALB 60s, nginx 60s, etc.). Without app-level pings these connections
|
|
37
|
-
// are silently torn down even when the UI is active, causing repeated
|
|
38
|
-
// reconnect cycles. ws library heartbeat is opt-in.
|
|
39
|
-
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
40
|
-
const heartbeat = setInterval(() => {
|
|
41
|
-
if (ws.readyState === ws.OPEN) {
|
|
42
|
-
try {
|
|
43
|
-
ws.ping();
|
|
44
|
-
} catch {
|
|
45
|
-
// socket may have been closed concurrently — interval will be cleared below
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}, HEARTBEAT_INTERVAL_MS);
|
|
49
|
-
const stopHeartbeat = () => clearInterval(heartbeat);
|
|
50
|
-
ws.on('close', stopHeartbeat);
|
|
51
|
-
ws.on('error', stopHeartbeat);
|
|
95
|
+
attachWebSocketHeartbeat(ws);
|
|
52
96
|
|
|
53
97
|
const incomingRequest = request as AuthenticatedWebSocketRequest;
|
|
54
98
|
const url = incomingRequest.url ?? '/';
|
package/server/modules/websocket/tests/{chat-image-filter.test.ts → chat-attachment-filter.test.ts}
RENAMED
|
@@ -3,7 +3,10 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import test from 'node:test';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
filterAttachmentsToUploadStore,
|
|
8
|
+
filterImagesToUploadStore,
|
|
9
|
+
} from '@/modules/websocket/services/chat-websocket.service.js';
|
|
7
10
|
|
|
8
11
|
const STORE = path.join(os.tmpdir(), 'cloudcli-assets-store');
|
|
9
12
|
|
|
@@ -37,8 +40,20 @@ test('paths outside the store, traversal, and subdirs are dropped', () => {
|
|
|
37
40
|
assert.deepEqual(result, []);
|
|
38
41
|
});
|
|
39
42
|
|
|
40
|
-
test('malformed payloads yield no
|
|
43
|
+
test('malformed payloads yield no attachments', () => {
|
|
41
44
|
assert.deepEqual(filterImagesToUploadStore(undefined, STORE), []);
|
|
42
45
|
assert.deepEqual(filterImagesToUploadStore('nope', STORE), []);
|
|
43
46
|
assert.deepEqual(filterImagesToUploadStore([{ name: 'no-path' }, 42], STORE), []);
|
|
44
47
|
});
|
|
48
|
+
|
|
49
|
+
test('general files inside the upload store preserve their metadata', () => {
|
|
50
|
+
const inside = path.join(STORE, 'brief.pdf');
|
|
51
|
+
const result = filterAttachmentsToUploadStore(
|
|
52
|
+
[{ path: inside, name: 'brief.pdf', mimeType: 'application/pdf', size: 2048 }],
|
|
53
|
+
STORE,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
assert.deepEqual(result, [
|
|
57
|
+
{ path: inside, name: 'brief.pdf', mimeType: 'application/pdf', size: 2048 },
|
|
58
|
+
]);
|
|
59
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
|
|
5
|
+
import { WebSocket } from 'ws';
|
|
6
|
+
|
|
7
|
+
import { handleShellConnection } from '@/modules/websocket/services/shell-websocket.service.js';
|
|
8
|
+
|
|
9
|
+
function createFakeSocket() {
|
|
10
|
+
const socket = new EventEmitter() as EventEmitter & {
|
|
11
|
+
readyState: number;
|
|
12
|
+
frames: string[];
|
|
13
|
+
send: (data: string) => void;
|
|
14
|
+
};
|
|
15
|
+
socket.readyState = WebSocket.OPEN;
|
|
16
|
+
socket.frames = [];
|
|
17
|
+
socket.send = (data: string) => socket.frames.push(data);
|
|
18
|
+
return socket;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createFakePty() {
|
|
22
|
+
let dataListener: ((data: string) => void) | null = null;
|
|
23
|
+
let exitListener: ((event: { exitCode: number; signal?: number }) => void) | null = null;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
killed: false,
|
|
27
|
+
onData(listener: (data: string) => void) {
|
|
28
|
+
dataListener = listener;
|
|
29
|
+
return { dispose: () => undefined };
|
|
30
|
+
},
|
|
31
|
+
onExit(listener: (event: { exitCode: number; signal?: number }) => void) {
|
|
32
|
+
exitListener = listener;
|
|
33
|
+
return { dispose: () => undefined };
|
|
34
|
+
},
|
|
35
|
+
emitData(data: string) {
|
|
36
|
+
dataListener?.(data);
|
|
37
|
+
},
|
|
38
|
+
emitExit() {
|
|
39
|
+
exitListener?.({ exitCode: 0 });
|
|
40
|
+
},
|
|
41
|
+
write() {},
|
|
42
|
+
resize() {},
|
|
43
|
+
kill() {
|
|
44
|
+
this.killed = true;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test('a stale socket close cannot detach the socket that replaced it', () => {
|
|
50
|
+
const pty = createFakePty();
|
|
51
|
+
const dependencies = {
|
|
52
|
+
resolveProviderSessionId: () => null,
|
|
53
|
+
spawnPty: () => pty as never,
|
|
54
|
+
};
|
|
55
|
+
const initMessage = JSON.stringify({
|
|
56
|
+
type: 'init',
|
|
57
|
+
projectPath: process.cwd(),
|
|
58
|
+
sessionId: `stale-close-${Date.now()}`,
|
|
59
|
+
hasSession: false,
|
|
60
|
+
provider: 'plain-shell',
|
|
61
|
+
isPlainShell: true,
|
|
62
|
+
initialCommand: 'test-command',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const firstSocket = createFakeSocket();
|
|
66
|
+
handleShellConnection(firstSocket as never, dependencies);
|
|
67
|
+
firstSocket.emit('message', initMessage);
|
|
68
|
+
|
|
69
|
+
const replacementSocket = createFakeSocket();
|
|
70
|
+
handleShellConnection(replacementSocket as never, dependencies);
|
|
71
|
+
replacementSocket.emit('message', initMessage);
|
|
72
|
+
replacementSocket.frames.length = 0;
|
|
73
|
+
|
|
74
|
+
// This ordering reproduces a delayed close from a backgrounded mobile tab.
|
|
75
|
+
firstSocket.emit('close');
|
|
76
|
+
pty.emitData('output-after-stale-close');
|
|
77
|
+
|
|
78
|
+
assert.equal(pty.killed, false);
|
|
79
|
+
assert.equal(replacementSocket.frames.length, 1);
|
|
80
|
+
assert.match(replacementSocket.frames[0], /output-after-stale-close/);
|
|
81
|
+
|
|
82
|
+
pty.emitExit();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('shell output detects and normalizes a wrapped authentication URL', () => {
|
|
86
|
+
const pty = createFakePty();
|
|
87
|
+
const socket = createFakeSocket();
|
|
88
|
+
const dependencies = {
|
|
89
|
+
resolveProviderSessionId: () => null,
|
|
90
|
+
spawnPty: () => pty as never,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
handleShellConnection(socket as never, dependencies);
|
|
94
|
+
socket.emit(
|
|
95
|
+
'message',
|
|
96
|
+
JSON.stringify({
|
|
97
|
+
type: 'init',
|
|
98
|
+
projectPath: process.cwd(),
|
|
99
|
+
sessionId: `wrapped-url-${Date.now()}`,
|
|
100
|
+
hasSession: false,
|
|
101
|
+
provider: 'plain-shell',
|
|
102
|
+
isPlainShell: true,
|
|
103
|
+
initialCommand: 'test-command',
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
socket.frames.length = 0;
|
|
107
|
+
|
|
108
|
+
pty.emitData("Continue in your browser: https://example.com/authorize?\ncode=abc\x1b[0m");
|
|
109
|
+
|
|
110
|
+
const frames = socket.frames.map((frame) => JSON.parse(frame) as Record<string, unknown>);
|
|
111
|
+
const authenticationFrame = frames.find((frame) => frame.type === 'auth_url');
|
|
112
|
+
assert.deepEqual(authenticationFrame, {
|
|
113
|
+
type: 'auth_url',
|
|
114
|
+
url: 'https://example.com/authorize?code=abc',
|
|
115
|
+
autoOpen: false,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
pty.emitExit();
|
|
119
|
+
});
|