mstro-app 0.4.52 → 0.5.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/LICENSE +129 -190
- package/PRIVACY.md +3 -3
- package/README.md +15 -6
- package/bin/commands/config.js +0 -1
- package/bin/mstro.js +1 -2
- package/bin/postinstall.js +0 -1
- 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 +7 -3
- 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 +1 -2
- 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 +63 -68
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +9 -5
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +0 -1
- 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 +16 -0
- package/dist/server/cli/improvisation-history-store.d.ts.map +1 -0
- package/dist/server/cli/improvisation-history-store.js +51 -0
- package/dist/server/cli/improvisation-history-store.js.map +1 -0
- package/dist/server/cli/improvisation-movements.d.ts +31 -0
- package/dist/server/cli/improvisation-movements.d.ts.map +1 -0
- package/dist/server/cli/improvisation-movements.js +92 -0
- package/dist/server/cli/improvisation-movements.js.map +1 -0
- package/dist/server/cli/improvisation-output-queue.d.ts +13 -0
- package/dist/server/cli/improvisation-output-queue.d.ts.map +1 -0
- package/dist/server/cli/improvisation-output-queue.js +39 -0
- package/dist/server/cli/improvisation-output-queue.js.map +1 -0
- package/dist/server/cli/improvisation-retry.d.ts +21 -51
- package/dist/server/cli/improvisation-retry.d.ts.map +1 -1
- package/dist/server/cli/improvisation-retry.js +18 -434
- package/dist/server/cli/improvisation-retry.js.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +10 -8
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +53 -149
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- 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 +4 -0
- package/dist/server/cli/retry/retry-best-result.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-best-result.js +60 -0
- package/dist/server/cli/retry/retry-best-result.js.map +1 -0
- package/dist/server/cli/retry/retry-context-loss.d.ts +6 -0
- package/dist/server/cli/retry/retry-context-loss.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-context-loss.js +67 -0
- package/dist/server/cli/retry/retry-context-loss.js.map +1 -0
- package/dist/server/cli/retry/retry-premature-completion.d.ts +5 -0
- package/dist/server/cli/retry/retry-premature-completion.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-premature-completion.js +80 -0
- package/dist/server/cli/retry/retry-premature-completion.js.map +1 -0
- package/dist/server/cli/retry/retry-recovery-strategies.d.ts +13 -0
- package/dist/server/cli/retry/retry-recovery-strategies.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-recovery-strategies.js +165 -0
- package/dist/server/cli/retry/retry-recovery-strategies.js.map +1 -0
- package/dist/server/cli/retry/retry-resume-strategy.d.ts +12 -0
- package/dist/server/cli/retry/retry-resume-strategy.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-resume-strategy.js +21 -0
- package/dist/server/cli/retry/retry-resume-strategy.js.map +1 -0
- package/dist/server/cli/retry/retry-runner-factory.d.ts +11 -0
- package/dist/server/cli/retry/retry-runner-factory.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-runner-factory.js +59 -0
- package/dist/server/cli/retry/retry-runner-factory.js.map +1 -0
- package/dist/server/cli/retry/retry-tool-results.d.ts +9 -0
- package/dist/server/cli/retry/retry-tool-results.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-tool-results.js +23 -0
- package/dist/server/cli/retry/retry-tool-results.js.map +1 -0
- package/dist/server/cli/retry/retry-types.d.ts +30 -0
- package/dist/server/cli/retry/retry-types.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-types.js +3 -0
- package/dist/server/cli/retry/retry-types.js.map +1 -0
- package/dist/server/index.js +21 -110
- 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.map +1 -1
- package/dist/server/mcp/bouncer-haiku.js +0 -1
- package/dist/server/mcp/bouncer-haiku.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +0 -1
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- 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 +16 -1
- package/dist/server/server-setup.d.ts.map +1 -1
- package/dist/server/server-setup.js +107 -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/board-config.d.ts +21 -0
- package/dist/server/services/plan/board-config.d.ts.map +1 -0
- package/dist/server/services/plan/board-config.js +111 -0
- package/dist/server/services/plan/board-config.js.map +1 -0
- package/dist/server/services/plan/composer.d.ts +1 -1
- package/dist/server/services/plan/composer.d.ts.map +1 -1
- package/dist/server/services/plan/composer.js +7 -6
- 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 +48 -48
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +202 -458
- 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 +16 -0
- package/dist/server/services/plan/issue-loader.d.ts.map +1 -0
- package/dist/server/services/plan/issue-loader.js +45 -0
- package/dist/server/services/plan/issue-loader.js.map +1 -0
- package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.js +0 -1
- 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 +34 -0
- package/dist/server/services/plan/issue-writer.d.ts.map +1 -0
- package/dist/server/services/plan/issue-writer.js +109 -0
- package/dist/server/services/plan/issue-writer.js.map +1 -0
- package/dist/server/services/plan/output-manager.js +2 -2
- 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 +0 -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 +11 -0
- package/dist/server/services/plan/progress-log.d.ts.map +1 -0
- package/dist/server/services/plan/progress-log.js +80 -0
- package/dist/server/services/plan/progress-log.js.map +1 -0
- package/dist/server/services/plan/prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/prompt-builder.js +48 -32
- package/dist/server/services/plan/prompt-builder.js.map +1 -1
- package/dist/server/services/plan/readiness-planner.d.ts +15 -0
- package/dist/server/services/plan/readiness-planner.d.ts.map +1 -0
- package/dist/server/services/plan/readiness-planner.js +40 -0
- package/dist/server/services/plan/readiness-planner.js.map +1 -0
- package/dist/server/services/plan/review-gate.d.ts +31 -0
- package/dist/server/services/plan/review-gate.d.ts.map +1 -1
- package/dist/server/services/plan/review-gate.js +52 -3
- 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.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 +25 -4
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js +150 -92
- 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.map +1 -1
- package/dist/server/services/settings.js +0 -1
- 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 +17 -0
- package/dist/server/services/websocket/file-download-handler.d.ts.map +1 -0
- package/dist/server/services/websocket/file-download-handler.js +164 -0
- package/dist/server/services/websocket/file-download-handler.js.map +1 -0
- 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 +0 -1
- 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 +258 -16
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler-context.d.ts +15 -0
- 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 +7 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +76 -15
- 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 +21 -0
- package/dist/server/services/websocket/msg-id-tracker.d.ts.map +1 -0
- package/dist/server/services/websocket/msg-id-tracker.js +76 -0
- package/dist/server/services/websocket/msg-id-tracker.js.map +1 -0
- 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 +0 -1
- package/dist/server/services/websocket/quality-complexity.js.map +1 -1
- package/dist/server/services/websocket/quality-grading.d.ts +46 -0
- package/dist/server/services/websocket/quality-grading.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-grading.js +482 -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 +15 -4
- 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-persistence.d.ts +14 -0
- package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-persistence.js +28 -12
- package/dist/server/services/websocket/quality-persistence.js.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.js +2 -3
- package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
- package/dist/server/services/websocket/quality-service.d.ts +3 -1
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-service.js +53 -58
- package/dist/server/services/websocket/quality-service.js.map +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-tools.js +6 -3
- package/dist/server/services/websocket/quality-tools.js.map +1 -1
- package/dist/server/services/websocket/quality-types.d.ts +18 -2
- package/dist/server/services/websocket/quality-types.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-types.js +0 -1
- package/dist/server/services/websocket/quality-types.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +48 -2
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +204 -66
- 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 +0 -1
- package/dist/server/services/websocket/session-history.js.map +1 -1
- package/dist/server/services/websocket/session-initialization.d.ts +2 -2
- package/dist/server/services/websocket/session-initialization.d.ts.map +1 -1
- package/dist/server/services/websocket/session-initialization.js +75 -18
- package/dist/server/services/websocket/session-initialization.js.map +1 -1
- package/dist/server/services/websocket/session-registry.d.ts +29 -1
- package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
- package/dist/server/services/websocket/session-registry.js +53 -5
- package/dist/server/services/websocket/session-registry.js.map +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/settings-handlers.js +0 -1
- 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 +24 -0
- package/dist/server/services/websocket/tab-broadcast.d.ts.map +1 -0
- package/dist/server/services/websocket/tab-broadcast.js +12 -0
- package/dist/server/services/websocket/tab-broadcast.js.map +1 -0
- package/dist/server/services/websocket/tab-event-buffer.d.ts +103 -0
- package/dist/server/services/websocket/tab-event-buffer.d.ts.map +1 -0
- package/dist/server/services/websocket/tab-event-buffer.js +106 -0
- package/dist/server/services/websocket/tab-event-buffer.js.map +1 -0
- package/dist/server/services/websocket/tab-event-replay.d.ts +20 -0
- package/dist/server/services/websocket/tab-event-replay.d.ts.map +1 -0
- package/dist/server/services/websocket/tab-event-replay.js +20 -0
- package/dist/server/services/websocket/tab-event-replay.js.map +1 -0
- package/dist/server/services/websocket/tab-handlers.d.ts +0 -1
- package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js +2 -10
- 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 +17 -8
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/services/websocket/types.js +8 -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 +2 -2
- package/server/README.md +1 -1
- package/server/cli/headless/claude-invoker-process.ts +0 -1
- package/server/cli/headless/claude-invoker-stall.ts +7 -3
- 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 +1 -2
- 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 +67 -73
- package/server/cli/headless/stall-assessor.ts +9 -5
- package/server/cli/headless/tool-watchdog.ts +0 -1
- package/server/cli/headless/types.ts +1 -2
- package/server/cli/improvisation-attachments.ts +0 -1
- package/server/cli/improvisation-history-store.ts +61 -0
- package/server/cli/improvisation-movements.ts +119 -0
- package/server/cli/improvisation-output-queue.ts +41 -0
- package/server/cli/improvisation-retry.ts +25 -601
- package/server/cli/improvisation-session-manager.ts +74 -161
- package/server/cli/improvisation-types.ts +0 -1
- package/server/cli/retry/retry-best-result.ts +69 -0
- package/server/cli/retry/retry-context-loss.ts +86 -0
- package/server/cli/retry/retry-premature-completion.ts +112 -0
- package/server/cli/retry/retry-recovery-strategies.ts +246 -0
- package/server/cli/retry/retry-resume-strategy.ts +32 -0
- package/server/cli/retry/retry-runner-factory.ts +69 -0
- package/server/cli/retry/retry-tool-results.ts +30 -0
- package/server/cli/retry/retry-types.ts +31 -0
- package/server/index.ts +37 -124
- package/server/mcp/bouncer-cli.ts +0 -1
- package/server/mcp/bouncer-haiku.ts +0 -1
- package/server/mcp/bouncer-integration.ts +0 -1
- 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 +126 -2
- 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/agents/assess-stall.md +11 -4
- package/server/services/plan/agents/code-review.md +13 -11
- package/server/services/plan/board-config.ts +121 -0
- package/server/services/plan/composer.ts +7 -6
- package/server/services/plan/config-installer.ts +0 -1
- package/server/services/plan/dependency-resolver.ts +0 -1
- package/server/services/plan/executor.ts +259 -470
- 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 +63 -0
- package/server/services/plan/issue-prompt-builder.ts +0 -1
- package/server/services/plan/issue-retry.ts +5 -2
- package/server/services/plan/issue-writer.ts +136 -0
- package/server/services/plan/output-manager.ts +2 -2
- package/server/services/plan/parser-core.ts +0 -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 +91 -0
- package/server/services/plan/prompt-builder.ts +73 -36
- package/server/services/plan/readiness-planner.ts +49 -0
- package/server/services/plan/review-gate.ts +102 -3
- package/server/services/plan/state-reconciler.ts +0 -1
- package/server/services/plan/types.ts +0 -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 +168 -105
- package/server/services/sentry.ts +0 -1
- package/server/services/settings.ts +0 -1
- 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 +190 -0
- 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 +0 -1
- 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 +289 -19
- package/server/services/websocket/handler-context.ts +15 -1
- package/server/services/websocket/handler.ts +79 -16
- package/server/services/websocket/index.ts +0 -1
- package/server/services/websocket/msg-id-tracker.ts +83 -0
- 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 +0 -1
- package/server/services/websocket/quality-grading.ts +611 -0
- package/server/services/websocket/quality-handlers.ts +16 -4
- package/server/services/websocket/quality-linting.ts +0 -1
- package/server/services/websocket/quality-persistence.ts +30 -8
- package/server/services/websocket/quality-review-agent.ts +2 -3
- package/server/services/websocket/quality-service.ts +54 -55
- package/server/services/websocket/quality-tools.ts +11 -3
- package/server/services/websocket/quality-types.ts +21 -3
- package/server/services/websocket/session-handlers.ts +213 -69
- package/server/services/websocket/session-history.ts +0 -1
- package/server/services/websocket/session-initialization.ts +83 -20
- package/server/services/websocket/session-registry.ts +61 -5
- package/server/services/websocket/settings-handlers.ts +0 -1
- 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 +37 -0
- package/server/services/websocket/tab-event-buffer.ts +158 -0
- package/server/services/websocket/tab-event-replay.ts +41 -0
- package/server/services/websocket/tab-handlers.ts +2 -10
- package/server/services/websocket/terminal-handlers.ts +39 -3
- package/server/services/websocket/types.ts +19 -7
- 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
|
* Review Gate — AI-powered quality gate for completed issues.
|
|
@@ -13,16 +12,19 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from
|
|
|
13
12
|
import { join } from 'node:path';
|
|
14
13
|
import { ResilientRunner } from '../../cli/headless/resilient-runner.js';
|
|
15
14
|
import { loadAgentPrompt } from './agent-loader.js';
|
|
15
|
+
import { getBoardReviewCriteria, resolveActiveBoardId } from './board-config.js';
|
|
16
16
|
import { resolveIsCodeTask } from './issue-classification.js';
|
|
17
|
+
import { appendCancellationNote, type WarnFn } from './issue-writer.js';
|
|
18
|
+
import { resolveOutputPath } from './output-manager.js';
|
|
17
19
|
import type { Issue, ReviewCheck, ReviewResult } from './types.js';
|
|
18
20
|
|
|
19
21
|
/** Max review attempts per issue per sprint before giving up */
|
|
20
22
|
export const MAX_REVIEW_ATTEMPTS = 3;
|
|
21
23
|
|
|
22
|
-
/** Review runner stall timeouts (ms) */
|
|
24
|
+
/** Review runner stall timeouts (ms) — hard cap is a backstop that only fires after stall signals flag the run */
|
|
23
25
|
const REVIEW_STALL_WARNING_MS = 300_000; // 5 min
|
|
24
26
|
const REVIEW_STALL_KILL_MS = 600_000; // 10 min
|
|
25
|
-
const REVIEW_STALL_HARD_CAP_MS =
|
|
27
|
+
const REVIEW_STALL_HARD_CAP_MS = 2_700_000; // 45 min backstop
|
|
26
28
|
|
|
27
29
|
export interface ReviewIssueOptions {
|
|
28
30
|
workingDir: string;
|
|
@@ -244,3 +246,100 @@ function buildReviewPrompt(
|
|
|
244
246
|
return `You are a reviewer. Review the work done for issue ${issue.id}: ${issue.title}.\n\n## Acceptance Criteria\n${criteriaStr}\n\nOutput EXACTLY one JSON object on its own line (no markdown fencing):\n{"passed": true, "checks": [{"name": "criteria_met", "passed": true, "details": "..."}]}`;
|
|
245
247
|
}
|
|
246
248
|
|
|
249
|
+
// ── Review pipeline orchestration ───────────────────────────
|
|
250
|
+
|
|
251
|
+
/** Status messages emitted during the review pipeline. */
|
|
252
|
+
export type ReviewProgressStatus = 'reviewing' | 'passed' | 'failed' | 'max_attempts';
|
|
253
|
+
|
|
254
|
+
/** Callbacks for the executor to observe review pipeline events. */
|
|
255
|
+
export interface ReviewPipelineCallbacks {
|
|
256
|
+
/** Update an issue's front-matter status on disk. */
|
|
257
|
+
setStatus: (issuePath: string, status: string) => Promise<void>;
|
|
258
|
+
onOutput: (issueId: string, text: string) => void;
|
|
259
|
+
onReviewProgress: (issueId: string, status: ReviewProgressStatus) => void;
|
|
260
|
+
onIssueAbandoned: (issueId: string, reason: string, attempts: number) => void;
|
|
261
|
+
onIssueCompleted: (issue: Issue) => void;
|
|
262
|
+
onIssueError: (issueId: string, error: string) => void;
|
|
263
|
+
warn: WarnFn;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Configuration for running the full review pipeline for a single issue. */
|
|
267
|
+
export interface ReviewPipelineOptions {
|
|
268
|
+
issue: Issue;
|
|
269
|
+
pmDir: string;
|
|
270
|
+
workingDir: string;
|
|
271
|
+
executionDir: string | null;
|
|
272
|
+
boardDir: string | null;
|
|
273
|
+
boardId: string | null;
|
|
274
|
+
extraEnv?: Record<string, string>;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Full review pipeline for a completed issue: attempt-count guard, status
|
|
279
|
+
* transitions, AI review, persistence, and event emission through the
|
|
280
|
+
* caller's callbacks. Returns `true` when the review passes and the issue is
|
|
281
|
+
* marked `done`, `false` otherwise (reverted, cancelled, or errored).
|
|
282
|
+
*/
|
|
283
|
+
export async function runReviewPipeline(
|
|
284
|
+
options: ReviewPipelineOptions,
|
|
285
|
+
callbacks: ReviewPipelineCallbacks,
|
|
286
|
+
): Promise<boolean> {
|
|
287
|
+
const { issue, pmDir, workingDir, executionDir, boardDir, boardId, extraEnv } = options;
|
|
288
|
+
const reviewDir = boardDir ?? pmDir;
|
|
289
|
+
const attempts = getReviewAttemptCount(reviewDir, issue);
|
|
290
|
+
|
|
291
|
+
if (attempts >= MAX_REVIEW_ATTEMPTS) {
|
|
292
|
+
await callbacks.setStatus(issue.path, 'cancelled');
|
|
293
|
+
await appendCancellationNote(
|
|
294
|
+
pmDir,
|
|
295
|
+
issue,
|
|
296
|
+
`Cancelled after ${MAX_REVIEW_ATTEMPTS} failed reviews — issue may need restructuring`,
|
|
297
|
+
callbacks.warn,
|
|
298
|
+
);
|
|
299
|
+
callbacks.onReviewProgress(issue.id, 'max_attempts');
|
|
300
|
+
callbacks.onIssueAbandoned(
|
|
301
|
+
issue.id,
|
|
302
|
+
`Review failed ${MAX_REVIEW_ATTEMPTS} times — cancelled to unblock dependents`,
|
|
303
|
+
attempts,
|
|
304
|
+
);
|
|
305
|
+
callbacks.onOutput(issue.id, 'Review: max attempts reached, cancelling issue to unblock dependents');
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
await callbacks.setStatus(issue.path, 'in_review');
|
|
310
|
+
callbacks.onReviewProgress(issue.id, 'reviewing');
|
|
311
|
+
|
|
312
|
+
const outputPath = resolveOutputPath(issue, workingDir, boardDir);
|
|
313
|
+
const effectiveBoardId = boardId ?? resolveActiveBoardId(pmDir);
|
|
314
|
+
const reviewCriteria = await getBoardReviewCriteria(pmDir, effectiveBoardId, callbacks.warn);
|
|
315
|
+
|
|
316
|
+
const result = await reviewIssue({
|
|
317
|
+
workingDir: executionDir || workingDir,
|
|
318
|
+
issue,
|
|
319
|
+
pmDir,
|
|
320
|
+
outputPath,
|
|
321
|
+
onOutput: (text) => callbacks.onOutput(issue.id, text),
|
|
322
|
+
logDir: boardDir ? join(boardDir, 'logs') : undefined,
|
|
323
|
+
reviewCriteria,
|
|
324
|
+
boardDir,
|
|
325
|
+
extraEnv,
|
|
326
|
+
});
|
|
327
|
+
persistReviewResult(reviewDir, issue, result);
|
|
328
|
+
|
|
329
|
+
if (result.passed) {
|
|
330
|
+
await callbacks.setStatus(issue.path, 'done');
|
|
331
|
+
callbacks.onReviewProgress(issue.id, 'passed');
|
|
332
|
+
callbacks.onIssueCompleted(issue);
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
await callbacks.setStatus(issue.path, 'todo');
|
|
337
|
+
appendReviewFeedback(pmDir, issue, result);
|
|
338
|
+
callbacks.onReviewProgress(issue.id, 'failed');
|
|
339
|
+
callbacks.onIssueError(
|
|
340
|
+
issue.id,
|
|
341
|
+
`Review failed: ${result.checks.filter(c => !c.passed).map(c => c.name).join(', ')}`,
|
|
342
|
+
);
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Token Lifecycle — refresh, verify, and auth-expired signaling for the
|
|
5
|
+
* platform device token. Extracted from PlatformConnection so the relay
|
|
6
|
+
* lifecycle (sockets, reconnect, heartbeat) and the auth lifecycle (token
|
|
7
|
+
* refresh, verify, expired-signal) can be reasoned about apart. The token
|
|
8
|
+
* lifecycle has its own clock (24h periodic check), its own remote endpoints
|
|
9
|
+
* (/refresh, /verify), and its own failure modes (revocation vs. transient
|
|
10
|
+
* network error) — none of which need to be in PlatformConnection.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
getCredentials,
|
|
15
|
+
shouldRefreshToken,
|
|
16
|
+
updateCredentials,
|
|
17
|
+
} from './platform-credentials.js'
|
|
18
|
+
|
|
19
|
+
const PERIODIC_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000
|
|
20
|
+
|
|
21
|
+
export interface TokenLifecycleDeps {
|
|
22
|
+
/** Platform base URL — used to compose /api/auth/device/{refresh,verify} */
|
|
23
|
+
platformUrl: string
|
|
24
|
+
/** Send a message upstream over the live relay socket (no-op if closed). */
|
|
25
|
+
send: (message: unknown) => void
|
|
26
|
+
/** Read the current relay connection ID at the moment auth-expired fires. */
|
|
27
|
+
getConnectionId: () => string | null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class TokenLifecycle {
|
|
31
|
+
private intervalHandle: ReturnType<typeof setInterval> | null = null
|
|
32
|
+
|
|
33
|
+
constructor(private readonly deps: TokenLifecycleDeps) {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Refresh the device token if it's older than the refresh interval.
|
|
37
|
+
* Returns `true` if the token is (still) valid after this call, `false`
|
|
38
|
+
* if refresh was attempted and rejected with an auth error — in which
|
|
39
|
+
* case the caller should surface an auth-expired signal to the web
|
|
40
|
+
* rather than silently reusing a dead token.
|
|
41
|
+
*/
|
|
42
|
+
async maybeRefresh(): Promise<boolean> {
|
|
43
|
+
const creds = getCredentials()
|
|
44
|
+
if (!creds) return false
|
|
45
|
+
if (!shouldRefreshToken(creds)) return true
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch(`${this.deps.platformUrl}/api/auth/device/refresh`, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: {
|
|
51
|
+
'Authorization': `Bearer ${creds.token}`,
|
|
52
|
+
'Content-Type': 'application/json'
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
if (response.ok) {
|
|
57
|
+
const data = await response.json() as { accessToken: string }
|
|
58
|
+
updateCredentials({
|
|
59
|
+
token: data.accessToken,
|
|
60
|
+
lastRefreshedAt: new Date().toISOString()
|
|
61
|
+
})
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
if (response.status === 401 || response.status === 403) {
|
|
65
|
+
console.warn(`[Platform] Token refresh failed — auth is expired (${response.status}). Run \`mstro login --force\`.`)
|
|
66
|
+
this.notifyAuthExpired()
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
console.warn(`[Platform] Token refresh failed with status ${response.status}, will retry later`)
|
|
70
|
+
return true
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.warn('[Platform] Token refresh error:', err)
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Verify the current token against the platform. A rejection (401/403)
|
|
79
|
+
* means the token is permanently invalid (revoked, signing-key rotation,
|
|
80
|
+
* account deleted); the caller should stop looping reconnects and tell
|
|
81
|
+
* the user to run `mstro login --force`.
|
|
82
|
+
*
|
|
83
|
+
* Returns `true` when the token is valid or the verification endpoint
|
|
84
|
+
* is unreachable (we prefer false negatives to false positives — a
|
|
85
|
+
* network blip shouldn't force a re-login).
|
|
86
|
+
*/
|
|
87
|
+
async verify(): Promise<boolean> {
|
|
88
|
+
const creds = getCredentials()
|
|
89
|
+
if (!creds?.token) return false
|
|
90
|
+
try {
|
|
91
|
+
const response = await fetch(`${this.deps.platformUrl}/api/auth/device/verify`, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: { 'Authorization': `Bearer ${creds.token}` },
|
|
94
|
+
})
|
|
95
|
+
if (response.status === 401 || response.status === 403) {
|
|
96
|
+
console.warn(`[Platform] Token verify rejected (${response.status}) — auth is expired.`)
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
return true
|
|
100
|
+
} catch {
|
|
101
|
+
// Network error: treat as "probably valid" so a flaky connection
|
|
102
|
+
// doesn't force users to re-login. The WebSocket open itself will
|
|
103
|
+
// catch a truly bad token via the 4001 path.
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Surface an auth-expired condition to any paired web clients.
|
|
110
|
+
*
|
|
111
|
+
* Two cooperating paths deliver this signal — either alone is enough,
|
|
112
|
+
* both together cover every timing edge:
|
|
113
|
+
*
|
|
114
|
+
* 1. **CLI-initiated (this method):** we detected a 401 from the
|
|
115
|
+
* `/refresh` or `/verify` endpoint *while the relay socket is
|
|
116
|
+
* still open*. The injected `send` pushes the message upstream so
|
|
117
|
+
* the server relays it to paired webs before the CLI closes.
|
|
118
|
+
* A no-op if the socket is already closed.
|
|
119
|
+
*
|
|
120
|
+
* 2. **Server-initiated:** when the platform closes a CLI socket
|
|
121
|
+
* with 4001 or 4008, `handleAuthClose` in `clientHandlers.ts`
|
|
122
|
+
* broadcasts the same `clientAuthExpired` to paired webs. This
|
|
123
|
+
* covers cases where the CLI never had a chance to detect the
|
|
124
|
+
* rejection itself (e.g. token revoked while the socket was idle,
|
|
125
|
+
* server-side token rotation).
|
|
126
|
+
*
|
|
127
|
+
* IMPORTANT: never route this through PlatformConnection's
|
|
128
|
+
* `onRelayedMessage` callback — that callback feeds INCOMING web→CLI
|
|
129
|
+
* requests into the local handler, which would treat
|
|
130
|
+
* `clientAuthExpired` as an unknown inbound request.
|
|
131
|
+
*/
|
|
132
|
+
notifyAuthExpired(): void {
|
|
133
|
+
this.deps.send({
|
|
134
|
+
type: 'clientAuthExpired',
|
|
135
|
+
data: {
|
|
136
|
+
connectionId: this.deps.getConnectionId(),
|
|
137
|
+
message: 'The CLI\'s device token is invalid — run `mstro login --force` on the machine.',
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Best-effort token verification, fired in parallel with the socket
|
|
144
|
+
* open so a slow verify endpoint never delays reconnect.
|
|
145
|
+
*
|
|
146
|
+
* Only runs when the token is stale enough that we'd be about to
|
|
147
|
+
* refresh anyway — keeps the hot path free of an extra network call.
|
|
148
|
+
* A truly-revoked token that slips past this check still hits 4001
|
|
149
|
+
* on the WebSocket, which also triggers `notifyAuthExpired`.
|
|
150
|
+
*/
|
|
151
|
+
maybeVerifyInParallel(): void {
|
|
152
|
+
const creds = getCredentials()
|
|
153
|
+
if (!creds || !shouldRefreshToken(creds)) return
|
|
154
|
+
this.verify().then((valid) => {
|
|
155
|
+
if (!valid) this.notifyAuthExpired()
|
|
156
|
+
}).catch(() => { /* network error — rely on 4001 close path */ })
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
startPeriodicCheck(): void {
|
|
160
|
+
this.intervalHandle = setInterval(() => {
|
|
161
|
+
this.maybeRefresh()
|
|
162
|
+
}, PERIODIC_CHECK_INTERVAL_MS)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
stopPeriodicCheck(): void {
|
|
166
|
+
if (this.intervalHandle) {
|
|
167
|
+
clearInterval(this.intervalHandle)
|
|
168
|
+
this.intervalHandle = null
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -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,39 +105,11 @@ 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
|
-
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
const response = await fetch(`${this.platformUrl}/api/auth/device/refresh`, {
|
|
99
|
-
method: 'POST',
|
|
100
|
-
headers: {
|
|
101
|
-
'Authorization': `Bearer ${creds.token}`,
|
|
102
|
-
'Content-Type': 'application/json'
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
if (response.ok) {
|
|
107
|
-
const data = await response.json() as { accessToken: string }
|
|
108
|
-
updateCredentials({
|
|
109
|
-
token: data.accessToken,
|
|
110
|
-
lastRefreshedAt: new Date().toISOString()
|
|
111
|
-
})
|
|
112
|
-
} else {
|
|
113
|
-
console.warn('[Platform] Token refresh failed, will retry later')
|
|
114
|
-
}
|
|
115
|
-
} catch (err) {
|
|
116
|
-
console.warn('[Platform] Token refresh error:', err)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private startTokenRefreshCheck(): void {
|
|
121
|
-
this.tokenRefreshInterval = setInterval(() => {
|
|
122
|
-
this.maybeRefreshToken()
|
|
123
|
-
}, 24 * 60 * 60 * 1000)
|
|
108
|
+
this.tokens = new TokenLifecycle({
|
|
109
|
+
platformUrl: this.platformUrl,
|
|
110
|
+
send: (msg) => this.send(msg),
|
|
111
|
+
getConnectionId: () => this.connectionId,
|
|
112
|
+
})
|
|
124
113
|
}
|
|
125
114
|
|
|
126
115
|
private startHeartbeat(): void {
|
|
@@ -153,48 +142,18 @@ export class PlatformConnection {
|
|
|
153
142
|
}
|
|
154
143
|
}
|
|
155
144
|
|
|
156
|
-
private stopTokenRefreshCheck(): void {
|
|
157
|
-
if (this.tokenRefreshInterval) {
|
|
158
|
-
clearInterval(this.tokenRefreshInterval)
|
|
159
|
-
this.tokenRefreshInterval = null
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
145
|
connect(): void {
|
|
164
146
|
this.isIntentionallyClosed = false
|
|
165
|
-
|
|
166
|
-
const machineHostname = hostname()
|
|
167
|
-
const clientId = getClientId()
|
|
168
|
-
const machineId = getMachineIdentifier()
|
|
169
|
-
const nodeVersion = process.version
|
|
170
|
-
const osType = type().toLowerCase()
|
|
171
|
-
const cpuArch = arch()
|
|
172
|
-
|
|
173
|
-
const credentials = getCredentials()
|
|
174
|
-
const authToken = credentials?.token
|
|
147
|
+
this.protocolUpgradeRequired = false
|
|
175
148
|
|
|
149
|
+
const authToken = getCredentials()?.token
|
|
176
150
|
if (!authToken) {
|
|
177
151
|
console.error('\n❌ Not logged in. Run `mstro login` first.\n')
|
|
178
152
|
this.callbacks.onError?.('Not logged in - run `mstro login` first')
|
|
179
153
|
return
|
|
180
154
|
}
|
|
181
155
|
|
|
182
|
-
const
|
|
183
|
-
name,
|
|
184
|
-
workingDirectory: this.workingDirectory,
|
|
185
|
-
machineHostname,
|
|
186
|
-
clientId,
|
|
187
|
-
machineId,
|
|
188
|
-
nodeVersion,
|
|
189
|
-
osType,
|
|
190
|
-
cpuArch,
|
|
191
|
-
cliVersion: CLI_VERSION,
|
|
192
|
-
capabilities: JSON.stringify({}),
|
|
193
|
-
startedAt: this.startedAt,
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
const wsUrl = `${this.platformUrl.replace(/^http/, 'ws')}/ws/client?${params}`
|
|
197
|
-
|
|
156
|
+
const wsUrl = this.buildConnectionUrl()
|
|
198
157
|
try {
|
|
199
158
|
this.ws = new WebSocketImpl(wsUrl)
|
|
200
159
|
} catch (err) {
|
|
@@ -205,7 +164,31 @@ export class PlatformConnection {
|
|
|
205
164
|
return
|
|
206
165
|
}
|
|
207
166
|
|
|
208
|
-
const connectionTimeout =
|
|
167
|
+
const connectionTimeout = this.startConnectionTimeout()
|
|
168
|
+
this.attachSocketHandlers(this.ws, authToken, connectionTimeout)
|
|
169
|
+
this.tokens.maybeVerifyInParallel()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private buildConnectionUrl(): string {
|
|
173
|
+
const params = new URLSearchParams({
|
|
174
|
+
name: basename(this.workingDirectory),
|
|
175
|
+
workingDirectory: this.workingDirectory,
|
|
176
|
+
machineHostname: hostname(),
|
|
177
|
+
clientId: getClientId(),
|
|
178
|
+
machineId: getMachineIdentifier(),
|
|
179
|
+
nodeVersion: process.version,
|
|
180
|
+
osType: type().toLowerCase(),
|
|
181
|
+
cpuArch: arch(),
|
|
182
|
+
cliVersion: CLI_VERSION,
|
|
183
|
+
capabilities: JSON.stringify({}),
|
|
184
|
+
startedAt: this.startedAt,
|
|
185
|
+
protocolVersion: String(PROTOCOL_VERSION),
|
|
186
|
+
})
|
|
187
|
+
return `${this.platformUrl.replace(/^http/, 'ws')}/ws/client?${params}`
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private startConnectionTimeout(): ReturnType<typeof setTimeout> {
|
|
191
|
+
return setTimeout(() => {
|
|
209
192
|
const state = this.ws?.readyState
|
|
210
193
|
if (this.ws && (state === 0 || state === undefined)) {
|
|
211
194
|
console.error('\n❌ Connection timeout. The platform may have rejected your credentials.')
|
|
@@ -214,53 +197,133 @@ export class PlatformConnection {
|
|
|
214
197
|
this.callbacks.onError?.('Connection timeout - run `mstro login --force`')
|
|
215
198
|
}
|
|
216
199
|
}, 10000)
|
|
200
|
+
}
|
|
217
201
|
|
|
218
|
-
|
|
202
|
+
private attachSocketHandlers(
|
|
203
|
+
ws: WebSocket,
|
|
204
|
+
authToken: string,
|
|
205
|
+
connectionTimeout: ReturnType<typeof setTimeout>,
|
|
206
|
+
): void {
|
|
207
|
+
ws.onopen = () => {
|
|
219
208
|
clearTimeout(connectionTimeout)
|
|
220
|
-
|
|
221
|
-
this.
|
|
222
|
-
this.
|
|
209
|
+
ws.send(JSON.stringify({ type: 'auth', token: authToken }))
|
|
210
|
+
this.tokens.maybeRefresh()
|
|
211
|
+
this.tokens.startPeriodicCheck()
|
|
223
212
|
this.reconnectAttempts = 0
|
|
224
213
|
trackEvent(AnalyticsEvents.PLATFORM_CONNECTED)
|
|
225
214
|
}
|
|
226
215
|
|
|
227
|
-
|
|
216
|
+
ws.onmessage = (event) => {
|
|
228
217
|
try {
|
|
229
|
-
|
|
230
|
-
this.handleMessage(message)
|
|
218
|
+
this.handleMessage(JSON.parse(event.data.toString()))
|
|
231
219
|
} catch (err) {
|
|
232
220
|
console.error('Failed to parse platform message:', err)
|
|
233
221
|
}
|
|
234
222
|
}
|
|
235
223
|
|
|
236
|
-
|
|
224
|
+
ws.onclose = (event) => {
|
|
237
225
|
clearTimeout(connectionTimeout)
|
|
238
|
-
this.
|
|
239
|
-
this.isConnected = false
|
|
240
|
-
|
|
241
|
-
if (!this.isIntentionallyClosed) {
|
|
242
|
-
const isAuthFailure = event.code === 4001 ||
|
|
243
|
-
event.reason?.includes('Unauthorized') ||
|
|
244
|
-
(event.code === 1006 && !this.everConnected)
|
|
245
|
-
|
|
246
|
-
if (isAuthFailure) {
|
|
247
|
-
console.error('\n❌ Authentication failed. Your device token may be invalid or expired.')
|
|
248
|
-
console.error(' Run `mstro login --force` to re-authenticate.\n')
|
|
249
|
-
this.callbacks.onError?.('Authentication failed - run `mstro login --force`')
|
|
250
|
-
return
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
console.log('Disconnected, reconnecting...')
|
|
254
|
-
this.callbacks.onDisconnected?.()
|
|
255
|
-
trackEvent(AnalyticsEvents.PLATFORM_DISCONNECTED)
|
|
256
|
-
this.scheduleReconnect()
|
|
257
|
-
}
|
|
226
|
+
this.handleSocketClose(event)
|
|
258
227
|
}
|
|
259
228
|
|
|
260
|
-
|
|
229
|
+
ws.onerror = () => {
|
|
261
230
|
clearTimeout(connectionTimeout)
|
|
262
231
|
// onclose will be called after this
|
|
263
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
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private handleSocketClose(event: CloseEvent): void {
|
|
254
|
+
this.stopHeartbeat()
|
|
255
|
+
this.isConnected = false
|
|
256
|
+
|
|
257
|
+
if (this.isIntentionallyClosed) return
|
|
258
|
+
|
|
259
|
+
if (this.isProtocolTooOldClose(event)) {
|
|
260
|
+
this.handleProtocolUpgradeRequired()
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const isAuthFailure = event.code === 4001 ||
|
|
265
|
+
event.reason?.includes('Unauthorized') ||
|
|
266
|
+
(event.code === 1006 && !this.everConnected)
|
|
267
|
+
|
|
268
|
+
if (isAuthFailure) {
|
|
269
|
+
console.error('\n❌ Authentication failed. Your device token may be invalid or expired.')
|
|
270
|
+
console.error(' Run `mstro login --force` to re-authenticate.\n')
|
|
271
|
+
this.tokens.notifyAuthExpired()
|
|
272
|
+
this.callbacks.onError?.('Authentication failed - run `mstro login --force`')
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log('Disconnected, reconnecting...')
|
|
277
|
+
this.callbacks.onDisconnected?.()
|
|
278
|
+
trackEvent(AnalyticsEvents.PLATFORM_DISCONNECTED)
|
|
279
|
+
this.scheduleReconnect()
|
|
280
|
+
}
|
|
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)
|
|
264
327
|
}
|
|
265
328
|
|
|
266
329
|
private handleMessage(message: Record<string, unknown>): void {
|
|
@@ -325,7 +388,7 @@ export class PlatformConnection {
|
|
|
325
388
|
disconnect(): void {
|
|
326
389
|
this.isIntentionallyClosed = true
|
|
327
390
|
this.stopHeartbeat()
|
|
328
|
-
this.
|
|
391
|
+
this.tokens.stopPeriodicCheck()
|
|
329
392
|
|
|
330
393
|
if (this.reconnectTimeout) {
|
|
331
394
|
clearTimeout(this.reconnectTimeout)
|