mstro-app 0.5.0 → 0.5.5
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/LICENSE +129 -190
- package/PRIVACY.md +11 -11
- package/README.md +75 -28
- package/bin/commands/config.js +1 -2
- package/bin/mstro.js +55 -5
- package/bin/postinstall.js +0 -1
- package/dist/server/cli/eta-estimator.d.ts +55 -0
- package/dist/server/cli/eta-estimator.d.ts.map +1 -0
- package/dist/server/cli/eta-estimator.js +222 -0
- package/dist/server/cli/eta-estimator.js.map +1 -0
- package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-process.js +0 -1
- package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stall.js +0 -1
- package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stream.js +0 -1
- package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-tools.js +0 -1
- package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +0 -1
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -1
- package/dist/server/cli/headless/haiku-assessments.js +0 -1
- package/dist/server/cli/headless/haiku-assessments.js.map +1 -1
- package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
- package/dist/server/cli/headless/headless-logger.js +0 -1
- package/dist/server/cli/headless/headless-logger.js.map +1 -1
- package/dist/server/cli/headless/index.d.ts.map +1 -1
- package/dist/server/cli/headless/index.js +0 -1
- package/dist/server/cli/headless/index.js.map +1 -1
- package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -1
- package/dist/server/cli/headless/native-timeout-detector.js +0 -1
- package/dist/server/cli/headless/native-timeout-detector.js.map +1 -1
- package/dist/server/cli/headless/output-utils.d.ts.map +1 -1
- package/dist/server/cli/headless/output-utils.js +0 -1
- package/dist/server/cli/headless/output-utils.js.map +1 -1
- package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
- package/dist/server/cli/headless/prompt-utils.js +0 -1
- package/dist/server/cli/headless/prompt-utils.js.map +1 -1
- package/dist/server/cli/headless/resilient-runner.d.ts.map +1 -1
- package/dist/server/cli/headless/resilient-runner.js +0 -1
- package/dist/server/cli/headless/resilient-runner.js.map +1 -1
- package/dist/server/cli/headless/retry-strategies.d.ts.map +1 -1
- package/dist/server/cli/headless/retry-strategies.js +0 -1
- package/dist/server/cli/headless/retry-strategies.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +0 -1
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts +50 -0
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +64 -10
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts +21 -0
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +19 -13
- package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/headless/types.js +0 -1
- package/dist/server/cli/headless/types.js.map +1 -1
- package/dist/server/cli/improvisation-attachments.d.ts.map +1 -1
- package/dist/server/cli/improvisation-attachments.js +0 -1
- package/dist/server/cli/improvisation-attachments.js.map +1 -1
- package/dist/server/cli/improvisation-history-store.d.ts.map +1 -1
- package/dist/server/cli/improvisation-history-store.js +5 -2
- package/dist/server/cli/improvisation-history-store.js.map +1 -1
- package/dist/server/cli/improvisation-movements.d.ts.map +1 -1
- package/dist/server/cli/improvisation-movements.js +0 -1
- package/dist/server/cli/improvisation-movements.js.map +1 -1
- package/dist/server/cli/improvisation-output-queue.d.ts +5 -1
- package/dist/server/cli/improvisation-output-queue.d.ts.map +1 -1
- package/dist/server/cli/improvisation-output-queue.js +30 -8
- package/dist/server/cli/improvisation-output-queue.js.map +1 -1
- package/dist/server/cli/improvisation-retry.d.ts.map +1 -1
- package/dist/server/cli/improvisation-retry.js +0 -1
- package/dist/server/cli/improvisation-retry.js.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +29 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +50 -2
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/cli/improvisation-types.d.ts +2 -0
- package/dist/server/cli/improvisation-types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-types.js +0 -1
- package/dist/server/cli/improvisation-types.js.map +1 -1
- package/dist/server/cli/retry/retry-best-result.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-best-result.js +0 -1
- package/dist/server/cli/retry/retry-best-result.js.map +1 -1
- package/dist/server/cli/retry/retry-context-loss.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-context-loss.js +0 -1
- package/dist/server/cli/retry/retry-context-loss.js.map +1 -1
- package/dist/server/cli/retry/retry-premature-completion.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-premature-completion.js +1 -2
- package/dist/server/cli/retry/retry-premature-completion.js.map +1 -1
- package/dist/server/cli/retry/retry-recovery-strategies.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-recovery-strategies.js +0 -1
- package/dist/server/cli/retry/retry-recovery-strategies.js.map +1 -1
- package/dist/server/cli/retry/retry-resume-strategy.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-resume-strategy.js +0 -1
- package/dist/server/cli/retry/retry-resume-strategy.js.map +1 -1
- package/dist/server/cli/retry/retry-runner-factory.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-runner-factory.js +0 -1
- package/dist/server/cli/retry/retry-runner-factory.js.map +1 -1
- package/dist/server/cli/retry/retry-tool-results.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-tool-results.js +0 -1
- package/dist/server/cli/retry/retry-tool-results.js.map +1 -1
- package/dist/server/cli/retry/retry-types.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-types.js +0 -1
- package/dist/server/cli/retry/retry-types.js.map +1 -1
- package/dist/server/engines/EngineEvent.d.ts +126 -0
- package/dist/server/engines/EngineEvent.d.ts.map +1 -0
- package/dist/server/engines/EngineEvent.js +11 -0
- package/dist/server/engines/EngineEvent.js.map +1 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.d.ts +47 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.d.ts.map +1 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.js +338 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.js.map +1 -0
- package/dist/server/engines/factory.d.ts +21 -0
- package/dist/server/engines/factory.d.ts.map +1 -0
- package/dist/server/engines/factory.js +152 -0
- package/dist/server/engines/factory.js.map +1 -0
- package/dist/server/engines/opencode/OpenCodeEngine.d.ts +148 -0
- package/dist/server/engines/opencode/OpenCodeEngine.d.ts.map +1 -0
- package/dist/server/engines/opencode/OpenCodeEngine.js +630 -0
- package/dist/server/engines/opencode/OpenCodeEngine.js.map +1 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.d.ts +172 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.d.ts.map +1 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.js +390 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.js.map +1 -0
- package/dist/server/engines/opencode/model-catalog.d.ts +94 -0
- package/dist/server/engines/opencode/model-catalog.d.ts.map +1 -0
- package/dist/server/engines/opencode/model-catalog.js +141 -0
- package/dist/server/engines/opencode/model-catalog.js.map +1 -0
- package/dist/server/engines/types.d.ts +146 -0
- package/dist/server/engines/types.d.ts.map +1 -0
- package/dist/server/engines/types.js +4 -0
- package/dist/server/engines/types.js.map +1 -0
- package/dist/server/index.js +1 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-cli.js +0 -1
- package/dist/server/mcp/bouncer-cli.js.map +1 -1
- package/dist/server/mcp/bouncer-haiku.d.ts +17 -4
- package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-haiku.js +8 -125
- package/dist/server/mcp/bouncer-haiku.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts +45 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +69 -6
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/classifier/BouncerClassifier.d.ts +34 -0
- package/dist/server/mcp/classifier/BouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/BouncerClassifier.js +4 -0
- package/dist/server/mcp/classifier/BouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.d.ts +17 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +142 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.d.ts +68 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.js +182 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/factory.d.ts +70 -0
- package/dist/server/mcp/classifier/factory.d.ts.map +1 -0
- package/dist/server/mcp/classifier/factory.js +155 -0
- package/dist/server/mcp/classifier/factory.js.map +1 -0
- package/dist/server/mcp/security-analysis.d.ts.map +1 -1
- package/dist/server/mcp/security-analysis.js +0 -1
- package/dist/server/mcp/security-analysis.js.map +1 -1
- package/dist/server/mcp/security-audit.d.ts.map +1 -1
- package/dist/server/mcp/security-audit.js +0 -1
- package/dist/server/mcp/security-audit.js.map +1 -1
- package/dist/server/mcp/security-patterns.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.js +0 -1
- package/dist/server/mcp/security-patterns.js.map +1 -1
- package/dist/server/mcp/server.js +0 -1
- package/dist/server/mcp/server.js.map +1 -1
- package/dist/server/routes/files.d.ts.map +1 -1
- package/dist/server/routes/files.js +0 -1
- package/dist/server/routes/files.js.map +1 -1
- package/dist/server/routes/improvise.d.ts.map +1 -1
- package/dist/server/routes/improvise.js +0 -1
- package/dist/server/routes/improvise.js.map +1 -1
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +0 -1
- package/dist/server/routes/index.js.map +1 -1
- package/dist/server/routes/instances.d.ts.map +1 -1
- package/dist/server/routes/instances.js +0 -1
- package/dist/server/routes/instances.js.map +1 -1
- package/dist/server/routes/notifications.d.ts.map +1 -1
- package/dist/server/routes/notifications.js +0 -1
- package/dist/server/routes/notifications.js.map +1 -1
- package/dist/server/server-setup.d.ts.map +1 -1
- package/dist/server/server-setup.js +0 -1
- package/dist/server/server-setup.js.map +1 -1
- package/dist/server/services/analytics.d.ts.map +1 -1
- package/dist/server/services/analytics.js +0 -1
- package/dist/server/services/analytics.js.map +1 -1
- package/dist/server/services/auth.d.ts.map +1 -1
- package/dist/server/services/auth.js +0 -1
- package/dist/server/services/auth.js.map +1 -1
- package/dist/server/services/client-id.d.ts.map +1 -1
- package/dist/server/services/client-id.js +0 -1
- package/dist/server/services/client-id.js.map +1 -1
- package/dist/server/services/file-explorer-ops.d.ts.map +1 -1
- package/dist/server/services/file-explorer-ops.js +0 -1
- package/dist/server/services/file-explorer-ops.js.map +1 -1
- package/dist/server/services/files.d.ts.map +1 -1
- package/dist/server/services/files.js +0 -1
- package/dist/server/services/files.js.map +1 -1
- package/dist/server/services/instances.d.ts.map +1 -1
- package/dist/server/services/instances.js +0 -1
- package/dist/server/services/instances.js.map +1 -1
- package/dist/server/services/pathUtils.d.ts.map +1 -1
- package/dist/server/services/pathUtils.js +0 -1
- package/dist/server/services/pathUtils.js.map +1 -1
- package/dist/server/services/plan/agent-loader.d.ts.map +1 -1
- package/dist/server/services/plan/agent-loader.js +0 -1
- package/dist/server/services/plan/agent-loader.js.map +1 -1
- package/dist/server/services/plan/agent-resolver.d.ts +26 -0
- package/dist/server/services/plan/agent-resolver.d.ts.map +1 -0
- package/dist/server/services/plan/agent-resolver.js +102 -0
- package/dist/server/services/plan/agent-resolver.js.map +1 -0
- package/dist/server/services/plan/board-config.d.ts.map +1 -1
- package/dist/server/services/plan/board-config.js +0 -1
- package/dist/server/services/plan/board-config.js.map +1 -1
- package/dist/server/services/plan/composer.d.ts.map +1 -1
- package/dist/server/services/plan/composer.js +59 -12
- package/dist/server/services/plan/composer.js.map +1 -1
- package/dist/server/services/plan/config-installer.d.ts.map +1 -1
- package/dist/server/services/plan/config-installer.js +0 -1
- package/dist/server/services/plan/config-installer.js.map +1 -1
- package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
- package/dist/server/services/plan/dependency-resolver.js +0 -1
- package/dist/server/services/plan/dependency-resolver.js.map +1 -1
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +48 -4
- package/dist/server/services/plan/executor.js.map +1 -1
- package/dist/server/services/plan/front-matter.d.ts.map +1 -1
- package/dist/server/services/plan/front-matter.js +0 -1
- package/dist/server/services/plan/front-matter.js.map +1 -1
- package/dist/server/services/plan/issue-classification.d.ts.map +1 -1
- package/dist/server/services/plan/issue-classification.js +0 -1
- package/dist/server/services/plan/issue-classification.js.map +1 -1
- package/dist/server/services/plan/issue-loader.d.ts.map +1 -1
- package/dist/server/services/plan/issue-loader.js +0 -1
- package/dist/server/services/plan/issue-loader.js.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.js +33 -2
- package/dist/server/services/plan/issue-prompt-builder.js.map +1 -1
- package/dist/server/services/plan/issue-retry.d.ts +3 -1
- package/dist/server/services/plan/issue-retry.d.ts.map +1 -1
- package/dist/server/services/plan/issue-retry.js +2 -1
- package/dist/server/services/plan/issue-retry.js.map +1 -1
- package/dist/server/services/plan/issue-writer.d.ts.map +1 -1
- package/dist/server/services/plan/issue-writer.js +0 -1
- package/dist/server/services/plan/issue-writer.js.map +1 -1
- package/dist/server/services/plan/output-manager.d.ts.map +1 -1
- package/dist/server/services/plan/output-manager.js +0 -1
- package/dist/server/services/plan/output-manager.js.map +1 -1
- package/dist/server/services/plan/parser-core.d.ts.map +1 -1
- package/dist/server/services/plan/parser-core.js +1 -1
- package/dist/server/services/plan/parser-core.js.map +1 -1
- package/dist/server/services/plan/parser-migration.d.ts.map +1 -1
- package/dist/server/services/plan/parser-migration.js +0 -1
- package/dist/server/services/plan/parser-migration.js.map +1 -1
- package/dist/server/services/plan/parser.d.ts.map +1 -1
- package/dist/server/services/plan/parser.js +0 -1
- package/dist/server/services/plan/parser.js.map +1 -1
- package/dist/server/services/plan/progress-log.d.ts.map +1 -1
- package/dist/server/services/plan/progress-log.js +0 -1
- package/dist/server/services/plan/progress-log.js.map +1 -1
- package/dist/server/services/plan/prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/prompt-builder.js +0 -1
- package/dist/server/services/plan/prompt-builder.js.map +1 -1
- package/dist/server/services/plan/readiness-planner.d.ts.map +1 -1
- package/dist/server/services/plan/readiness-planner.js +0 -1
- package/dist/server/services/plan/readiness-planner.js.map +1 -1
- package/dist/server/services/plan/review-gate.d.ts.map +1 -1
- package/dist/server/services/plan/review-gate.js +0 -1
- package/dist/server/services/plan/review-gate.js.map +1 -1
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
- package/dist/server/services/plan/state-reconciler.js +0 -1
- package/dist/server/services/plan/state-reconciler.js.map +1 -1
- package/dist/server/services/plan/types.d.ts +1 -0
- package/dist/server/services/plan/types.d.ts.map +1 -1
- package/dist/server/services/plan/types.js +0 -1
- package/dist/server/services/plan/types.js.map +1 -1
- package/dist/server/services/plan/watcher.d.ts.map +1 -1
- package/dist/server/services/plan/watcher.js +0 -1
- package/dist/server/services/plan/watcher.js.map +1 -1
- package/dist/server/services/platform-credentials.d.ts.map +1 -1
- package/dist/server/services/platform-credentials.js +0 -1
- package/dist/server/services/platform-credentials.js.map +1 -1
- package/dist/server/services/platform-token-lifecycle.d.ts +70 -0
- package/dist/server/services/platform-token-lifecycle.d.ts.map +1 -0
- package/dist/server/services/platform-token-lifecycle.js +156 -0
- package/dist/server/services/platform-token-lifecycle.js.map +1 -0
- package/dist/server/services/platform.d.ts +21 -56
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js +98 -142
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/sentry.d.ts.map +1 -1
- package/dist/server/services/sentry.js +0 -1
- package/dist/server/services/sentry.js.map +1 -1
- package/dist/server/services/settings.d.ts +76 -2
- package/dist/server/services/settings.d.ts.map +1 -1
- package/dist/server/services/settings.js +127 -5
- package/dist/server/services/settings.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +0 -1
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/terminal/pty-utils.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-utils.js +0 -1
- package/dist/server/services/terminal/pty-utils.js.map +1 -1
- package/dist/server/services/websocket/autocomplete.d.ts.map +1 -1
- package/dist/server/services/websocket/autocomplete.js +0 -1
- package/dist/server/services/websocket/autocomplete.js.map +1 -1
- package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-definition-handlers.js +0 -1
- package/dist/server/services/websocket/file-definition-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-download-handler.d.ts.map +1 -1
- package/dist/server/services/websocket/file-download-handler.js +0 -1
- package/dist/server/services/websocket/file-download-handler.js.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.js +0 -1
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-search-handlers.js +0 -1
- package/dist/server/services/websocket/file-search-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-upload-handler.d.ts +2 -3
- package/dist/server/services/websocket/file-upload-handler.d.ts.map +1 -1
- package/dist/server/services/websocket/file-upload-handler.js +4 -7
- package/dist/server/services/websocket/file-upload-handler.js.map +1 -1
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/file-utils.js +0 -1
- package/dist/server/services/websocket/file-utils.js.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.js +19 -7
- package/dist/server/services/websocket/git-branch-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-diff-handlers.js +0 -1
- package/dist/server/services/websocket/git-diff-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-handlers.js +58 -6
- package/dist/server/services/websocket/git-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-head-watcher.d.ts.map +1 -1
- package/dist/server/services/websocket/git-head-watcher.js +0 -1
- package/dist/server/services/websocket/git-head-watcher.js.map +1 -1
- package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-log-handlers.js +0 -1
- package/dist/server/services/websocket/git-log-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.js +0 -1
- package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-tag-handlers.js +0 -1
- package/dist/server/services/websocket/git-tag-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-utils.d.ts +18 -3
- package/dist/server/services/websocket/git-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/git-utils.js +58 -8
- package/dist/server/services/websocket/git-utils.js.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.js +230 -14
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler-context.d.ts.map +1 -1
- package/dist/server/services/websocket/handler-context.js +0 -1
- package/dist/server/services/websocket/handler-context.js.map +1 -1
- package/dist/server/services/websocket/handler.d.ts +17 -1
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +57 -6
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/index.d.ts.map +1 -1
- package/dist/server/services/websocket/index.js +0 -1
- package/dist/server/services/websocket/index.js.map +1 -1
- package/dist/server/services/websocket/msg-id-tracker.d.ts.map +1 -1
- package/dist/server/services/websocket/msg-id-tracker.js +0 -1
- package/dist/server/services/websocket/msg-id-tracker.js.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.js +0 -1
- package/dist/server/services/websocket/plan-board-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.js +6 -2
- package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-handlers.js +0 -1
- package/dist/server/services/websocket/plan-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-helpers.js +0 -1
- package/dist/server/services/websocket/plan-helpers.js.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.js +0 -1
- package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js +0 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-complexity.js +78 -27
- package/dist/server/services/websocket/quality-complexity.js.map +1 -1
- package/dist/server/services/websocket/quality-eta.d.ts +47 -0
- package/dist/server/services/websocket/quality-eta.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-eta.js +110 -0
- package/dist/server/services/websocket/quality-eta.js.map +1 -0
- package/dist/server/services/websocket/quality-grading.d.ts +69 -0
- package/dist/server/services/websocket/quality-grading.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-grading.js +650 -0
- package/dist/server/services/websocket/quality-grading.js.map +1 -0
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-handlers.js +145 -8
- package/dist/server/services/websocket/quality-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-linting.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-linting.js +0 -1
- package/dist/server/services/websocket/quality-linting.js.map +1 -1
- package/dist/server/services/websocket/quality-operations.d.ts +34 -0
- package/dist/server/services/websocket/quality-operations.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-operations.js +47 -0
- package/dist/server/services/websocket/quality-operations.js.map +1 -0
- package/dist/server/services/websocket/quality-persistence.d.ts +23 -0
- package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-persistence.js +38 -12
- package/dist/server/services/websocket/quality-persistence.js.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.js +105 -57
- package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
- package/dist/server/services/websocket/quality-service.d.ts +12 -2
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-service.js +387 -72
- package/dist/server/services/websocket/quality-service.js.map +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts +22 -1
- package/dist/server/services/websocket/quality-tools.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-tools.js +55 -3
- package/dist/server/services/websocket/quality-tools.js.map +1 -1
- package/dist/server/services/websocket/quality-types.d.ts +52 -3
- package/dist/server/services/websocket/quality-types.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-types.js +1 -2
- package/dist/server/services/websocket/quality-types.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +3 -1
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +57 -10
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/session-history.d.ts.map +1 -1
- package/dist/server/services/websocket/session-history.js +3 -1
- package/dist/server/services/websocket/session-history.js.map +1 -1
- package/dist/server/services/websocket/session-initialization.d.ts.map +1 -1
- package/dist/server/services/websocket/session-initialization.js +158 -43
- package/dist/server/services/websocket/session-initialization.js.map +1 -1
- package/dist/server/services/websocket/session-registry.d.ts +25 -0
- package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
- package/dist/server/services/websocket/session-registry.js +19 -1
- package/dist/server/services/websocket/session-registry.js.map +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/settings-handlers.js +35 -5
- package/dist/server/services/websocket/settings-handlers.js.map +1 -1
- package/dist/server/services/websocket/skill-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/skill-handlers.js +0 -1
- package/dist/server/services/websocket/skill-handlers.js.map +1 -1
- package/dist/server/services/websocket/skill-watcher.d.ts.map +1 -1
- package/dist/server/services/websocket/skill-watcher.js +0 -1
- package/dist/server/services/websocket/skill-watcher.js.map +1 -1
- package/dist/server/services/websocket/tab-broadcast.d.ts +7 -2
- package/dist/server/services/websocket/tab-broadcast.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-broadcast.js +10 -3
- package/dist/server/services/websocket/tab-broadcast.js.map +1 -1
- package/dist/server/services/websocket/tab-event-buffer.d.ts +97 -8
- package/dist/server/services/websocket/tab-event-buffer.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-event-buffer.js +138 -13
- package/dist/server/services/websocket/tab-event-buffer.js.map +1 -1
- package/dist/server/services/websocket/tab-event-replay.d.ts +29 -13
- package/dist/server/services/websocket/tab-event-replay.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-event-replay.js +55 -3
- package/dist/server/services/websocket/tab-event-replay.js.map +1 -1
- package/dist/server/services/websocket/tab-handlers.d.ts +9 -1
- package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js +47 -3
- package/dist/server/services/websocket/tab-handlers.js.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.js +39 -4
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +30 -7
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/services/websocket/types.js +12 -7
- package/dist/server/services/websocket/types.js.map +1 -1
- package/dist/server/utils/agent-manager.d.ts.map +1 -1
- package/dist/server/utils/agent-manager.js +0 -1
- package/dist/server/utils/agent-manager.js.map +1 -1
- package/dist/server/utils/paths.d.ts.map +1 -1
- package/dist/server/utils/paths.js +0 -1
- package/dist/server/utils/paths.js.map +1 -1
- package/dist/server/utils/port-manager.d.ts.map +1 -1
- package/dist/server/utils/port-manager.js +0 -1
- package/dist/server/utils/port-manager.js.map +1 -1
- package/dist/server/utils/port.d.ts.map +1 -1
- package/dist/server/utils/port.js +0 -1
- package/dist/server/utils/port.js.map +1 -1
- package/package.json +6 -4
- package/server/cli/eta-estimator.ts +249 -0
- package/server/cli/headless/claude-invoker-process.ts +0 -1
- package/server/cli/headless/claude-invoker-stall.ts +0 -1
- package/server/cli/headless/claude-invoker-stream.ts +0 -1
- package/server/cli/headless/claude-invoker-tools.ts +0 -1
- package/server/cli/headless/claude-invoker.ts +0 -1
- package/server/cli/headless/haiku-assessments.ts +0 -1
- package/server/cli/headless/headless-logger.ts +0 -1
- package/server/cli/headless/index.ts +0 -1
- package/server/cli/headless/native-timeout-detector.ts +0 -1
- package/server/cli/headless/output-utils.ts +0 -1
- package/server/cli/headless/prompt-utils.ts +0 -1
- package/server/cli/headless/resilient-runner.ts +0 -1
- package/server/cli/headless/retry-strategies.ts +0 -1
- package/server/cli/headless/runner.ts +0 -1
- package/server/cli/headless/stall-assessor.ts +93 -1
- package/server/cli/headless/tool-watchdog.ts +21 -1
- package/server/cli/headless/types.ts +0 -1
- package/server/cli/improvisation-attachments.ts +0 -1
- package/server/cli/improvisation-history-store.ts +4 -2
- package/server/cli/improvisation-movements.ts +0 -1
- package/server/cli/improvisation-output-queue.ts +29 -8
- package/server/cli/improvisation-retry.ts +0 -1
- package/server/cli/improvisation-session-manager.ts +54 -2
- package/server/cli/improvisation-types.ts +2 -1
- package/server/cli/retry/retry-best-result.ts +0 -1
- package/server/cli/retry/retry-context-loss.ts +0 -1
- package/server/cli/retry/retry-premature-completion.ts +1 -2
- package/server/cli/retry/retry-recovery-strategies.ts +0 -1
- package/server/cli/retry/retry-resume-strategy.ts +0 -1
- package/server/cli/retry/retry-runner-factory.ts +0 -1
- package/server/cli/retry/retry-tool-results.ts +0 -1
- package/server/cli/retry/retry-types.ts +0 -1
- package/server/engines/EngineEvent.ts +156 -0
- package/server/engines/claude/ClaudeCodeEngine.ts +404 -0
- package/server/engines/factory.ts +176 -0
- package/server/engines/opencode/OpenCodeEngine.ts +786 -0
- package/server/engines/opencode/OpenCodeServerManager.ts +577 -0
- package/server/engines/opencode/model-catalog.ts +217 -0
- package/server/engines/types.ts +173 -0
- package/server/index.ts +1 -2
- package/server/mcp/bouncer-cli.ts +0 -1
- package/server/mcp/bouncer-haiku.ts +21 -146
- package/server/mcp/bouncer-integration.ts +107 -6
- package/server/mcp/classifier/BouncerClassifier.ts +40 -0
- package/server/mcp/classifier/ClaudeBouncerClassifier.ts +189 -0
- package/server/mcp/classifier/OpenCodeBouncerClassifier.ts +305 -0
- package/server/mcp/classifier/factory.ts +195 -0
- package/server/mcp/security-analysis.ts +0 -1
- package/server/mcp/security-audit.ts +0 -1
- package/server/mcp/security-patterns.ts +0 -1
- package/server/mcp/server.ts +0 -1
- package/server/routes/files.ts +0 -1
- package/server/routes/improvise.ts +0 -1
- package/server/routes/index.ts +0 -1
- package/server/routes/instances.ts +0 -1
- package/server/routes/notifications.ts +0 -1
- package/server/server-setup.ts +0 -1
- package/server/services/analytics.ts +0 -1
- package/server/services/auth.ts +0 -1
- package/server/services/client-id.ts +0 -1
- package/server/services/file-explorer-ops.ts +0 -1
- package/server/services/files.ts +0 -1
- package/server/services/instances.ts +0 -1
- package/server/services/pathUtils.ts +0 -1
- package/server/services/plan/agent-loader.ts +0 -1
- package/server/services/plan/agent-resolver.ts +115 -0
- package/server/services/plan/agents/code-review.md +43 -11
- package/server/services/plan/board-config.ts +0 -1
- package/server/services/plan/composer.ts +63 -12
- package/server/services/plan/config-installer.ts +0 -1
- package/server/services/plan/dependency-resolver.ts +0 -1
- package/server/services/plan/executor.ts +48 -4
- package/server/services/plan/front-matter.ts +0 -1
- package/server/services/plan/issue-classification.ts +0 -1
- package/server/services/plan/issue-loader.ts +0 -1
- package/server/services/plan/issue-prompt-builder.ts +39 -2
- package/server/services/plan/issue-retry.ts +5 -2
- package/server/services/plan/issue-writer.ts +0 -1
- package/server/services/plan/output-manager.ts +0 -1
- package/server/services/plan/parser-core.ts +1 -1
- package/server/services/plan/parser-migration.ts +0 -1
- package/server/services/plan/parser.ts +0 -1
- package/server/services/plan/progress-log.ts +0 -1
- package/server/services/plan/prompt-builder.ts +0 -1
- package/server/services/plan/readiness-planner.ts +0 -1
- package/server/services/plan/review-gate.ts +0 -1
- package/server/services/plan/state-reconciler.ts +0 -1
- package/server/services/plan/types.ts +4 -1
- package/server/services/plan/watcher.ts +0 -1
- package/server/services/platform-credentials.ts +0 -1
- package/server/services/platform-token-lifecycle.ts +171 -0
- package/server/services/platform.ts +106 -148
- package/server/services/sentry.ts +0 -1
- package/server/services/settings.ts +161 -5
- package/server/services/terminal/pty-manager.ts +0 -1
- package/server/services/terminal/pty-utils.ts +0 -1
- package/server/services/websocket/autocomplete.ts +0 -1
- package/server/services/websocket/file-definition-handlers.ts +0 -1
- package/server/services/websocket/file-download-handler.ts +0 -1
- package/server/services/websocket/file-explorer-handlers.ts +0 -1
- package/server/services/websocket/file-search-handlers.ts +0 -1
- package/server/services/websocket/file-upload-handler.ts +6 -5
- package/server/services/websocket/file-utils.ts +0 -1
- package/server/services/websocket/git-branch-handlers.ts +20 -7
- package/server/services/websocket/git-diff-handlers.ts +0 -1
- package/server/services/websocket/git-handlers.ts +66 -10
- package/server/services/websocket/git-head-watcher.ts +0 -1
- package/server/services/websocket/git-log-handlers.ts +0 -1
- package/server/services/websocket/git-pr-handlers.ts +0 -1
- package/server/services/websocket/git-tag-handlers.ts +0 -1
- package/server/services/websocket/git-utils.ts +69 -9
- package/server/services/websocket/git-worktree-handlers.ts +260 -17
- package/server/services/websocket/handler-context.ts +0 -1
- package/server/services/websocket/handler.ts +62 -6
- package/server/services/websocket/index.ts +0 -1
- package/server/services/websocket/msg-id-tracker.ts +0 -1
- package/server/services/websocket/plan-board-handlers.ts +0 -1
- package/server/services/websocket/plan-execution-handlers.ts +6 -2
- package/server/services/websocket/plan-handlers.ts +0 -1
- package/server/services/websocket/plan-helpers.ts +0 -1
- package/server/services/websocket/plan-issue-handlers.ts +0 -1
- package/server/services/websocket/plan-sprint-handlers.ts +0 -1
- package/server/services/websocket/quality-complexity.ts +80 -27
- package/server/services/websocket/quality-eta.ts +155 -0
- package/server/services/websocket/quality-grading.ts +834 -0
- package/server/services/websocket/quality-handlers.ts +153 -8
- package/server/services/websocket/quality-linting.ts +0 -1
- package/server/services/websocket/quality-operations.ts +72 -0
- package/server/services/websocket/quality-persistence.ts +47 -8
- package/server/services/websocket/quality-review-agent.ts +154 -65
- package/server/services/websocket/quality-service.ts +415 -68
- package/server/services/websocket/quality-tools.ts +62 -3
- package/server/services/websocket/quality-types.ts +61 -4
- package/server/services/websocket/session-handlers.ts +64 -11
- package/server/services/websocket/session-history.ts +3 -1
- package/server/services/websocket/session-initialization.ts +189 -47
- package/server/services/websocket/session-registry.ts +37 -1
- package/server/services/websocket/settings-handlers.ts +41 -5
- package/server/services/websocket/skill-handlers.ts +0 -1
- package/server/services/websocket/skill-watcher.ts +0 -1
- package/server/services/websocket/tab-broadcast.ts +10 -3
- package/server/services/websocket/tab-event-buffer.ts +143 -12
- package/server/services/websocket/tab-event-replay.ts +70 -4
- package/server/services/websocket/tab-handlers.ts +53 -6
- package/server/services/websocket/terminal-handlers.ts +39 -3
- package/server/services/websocket/types.ts +39 -8
- package/server/utils/agent-manager.ts +0 -1
- package/server/utils/paths.ts +0 -1
- package/server/utils/port-manager.ts +0 -1
- package/server/utils/port.ts +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Platform Connection Service
|
|
@@ -14,12 +13,8 @@ import { arch, hostname, type } from 'node:os'
|
|
|
14
13
|
import { basename } from 'node:path'
|
|
15
14
|
import { AnalyticsEvents, trackEvent } from './analytics.js'
|
|
16
15
|
import { getClientId } from './client-id.js'
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
getCredentials,
|
|
20
|
-
shouldRefreshToken,
|
|
21
|
-
updateCredentials,
|
|
22
|
-
} from './platform-credentials.js'
|
|
16
|
+
import { CLI_VERSION, getCredentials } from './platform-credentials.js'
|
|
17
|
+
import { TokenLifecycle } from './platform-token-lifecycle.js'
|
|
23
18
|
import { captureException } from './sentry.js'
|
|
24
19
|
|
|
25
20
|
/**
|
|
@@ -50,6 +45,27 @@ if (typeof WebSocket !== 'undefined') {
|
|
|
50
45
|
// PLATFORM_URL is set via --server / --dev flag in mstro.js
|
|
51
46
|
const DEFAULT_PLATFORM_URL = process.env.PLATFORM_URL || 'https://api.mstro.app'
|
|
52
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Relay wire-format version this CLI speaks. Bumped by the server when the
|
|
50
|
+
* relay protocol changes incompatibly. Must match (or exceed) the server's
|
|
51
|
+
* `minProtocolVersion` in `server/src/relay/version-policy.ts` — when the
|
|
52
|
+
* server's floor moves above this value, the upgrade handshake returns 426
|
|
53
|
+
* and the CLI surfaces the upgrade message via `handleSocketClose`.
|
|
54
|
+
*
|
|
55
|
+
* This is a build-time constant, not user-configurable: a stale CLI must be
|
|
56
|
+
* told to upgrade rather than allowed to opt itself into compatibility.
|
|
57
|
+
*/
|
|
58
|
+
const PROTOCOL_VERSION = 1
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Long sentinel delay used after a 426 protocol-too-old close. The CLI
|
|
62
|
+
* auto-upgrades on the next `mstro` invocation, so a tight reconnect loop
|
|
63
|
+
* would just hammer the server with rejections that policy already decided
|
|
64
|
+
* to refuse. One hour is long enough to count as "stop", short enough that
|
|
65
|
+
* a forgotten foreground process eventually retries on its own.
|
|
66
|
+
*/
|
|
67
|
+
const PROTOCOL_UPGRADE_RECONNECT_DELAY_MS = 60 * 60 * 1000
|
|
68
|
+
|
|
53
69
|
interface ConnectionCallbacks {
|
|
54
70
|
onConnected?: (connectionId: string) => void
|
|
55
71
|
onDisconnected?: () => void
|
|
@@ -73,11 +89,12 @@ export class PlatformConnection {
|
|
|
73
89
|
private callbacks: ConnectionCallbacks
|
|
74
90
|
private connectionId: string | null = null
|
|
75
91
|
private isConnected = false
|
|
76
|
-
private tokenRefreshInterval: ReturnType<typeof setInterval> | null = null
|
|
77
92
|
private heartbeatInterval: ReturnType<typeof setInterval> | null = null
|
|
78
93
|
private missedPongs = 0
|
|
79
94
|
private everConnected = false
|
|
95
|
+
private protocolUpgradeRequired = false
|
|
80
96
|
private readonly startedAt: string
|
|
97
|
+
private readonly tokens: TokenLifecycle
|
|
81
98
|
|
|
82
99
|
constructor(
|
|
83
100
|
workingDirectory: string,
|
|
@@ -88,120 +105,13 @@ export class PlatformConnection {
|
|
|
88
105
|
this.platformUrl = platformUrl || DEFAULT_PLATFORM_URL
|
|
89
106
|
this.callbacks = callbacks
|
|
90
107
|
this.startedAt = new Date().toISOString()
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* Returns `true` if the token is (still) valid after this call, `false`
|
|
96
|
-
* if refresh was attempted and rejected with an auth error — in which
|
|
97
|
-
* case the caller should surface an auth-expired signal to the web
|
|
98
|
-
* rather than silently reusing a dead token.
|
|
99
|
-
*/
|
|
100
|
-
private async maybeRefreshToken(): Promise<boolean> {
|
|
101
|
-
const creds = getCredentials()
|
|
102
|
-
if (!creds) return false
|
|
103
|
-
if (!shouldRefreshToken(creds)) return true
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const response = await fetch(`${this.platformUrl}/api/auth/device/refresh`, {
|
|
107
|
-
method: 'POST',
|
|
108
|
-
headers: {
|
|
109
|
-
'Authorization': `Bearer ${creds.token}`,
|
|
110
|
-
'Content-Type': 'application/json'
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
if (response.ok) {
|
|
115
|
-
const data = await response.json() as { accessToken: string }
|
|
116
|
-
updateCredentials({
|
|
117
|
-
token: data.accessToken,
|
|
118
|
-
lastRefreshedAt: new Date().toISOString()
|
|
119
|
-
})
|
|
120
|
-
return true
|
|
121
|
-
}
|
|
122
|
-
if (response.status === 401 || response.status === 403) {
|
|
123
|
-
console.warn(`[Platform] Token refresh failed — auth is expired (${response.status}). Run \`mstro login --force\`.`)
|
|
124
|
-
this.notifyAuthExpired()
|
|
125
|
-
return false
|
|
126
|
-
}
|
|
127
|
-
console.warn(`[Platform] Token refresh failed with status ${response.status}, will retry later`)
|
|
128
|
-
return true
|
|
129
|
-
} catch (err) {
|
|
130
|
-
console.warn('[Platform] Token refresh error:', err)
|
|
131
|
-
return true
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Verify the current token against the platform. A rejection (401/403)
|
|
137
|
-
* means the token is permanently invalid (revoked, signing-key rotation,
|
|
138
|
-
* account deleted); the caller should stop looping reconnects and tell
|
|
139
|
-
* the user to run `mstro login --force`.
|
|
140
|
-
*
|
|
141
|
-
* Returns `true` when the token is valid or the verification endpoint
|
|
142
|
-
* is unreachable (we prefer false negatives to false positives — a
|
|
143
|
-
* network blip shouldn't force a re-login).
|
|
144
|
-
*/
|
|
145
|
-
private async verifyToken(): Promise<boolean> {
|
|
146
|
-
const creds = getCredentials()
|
|
147
|
-
if (!creds?.token) return false
|
|
148
|
-
try {
|
|
149
|
-
const response = await fetch(`${this.platformUrl}/api/auth/device/verify`, {
|
|
150
|
-
method: 'POST',
|
|
151
|
-
headers: { 'Authorization': `Bearer ${creds.token}` },
|
|
152
|
-
})
|
|
153
|
-
if (response.status === 401 || response.status === 403) {
|
|
154
|
-
console.warn(`[Platform] Token verify rejected (${response.status}) — auth is expired.`)
|
|
155
|
-
return false
|
|
156
|
-
}
|
|
157
|
-
return true
|
|
158
|
-
} catch {
|
|
159
|
-
// Network error: treat as "probably valid" so a flaky connection
|
|
160
|
-
// doesn't force users to re-login. The WebSocket open itself will
|
|
161
|
-
// catch a truly bad token via the 4001 path.
|
|
162
|
-
return true
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Surface an auth-expired condition to any paired web clients.
|
|
168
|
-
*
|
|
169
|
-
* Two cooperating paths deliver this signal — either alone is enough,
|
|
170
|
-
* both together cover every timing edge:
|
|
171
|
-
*
|
|
172
|
-
* 1. **CLI-initiated (this method):** we detected a 401 from the
|
|
173
|
-
* `/refresh` or `/verify` endpoint *while the relay socket is
|
|
174
|
-
* still open*. `this.send` pushes the message upstream so the
|
|
175
|
-
* server relays it to paired webs before we intentionally close.
|
|
176
|
-
* A no-op if the socket is already closed.
|
|
177
|
-
*
|
|
178
|
-
* 2. **Server-initiated:** when the platform closes a CLI socket
|
|
179
|
-
* with 4001 or 4008, `handleAuthClose` in `clientHandlers.ts`
|
|
180
|
-
* broadcasts the same `clientAuthExpired` to paired webs. This
|
|
181
|
-
* covers the cases where the CLI never had a chance to detect
|
|
182
|
-
* the rejection itself (e.g. token revoked while the socket was
|
|
183
|
-
* idle, server-side token rotation).
|
|
184
|
-
*
|
|
185
|
-
* IMPORTANT: never call `this.callbacks.onRelayedMessage` here —
|
|
186
|
-
* that callback feeds INCOMING web→CLI requests into the local handler,
|
|
187
|
-
* which would treat `clientAuthExpired` as an unknown inbound request.
|
|
188
|
-
*/
|
|
189
|
-
private notifyAuthExpired(): void {
|
|
190
|
-
this.send({
|
|
191
|
-
type: 'clientAuthExpired',
|
|
192
|
-
data: {
|
|
193
|
-
connectionId: this.connectionId,
|
|
194
|
-
message: 'The CLI\'s device token is invalid — run `mstro login --force` on the machine.',
|
|
195
|
-
},
|
|
108
|
+
this.tokens = new TokenLifecycle({
|
|
109
|
+
platformUrl: this.platformUrl,
|
|
110
|
+
send: (msg) => this.send(msg),
|
|
111
|
+
getConnectionId: () => this.connectionId,
|
|
196
112
|
})
|
|
197
113
|
}
|
|
198
114
|
|
|
199
|
-
private startTokenRefreshCheck(): void {
|
|
200
|
-
this.tokenRefreshInterval = setInterval(() => {
|
|
201
|
-
this.maybeRefreshToken()
|
|
202
|
-
}, 24 * 60 * 60 * 1000)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
115
|
private startHeartbeat(): void {
|
|
206
116
|
this.missedPongs = 0
|
|
207
117
|
this.heartbeatInterval = setInterval(() => this.heartbeatTick(), 25_000)
|
|
@@ -232,15 +142,9 @@ export class PlatformConnection {
|
|
|
232
142
|
}
|
|
233
143
|
}
|
|
234
144
|
|
|
235
|
-
private stopTokenRefreshCheck(): void {
|
|
236
|
-
if (this.tokenRefreshInterval) {
|
|
237
|
-
clearInterval(this.tokenRefreshInterval)
|
|
238
|
-
this.tokenRefreshInterval = null
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
145
|
connect(): void {
|
|
243
146
|
this.isIntentionallyClosed = false
|
|
147
|
+
this.protocolUpgradeRequired = false
|
|
244
148
|
|
|
245
149
|
const authToken = getCredentials()?.token
|
|
246
150
|
if (!authToken) {
|
|
@@ -262,24 +166,7 @@ export class PlatformConnection {
|
|
|
262
166
|
|
|
263
167
|
const connectionTimeout = this.startConnectionTimeout()
|
|
264
168
|
this.attachSocketHandlers(this.ws, authToken, connectionTimeout)
|
|
265
|
-
this.
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Best-effort token verification, fired in parallel with the socket
|
|
270
|
-
* open so a slow verify endpoint never delays reconnect.
|
|
271
|
-
*
|
|
272
|
-
* Only runs when the token is stale enough that we'd be about to
|
|
273
|
-
* refresh anyway — keeps the hot path free of an extra network call.
|
|
274
|
-
* A truly-revoked token that slips past this check still hits 4001
|
|
275
|
-
* on the WebSocket, which also triggers `notifyAuthExpired`.
|
|
276
|
-
*/
|
|
277
|
-
private maybeVerifyTokenInParallel(): void {
|
|
278
|
-
const creds = getCredentials()
|
|
279
|
-
if (!creds || !shouldRefreshToken(creds)) return
|
|
280
|
-
this.verifyToken().then((valid) => {
|
|
281
|
-
if (!valid) this.notifyAuthExpired()
|
|
282
|
-
}).catch(() => { /* network error — rely on 4001 close path */ })
|
|
169
|
+
this.tokens.maybeVerifyInParallel()
|
|
283
170
|
}
|
|
284
171
|
|
|
285
172
|
private buildConnectionUrl(): string {
|
|
@@ -295,6 +182,7 @@ export class PlatformConnection {
|
|
|
295
182
|
cliVersion: CLI_VERSION,
|
|
296
183
|
capabilities: JSON.stringify({}),
|
|
297
184
|
startedAt: this.startedAt,
|
|
185
|
+
protocolVersion: String(PROTOCOL_VERSION),
|
|
298
186
|
})
|
|
299
187
|
return `${this.platformUrl.replace(/^http/, 'ws')}/ws/client?${params}`
|
|
300
188
|
}
|
|
@@ -319,8 +207,8 @@ export class PlatformConnection {
|
|
|
319
207
|
ws.onopen = () => {
|
|
320
208
|
clearTimeout(connectionTimeout)
|
|
321
209
|
ws.send(JSON.stringify({ type: 'auth', token: authToken }))
|
|
322
|
-
this.
|
|
323
|
-
this.
|
|
210
|
+
this.tokens.maybeRefresh()
|
|
211
|
+
this.tokens.startPeriodicCheck()
|
|
324
212
|
this.reconnectAttempts = 0
|
|
325
213
|
trackEvent(AnalyticsEvents.PLATFORM_CONNECTED)
|
|
326
214
|
}
|
|
@@ -342,6 +230,24 @@ export class PlatformConnection {
|
|
|
342
230
|
clearTimeout(connectionTimeout)
|
|
343
231
|
// onclose will be called after this
|
|
344
232
|
}
|
|
233
|
+
|
|
234
|
+
// The Node `ws` library exposes the raw HTTP upgrade response as an
|
|
235
|
+
// `unexpected-response` event when the server returns a non-101 status
|
|
236
|
+
// (for us: 426 Upgrade Required from the protocol-version gate). The
|
|
237
|
+
// global WebSocket in Bun / Node 21+ doesn't expose this — for those
|
|
238
|
+
// runtimes we rely on the close-code path below. Both paths feed into
|
|
239
|
+
// `protocolUpgradeRequired`, so `handleSocketClose` can decide once.
|
|
240
|
+
const wsWithEmitter = ws as unknown as {
|
|
241
|
+
on?: (event: string, listener: (...args: unknown[]) => void) => void
|
|
242
|
+
}
|
|
243
|
+
if (typeof wsWithEmitter.on === 'function') {
|
|
244
|
+
wsWithEmitter.on('unexpected-response', (...args: unknown[]) => {
|
|
245
|
+
const response = args[1] as { statusCode?: number } | undefined
|
|
246
|
+
if (response?.statusCode === 426) {
|
|
247
|
+
this.protocolUpgradeRequired = true
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
}
|
|
345
251
|
}
|
|
346
252
|
|
|
347
253
|
private handleSocketClose(event: CloseEvent): void {
|
|
@@ -350,6 +256,11 @@ export class PlatformConnection {
|
|
|
350
256
|
|
|
351
257
|
if (this.isIntentionallyClosed) return
|
|
352
258
|
|
|
259
|
+
if (this.isProtocolTooOldClose(event)) {
|
|
260
|
+
this.handleProtocolUpgradeRequired()
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
353
264
|
const isAuthFailure = event.code === 4001 ||
|
|
354
265
|
event.reason?.includes('Unauthorized') ||
|
|
355
266
|
(event.code === 1006 && !this.everConnected)
|
|
@@ -357,7 +268,7 @@ export class PlatformConnection {
|
|
|
357
268
|
if (isAuthFailure) {
|
|
358
269
|
console.error('\n❌ Authentication failed. Your device token may be invalid or expired.')
|
|
359
270
|
console.error(' Run `mstro login --force` to re-authenticate.\n')
|
|
360
|
-
this.notifyAuthExpired()
|
|
271
|
+
this.tokens.notifyAuthExpired()
|
|
361
272
|
this.callbacks.onError?.('Authentication failed - run `mstro login --force`')
|
|
362
273
|
return
|
|
363
274
|
}
|
|
@@ -368,6 +279,53 @@ export class PlatformConnection {
|
|
|
368
279
|
this.scheduleReconnect()
|
|
369
280
|
}
|
|
370
281
|
|
|
282
|
+
/**
|
|
283
|
+
* The relay can refuse a CLI for being too old in two shapes:
|
|
284
|
+
*
|
|
285
|
+
* 1. **HTTP 426 during upgrade** — the `ws` library surfaces this via
|
|
286
|
+
* its `unexpected-response` event, which sets
|
|
287
|
+
* `protocolUpgradeRequired` before `onclose` fires. This is the path
|
|
288
|
+
* the current server takes (`checkProtocolVersionGate`).
|
|
289
|
+
*
|
|
290
|
+
* 2. **WS close 1002/1008 with `protocol-too-old` reason** — reserved
|
|
291
|
+
* for a future server variant that completes the upgrade and then
|
|
292
|
+
* closes (e.g. when the policy check moves into a post-handshake
|
|
293
|
+
* stage). The reason string is part of the contract with the server.
|
|
294
|
+
*
|
|
295
|
+
* Anything else (1006 race after a successful run, 1001 going-away on
|
|
296
|
+
* deploy) must fall through to the regular reconnect path so transient
|
|
297
|
+
* failures keep healing on their own.
|
|
298
|
+
*/
|
|
299
|
+
private isProtocolTooOldClose(event: CloseEvent): boolean {
|
|
300
|
+
if (this.protocolUpgradeRequired) return true
|
|
301
|
+
const code = event.code
|
|
302
|
+
if ((code === 1002 || code === 1008) && event.reason?.includes('protocol-too-old')) {
|
|
303
|
+
return true
|
|
304
|
+
}
|
|
305
|
+
return false
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private handleProtocolUpgradeRequired(): void {
|
|
309
|
+
const message = 'Mstro CLI is out of date for this orchestra. Run `mstro` again to upgrade and reconnect.'
|
|
310
|
+
console.error(`\n❌ ${message}\n`)
|
|
311
|
+
this.callbacks.onError?.(message)
|
|
312
|
+
this.callbacks.onDisconnected?.()
|
|
313
|
+
|
|
314
|
+
// Don't exit the process — terminal sessions and other local state stay
|
|
315
|
+
// alive while the user re-runs `mstro`. Schedule a single, far-future
|
|
316
|
+
// reconnect as a sentinel so we silently retry exactly once if the
|
|
317
|
+
// process is somehow still around an hour later. The existing
|
|
318
|
+
// exponential-backoff path is bypassed deliberately: hammering a server
|
|
319
|
+
// that has already decided to refuse on policy is pointless.
|
|
320
|
+
if (this.reconnectTimeout) {
|
|
321
|
+
clearTimeout(this.reconnectTimeout)
|
|
322
|
+
}
|
|
323
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
324
|
+
this.reconnectTimeout = null
|
|
325
|
+
this.connect()
|
|
326
|
+
}, PROTOCOL_UPGRADE_RECONNECT_DELAY_MS)
|
|
327
|
+
}
|
|
328
|
+
|
|
371
329
|
private handleMessage(message: Record<string, unknown>): void {
|
|
372
330
|
switch (message.type) {
|
|
373
331
|
case 'paired':
|
|
@@ -430,7 +388,7 @@ export class PlatformConnection {
|
|
|
430
388
|
disconnect(): void {
|
|
431
389
|
this.isIntentionallyClosed = true
|
|
432
390
|
this.stopHeartbeat()
|
|
433
|
-
this.
|
|
391
|
+
this.tokens.stopPeriodicCheck()
|
|
434
392
|
|
|
435
393
|
if (this.reconnectTimeout) {
|
|
436
394
|
clearTimeout(this.reconnectTimeout)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Settings Service
|
|
@@ -16,10 +15,75 @@
|
|
|
16
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
17
16
|
import { homedir } from 'node:os'
|
|
18
17
|
import { join } from 'node:path'
|
|
18
|
+
import type { EngineId } from '../engines/types.js'
|
|
19
19
|
|
|
20
20
|
const MSTRO_DIR = join(homedir(), '.mstro')
|
|
21
21
|
const SETTINGS_FILE = join(MSTRO_DIR, 'settings.json')
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Configuration for the Layer-2 Bouncer classifier (the AI model that runs
|
|
25
|
+
* for every ambiguous tool call). The model MUST be flagged
|
|
26
|
+
* `bouncerEligible` in the engine's model catalogue — frontier models
|
|
27
|
+
* (Opus, GPT-4o, …) are deliberately disallowed because they slow the
|
|
28
|
+
* classifier path and degrade the whole security layer.
|
|
29
|
+
*/
|
|
30
|
+
export interface BouncerClassifierConfig {
|
|
31
|
+
engine: EngineId
|
|
32
|
+
/** Engine-specific model id, e.g. 'haiku', 'sonnet', 'openai/gpt-5-mini'. */
|
|
33
|
+
model: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Canonical list of bouncer-eligible models per engine. Mirrors
|
|
38
|
+
* `web/src/components/views/SettingsView/constants.ts` — keep the two in
|
|
39
|
+
* sync. Only cheap/fast models appear here; if you need to add a model,
|
|
40
|
+
* check p50 latency < ~1s and JSON-mode capability first.
|
|
41
|
+
*/
|
|
42
|
+
export const BOUNCER_ELIGIBLE_MODELS: Record<EngineId, readonly string[]> = {
|
|
43
|
+
'claude-code': ['haiku', 'sonnet'],
|
|
44
|
+
opencode: [
|
|
45
|
+
'openai/gpt-5-mini',
|
|
46
|
+
'openai/gpt-5-nano',
|
|
47
|
+
'google/gemini-2.5-flash',
|
|
48
|
+
'ollama/llama3.1:8b',
|
|
49
|
+
],
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Default classifier — Claude Haiku. Matches the pre-feature-flag behavior. */
|
|
53
|
+
export const DEFAULT_BOUNCER_CLASSIFIER: BouncerClassifierConfig = {
|
|
54
|
+
engine: 'claude-code',
|
|
55
|
+
model: 'haiku',
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validate a `BouncerClassifierConfig`. Rejects with a thrown `Error` when
|
|
60
|
+
* the model is not flagged `bouncerEligible` under the requested engine —
|
|
61
|
+
* e.g. attempting to use Opus as a classifier, or a frontier OpenCode
|
|
62
|
+
* model. The WebSocket settings handler uses this to reject crafted
|
|
63
|
+
* payloads from the web client.
|
|
64
|
+
*/
|
|
65
|
+
export function validateBouncerClassifier(config: unknown): BouncerClassifierConfig {
|
|
66
|
+
if (config === null || typeof config !== 'object') {
|
|
67
|
+
throw new Error('bouncerClassifier must be an object with { engine, model }')
|
|
68
|
+
}
|
|
69
|
+
const { engine, model } = config as { engine?: unknown; model?: unknown }
|
|
70
|
+
if (engine !== 'claude-code' && engine !== 'opencode') {
|
|
71
|
+
throw new Error(`bouncerClassifier.engine must be 'claude-code' or 'opencode' (got ${String(engine)})`)
|
|
72
|
+
}
|
|
73
|
+
if (typeof model !== 'string' || model.length === 0) {
|
|
74
|
+
throw new Error('bouncerClassifier.model must be a non-empty string')
|
|
75
|
+
}
|
|
76
|
+
const eligible = BOUNCER_ELIGIBLE_MODELS[engine]
|
|
77
|
+
if (!eligible.includes(model)) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Model '${model}' is not bouncer-eligible for engine '${engine}'. ` +
|
|
80
|
+
`Eligible models: ${eligible.join(', ')}. ` +
|
|
81
|
+
`Frontier models (Opus, GPT-4o, etc.) are deliberately excluded to keep the classifier fast.`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
return { engine, model }
|
|
85
|
+
}
|
|
86
|
+
|
|
23
87
|
export interface MstroSettings {
|
|
24
88
|
/**
|
|
25
89
|
* Claude model to use for main execution.
|
|
@@ -38,11 +102,26 @@ export interface MstroSettings {
|
|
|
38
102
|
effortLevel: string
|
|
39
103
|
/** Per-repo preferred PR base branch, keyed by normalized remote URL */
|
|
40
104
|
prBaseBranches?: Record<string, string>
|
|
105
|
+
/**
|
|
106
|
+
* Feature flag gating all OpenCode code paths (engine factory, classifier
|
|
107
|
+
* factory, and UI). When `false`, the system behaves byte-identically to
|
|
108
|
+
* pre-OpenCode main: no `opencode serve` subprocess, no classifier picker,
|
|
109
|
+
* no EngineSection/EnginePicker in the web UI. Resolution order in
|
|
110
|
+
* `isEngineSwapEnabled()`: env var → stored setting → NODE_ENV default.
|
|
111
|
+
*/
|
|
112
|
+
engineSwap?: boolean
|
|
113
|
+
/**
|
|
114
|
+
* Which engine + model backs the Layer-2 Bouncer classifier. Defaults to
|
|
115
|
+
* `{ engine: 'claude-code', model: 'haiku' }`. Only models flagged
|
|
116
|
+
* `bouncerEligible` are accepted — see {@link validateBouncerClassifier}.
|
|
117
|
+
*/
|
|
118
|
+
bouncerClassifier?: BouncerClassifierConfig
|
|
41
119
|
}
|
|
42
120
|
|
|
43
121
|
const DEFAULT_SETTINGS: MstroSettings = {
|
|
44
122
|
model: 'opus',
|
|
45
|
-
effortLevel: 'auto'
|
|
123
|
+
effortLevel: 'auto',
|
|
124
|
+
bouncerClassifier: { ...DEFAULT_BOUNCER_CLASSIFIER },
|
|
46
125
|
}
|
|
47
126
|
|
|
48
127
|
/**
|
|
@@ -55,7 +134,11 @@ function ensureMstroDir(): void {
|
|
|
55
134
|
}
|
|
56
135
|
|
|
57
136
|
/**
|
|
58
|
-
* Get current settings, merged with defaults for any missing fields
|
|
137
|
+
* Get current settings, merged with defaults for any missing fields. A
|
|
138
|
+
* persisted `bouncerClassifier` that is no longer bouncer-eligible (e.g. a
|
|
139
|
+
* catalogue change removed the model) is dropped in favor of the default
|
|
140
|
+
* and a warning is logged — the Bouncer must never silently run a
|
|
141
|
+
* non-eligible model just because someone edited settings.json by hand.
|
|
59
142
|
*/
|
|
60
143
|
export function getSettings(): MstroSettings {
|
|
61
144
|
if (!existsSync(SETTINGS_FILE)) {
|
|
@@ -65,10 +148,22 @@ export function getSettings(): MstroSettings {
|
|
|
65
148
|
try {
|
|
66
149
|
const content = readFileSync(SETTINGS_FILE, 'utf-8')
|
|
67
150
|
const stored = JSON.parse(content)
|
|
68
|
-
|
|
151
|
+
const merged: MstroSettings = {
|
|
69
152
|
...DEFAULT_SETTINGS,
|
|
70
153
|
...stored,
|
|
71
154
|
}
|
|
155
|
+
if (stored && typeof stored === 'object' && 'bouncerClassifier' in stored) {
|
|
156
|
+
try {
|
|
157
|
+
merged.bouncerClassifier = validateBouncerClassifier(stored.bouncerClassifier)
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.warn(
|
|
160
|
+
'[settings] Stored bouncerClassifier is not bouncer-eligible, falling back to default:',
|
|
161
|
+
err instanceof Error ? err.message : String(err),
|
|
162
|
+
)
|
|
163
|
+
merged.bouncerClassifier = { ...DEFAULT_BOUNCER_CLASSIFIER }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return merged
|
|
72
167
|
} catch (err) {
|
|
73
168
|
console.warn('Failed to read settings file, using defaults:', err)
|
|
74
169
|
return { ...DEFAULT_SETTINGS }
|
|
@@ -76,9 +171,18 @@ export function getSettings(): MstroSettings {
|
|
|
76
171
|
}
|
|
77
172
|
|
|
78
173
|
/**
|
|
79
|
-
* Save full settings to disk
|
|
174
|
+
* Save full settings to disk. Rejects if `bouncerClassifier` is present but
|
|
175
|
+
* its model is not flagged `bouncerEligible` — this is the save-time half
|
|
176
|
+
* of the guard; `getSettings` enforces the read-time half. Together they
|
|
177
|
+
* ensure the Bouncer is never configured with a frontier model (Opus,
|
|
178
|
+
* GPT-4o, …) regardless of whether the mutation came from the web UI or a
|
|
179
|
+
* direct edit of settings.json.
|
|
80
180
|
*/
|
|
81
181
|
export function saveSettings(settings: MstroSettings): void {
|
|
182
|
+
if (settings.bouncerClassifier !== undefined) {
|
|
183
|
+
// Throws on non-eligible model — callers must surface the error.
|
|
184
|
+
validateBouncerClassifier(settings.bouncerClassifier)
|
|
185
|
+
}
|
|
82
186
|
ensureMstroDir()
|
|
83
187
|
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2), {
|
|
84
188
|
mode: 0o600
|
|
@@ -117,6 +221,58 @@ export function setEffortLevel(effortLevel: string): void {
|
|
|
117
221
|
saveSettings(settings)
|
|
118
222
|
}
|
|
119
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Get the current Bouncer classifier configuration. Returns the default
|
|
226
|
+
* `{ engine: 'claude-code', model: 'haiku' }` when nothing is persisted.
|
|
227
|
+
*/
|
|
228
|
+
export function getBouncerClassifier(): BouncerClassifierConfig {
|
|
229
|
+
const settings = getSettings()
|
|
230
|
+
if (settings.bouncerClassifier) {
|
|
231
|
+
try {
|
|
232
|
+
return validateBouncerClassifier(settings.bouncerClassifier)
|
|
233
|
+
} catch {
|
|
234
|
+
// Stored config is no longer eligible (e.g. model removed from the
|
|
235
|
+
// catalogue). Fall back to the safe default rather than crashing.
|
|
236
|
+
return { ...DEFAULT_BOUNCER_CLASSIFIER }
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return { ...DEFAULT_BOUNCER_CLASSIFIER }
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Persist a new Bouncer classifier config. Throws if the model is not
|
|
244
|
+
* flagged `bouncerEligible` under the requested engine — callers should
|
|
245
|
+
* surface the error to the UI so the user sees a clear rejection reason.
|
|
246
|
+
*/
|
|
247
|
+
export function setBouncerClassifier(config: unknown): BouncerClassifierConfig {
|
|
248
|
+
const validated = validateBouncerClassifier(config)
|
|
249
|
+
const settings = getSettings()
|
|
250
|
+
settings.bouncerClassifier = validated
|
|
251
|
+
saveSettings(settings)
|
|
252
|
+
return validated
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Resolve the engineSwap feature flag. Precedence:
|
|
257
|
+
* 1. `MSTRO_ENABLE_ENGINE_SWAP` env var ('true'|'1' → on, 'false'|'0' → off).
|
|
258
|
+
* 2. `engineSwap` field in `~/.mstro/settings.json`.
|
|
259
|
+
* 3. NODE_ENV default — off in production, on otherwise (dev/staging/test).
|
|
260
|
+
*
|
|
261
|
+
* Callers who need a single boolean should use this helper rather than
|
|
262
|
+
* reading the field directly, so the precedence stays in one place.
|
|
263
|
+
*/
|
|
264
|
+
export function isEngineSwapEnabled(): boolean {
|
|
265
|
+
const envFlag = process.env.MSTRO_ENABLE_ENGINE_SWAP
|
|
266
|
+
if (envFlag !== undefined) {
|
|
267
|
+
const normalized = envFlag.trim().toLowerCase()
|
|
268
|
+
if (normalized === 'true' || normalized === '1') return true
|
|
269
|
+
if (normalized === 'false' || normalized === '0') return false
|
|
270
|
+
}
|
|
271
|
+
const stored = getSettings().engineSwap
|
|
272
|
+
if (typeof stored === 'boolean') return stored
|
|
273
|
+
return process.env.NODE_ENV !== 'production'
|
|
274
|
+
}
|
|
275
|
+
|
|
120
276
|
/** Normalize a remote URL into a stable key (e.g. "github.com/owner/repo") */
|
|
121
277
|
function normalizeRemoteUrl(remoteUrl: string): string {
|
|
122
278
|
return remoteUrl
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Chunked File Upload Handler
|
|
6
5
|
*
|
|
7
6
|
* Receives files in chunks over WebSocket from remote web clients.
|
|
7
|
+
* The destination working directory is passed per-call (effective dir for the tab,
|
|
8
|
+
* which resolves to the worktree path when the tab is on a worktree).
|
|
8
9
|
* - When `targetPath` is provided: streams to <workingDir>/<targetPath> (drag-drop into file tree).
|
|
9
|
-
* - Otherwise: streams to
|
|
10
|
+
* - Otherwise: streams to <workingDir>/.mstro/tmp/attachments/{tabId}/ (prompt attachments).
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import type { WriteStream } from 'node:fs';
|
|
@@ -50,7 +51,6 @@ export class FileUploadHandler {
|
|
|
50
51
|
private cleanupInterval: ReturnType<typeof setInterval>;
|
|
51
52
|
|
|
52
53
|
constructor(
|
|
53
|
-
private workingDir: string,
|
|
54
54
|
private ctx?: HandlerContext,
|
|
55
55
|
) {
|
|
56
56
|
// Periodically clean up stale uploads
|
|
@@ -74,6 +74,7 @@ export class FileUploadHandler {
|
|
|
74
74
|
send: (ws: WSContext, response: WebSocketResponse) => void,
|
|
75
75
|
tabId: string,
|
|
76
76
|
data: { uploadId: string; fileName: string; fileSize: number; mimeType: string; isImage: boolean; totalChunks: number; targetPath?: string; overwrite?: boolean },
|
|
77
|
+
workingDir: string,
|
|
77
78
|
permission?: 'view',
|
|
78
79
|
): void {
|
|
79
80
|
const { uploadId, fileName, fileSize, mimeType, isImage, totalChunks, targetPath, overwrite } = data;
|
|
@@ -89,8 +90,8 @@ export class FileUploadHandler {
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
const resolved = targetPath !== undefined
|
|
92
|
-
? resolveFileTreeTarget({ targetPath, fileName, workingDir
|
|
93
|
-
: resolveAttachmentTarget({ workingDir
|
|
93
|
+
? resolveFileTreeTarget({ targetPath, fileName, workingDir, permission, overwrite })
|
|
94
|
+
: resolveAttachmentTarget({ workingDir, tabId, fileName });
|
|
94
95
|
|
|
95
96
|
if (!resolved.ok) {
|
|
96
97
|
sendError(resolved.error);
|