specrails-desktop 2.35.0 → 2.42.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/README.md +94 -247
- package/client/dist/assets/ActivityFeedPage-DoEMumQS.js +1 -0
- package/client/dist/assets/AgentBrowserCapture-DIh4K0gh.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-mbq4WEoO.js +2 -0
- package/client/dist/assets/AgentModeCodePane-CKh7zuA4.js +2 -0
- package/client/dist/assets/AgentModeJobsPane-BDokPiLJ.js +1 -0
- package/client/dist/assets/AgentsPage-BFVAZbBj.js +87 -0
- package/client/dist/assets/AnalyticsPage-3Y9LuWwz.js +1 -0
- package/client/dist/assets/CodePage-C9KyuV_C.js +3 -0
- package/client/dist/assets/DesktopAnalyticsPage-Bhi54bXQ.js +1 -0
- package/client/dist/assets/{DocsDialog-BPAzDICW.js → DocsDialog-Dj6mt4ga.js} +2 -2
- package/client/dist/assets/{DocsPage-Dqo4Hn6G.js → DocsPage-24yWglNI.js} +1 -1
- package/client/dist/assets/ExportDropdown-ophapHrT.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-C1ucmxQh.js +19 -0
- package/client/dist/assets/JobDetailModal-DNWmgVHD.js +1 -0
- package/client/dist/assets/JobDetailPage-BcajZyuu.js +1 -0
- package/client/dist/assets/JobsPage-B9nTZw6e.js +1 -0
- package/client/dist/assets/LoopBuilderPage-Oc48iOFW.js +7 -0
- package/client/dist/assets/LoopPreviewModal-ae0Fr6SU.js +1 -0
- package/client/dist/assets/LoopsPage-CUEqzUBI.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-Deg39peL.js +1 -0
- package/client/dist/assets/PluginsPage-C1xtARIx.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-JZzrXFBN.js +1 -0
- package/client/dist/assets/RepositoryDeliveries-CSDsiVUz.js +5 -0
- package/client/dist/assets/RepositoryScopeSelector-D-RCaknl.js +1 -0
- package/client/dist/assets/ReviewPacketPage-x5EfGTiL.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-BSyuZxGq.js +1 -0
- package/client/dist/assets/TicketDetailModalContext-DqyF0so-.js +14 -0
- package/client/dist/assets/Trans-SnMX4MhW.js +1 -0
- package/client/dist/assets/agent-BdT0SB1D.js +1 -0
- package/client/dist/assets/agent-Bh9K7iEt.js +1 -0
- package/client/dist/assets/agent-C4ibdFk9.js +1 -0
- package/client/dist/assets/agent-CSFY22Zo.js +1 -0
- package/client/dist/assets/agent-DAnqxUn8.js +1 -0
- package/client/dist/assets/agent-DTQiu0Mi.js +1 -0
- package/client/dist/assets/agent-T54WuR_Y.js +1 -0
- package/client/dist/assets/agent-i75WDZOA.js +1 -0
- package/client/dist/assets/browser-05vRDg_y.js +1 -0
- package/client/dist/assets/browser-Bulb-cB1.js +1 -0
- package/client/dist/assets/browser-C7f-Jsg1.js +1 -0
- package/client/dist/assets/browser-CG0PE1j_.js +1 -0
- package/client/dist/assets/browser-CMzaqAjo.js +1 -0
- package/client/dist/assets/browser-DMajYdNX.js +1 -0
- package/client/dist/assets/browser-DxcOHrdP.js +1 -0
- package/client/dist/assets/browser-ObE7xVo3.js +1 -0
- package/client/dist/assets/builder-765NeYTp.js +1 -0
- package/client/dist/assets/builder-8MG3V6ea.js +1 -0
- package/client/dist/assets/builder-B-1UBs5K.js +1 -0
- package/client/dist/assets/builder-BTiGnPT0.js +1 -0
- package/client/dist/assets/builder-Cx9Rz-7u.js +1 -0
- package/client/dist/assets/builder-DKFMELBl.js +1 -0
- package/client/dist/assets/builder-OcmKrC0h.js +1 -0
- package/client/dist/assets/builder-P9qVBXFZ.js +1 -0
- package/client/dist/assets/{button-CJqFnUOL.js → button-DCBqEaZl.js} +1 -1
- package/client/dist/assets/{code-CkUuW4m9.js → code-B37GRg9C.js} +1 -1
- package/client/dist/assets/{code-B0O8-p8k.js → code-BBOEZbf9.js} +1 -1
- package/client/dist/assets/{code-B1VLZDhS.js → code-BRm-jcyx.js} +1 -1
- package/client/dist/assets/{code-Bpj4Ddml.js → code-CF7w62CR.js} +1 -1
- package/client/dist/assets/{code-BykBZ5-e.js → code-CLuvJt2r.js} +1 -1
- package/client/dist/assets/{code-B86IMAfz.js → code-D0RCm59f.js} +1 -1
- package/client/dist/assets/code-DaZ-ZwCT.js +1 -0
- package/client/dist/assets/{code-cAqaqMTx.js → code-iIHkGaCL.js} +1 -1
- package/client/dist/assets/commands-0XjAftla.js +1 -0
- package/client/dist/assets/commands-BAWSYHjQ.js +1 -0
- package/client/dist/assets/commands-BBwowgiE.js +1 -0
- package/client/dist/assets/commands-BacjIZLN.js +1 -0
- package/client/dist/assets/commands-Bs62IAZ3.js +1 -0
- package/client/dist/assets/commands-CLPdh-Hh.js +1 -0
- package/client/dist/assets/commands-DlbFzgrp.js +1 -0
- package/client/dist/assets/commands-LjPSsi5F.js +1 -0
- package/client/dist/assets/common-B5AZNYMH.js +1 -0
- package/client/dist/assets/common-BFZ0c_vN.js +1 -0
- package/client/dist/assets/common-BrlwEnL7.js +1 -0
- package/client/dist/assets/common-BttQx3z4.js +1 -0
- package/client/dist/assets/common-CM65k_-e.js +1 -0
- package/client/dist/assets/common-D4mvFFlu.js +1 -0
- package/client/dist/assets/common-TZ9Ny96S.js +1 -0
- package/client/dist/assets/common-nFpSF4GC.js +1 -0
- package/client/dist/assets/dashboard-B14uYXkG.js +1 -0
- package/client/dist/assets/dashboard-BK_mez-z.js +1 -0
- package/client/dist/assets/dashboard-BfBir_6o.js +1 -0
- package/client/dist/assets/dashboard-Bt0cCzeG.js +1 -0
- package/client/dist/assets/dashboard-BwkjxI6H.js +1 -0
- package/client/dist/assets/{dashboard-C8WOEiPu.js → dashboard-C3bDKf7s.js} +1 -1
- package/client/dist/assets/dashboard-CdvjGfog.js +1 -0
- package/client/dist/assets/dashboard-fRrP0Kv0.js +1 -0
- package/client/dist/assets/dialog-SRw01_QU.js +45 -0
- package/client/dist/assets/dist-js-CJ_2XX1Y.js +1 -0
- package/client/dist/assets/formatDistanceToNow-DSj5DprF.js +1 -0
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-YhD8rIEs.js +1 -0
- package/client/dist/assets/index-DwAmL7m5.css +2 -0
- package/client/dist/assets/index-EzrkUb8B.js +76 -0
- package/client/dist/assets/{jira-api-CcZMcEN5.js → jira-api-CSCqGr-Q.js} +1 -1
- package/client/dist/assets/{jobs-CN_zv4Gr.js → jobs-B8rrzfyZ.js} +1 -1
- package/client/dist/assets/{jobs-DUlWSYq2.js → jobs-BS6RTurf.js} +1 -1
- package/client/dist/assets/{jobs-Bpks6doU.js → jobs-BTT0Bsks.js} +1 -1
- package/client/dist/assets/{jobs-Bgq-PZkC.js → jobs-CezU69AE.js} +1 -1
- package/client/dist/assets/{jobs-SwYS2M-B.js → jobs-ClFJae0s.js} +1 -1
- package/client/dist/assets/{jobs-BAls3bWq.js → jobs-DJSFIFVy.js} +1 -1
- package/client/dist/assets/{jobs-DhxKgWFq.js → jobs-DhgGr8vm.js} +1 -1
- package/client/dist/assets/{jobs-B_TeSQxb.js → jobs-DzMjUcaN.js} +1 -1
- package/client/dist/assets/mcp-6rMoZfEO.js +1 -0
- package/client/dist/assets/mcp-BD8sL5mP.js +1 -0
- package/client/dist/assets/mcp-BkcA3I5s.js +1 -0
- package/client/dist/assets/mcp-CCpSelOh.js +1 -0
- package/client/dist/assets/mcp-CUAN6ORK.js +1 -0
- package/client/dist/assets/mcp-CqbRxRTE.js +1 -0
- package/client/dist/assets/mcp-DP2YfVhO.js +1 -0
- package/client/dist/assets/mcp-DUVQamDz.js +1 -0
- package/client/dist/assets/narration-B-ynWTpn.js +1 -0
- package/client/dist/assets/narration-Bj_laZwg.js +1 -0
- package/client/dist/assets/narration-BxHpmMv_.js +1 -0
- package/client/dist/assets/narration-C1Px0-0j.js +1 -0
- package/client/dist/assets/narration-CAXhGbzM.js +1 -0
- package/client/dist/assets/narration-DQ1l_Kq6.js +1 -0
- package/client/dist/assets/narration-XISXwqZd.js +1 -0
- package/client/dist/assets/narration-kwzfglQo.js +1 -0
- package/client/dist/assets/nav-1-Hh2kOz.js +1 -0
- package/client/dist/assets/nav-B34QkESm.js +1 -0
- package/client/dist/assets/nav-Bap_UXAq.js +1 -0
- package/client/dist/assets/{nav-pYuXq7sf.js → nav-BwxXIB24.js} +1 -1
- package/client/dist/assets/nav-Ch-zHBG5.js +1 -0
- package/client/dist/assets/nav-CygC_WN-.js +1 -0
- package/client/dist/assets/{nav-iwjCg5FR.js → nav-KnnLQtOQ.js} +1 -1
- package/client/dist/assets/nav-m00uxYUG.js +1 -0
- package/client/dist/assets/packet-BQ2tXfJI.js +1 -0
- package/client/dist/assets/packet-BdpOJ1Cy.js +1 -0
- package/client/dist/assets/packet-C3Ievb9R.js +1 -0
- package/client/dist/assets/packet-C4zJIG0Y.js +1 -0
- package/client/dist/assets/packet-Cx-zRuWn.js +1 -0
- package/client/dist/assets/packet-DUTGX52M.js +1 -0
- package/client/dist/assets/packet-DhsKtUPB.js +1 -0
- package/client/dist/assets/packet-z3LJ8Zyy.js +1 -0
- package/client/dist/assets/project-repositories-DYhTvwQ1.js +1 -0
- package/client/dist/assets/provider-capabilities-B-wOHfLP.js +1 -0
- package/client/dist/assets/settings-BBt2xtDQ.js +1 -0
- package/client/dist/assets/settings-BJB9RKJj.js +1 -0
- package/client/dist/assets/settings-CHwQR2rB.js +1 -0
- package/client/dist/assets/settings-CrMi3kSi.js +1 -0
- package/client/dist/assets/settings-DKyhDGD1.js +1 -0
- package/client/dist/assets/settings-Deupji9c.js +1 -0
- package/client/dist/assets/settings-DqRMmwAM.js +1 -0
- package/client/dist/assets/settings-J4rj6vo-.js +1 -0
- package/client/dist/assets/setup-2xGgu4Sk.js +1 -0
- package/client/dist/assets/setup-Ba11l2JA.js +1 -0
- package/client/dist/assets/setup-BzW4kMok.js +1 -0
- package/client/dist/assets/setup-COzwYhkJ.js +1 -0
- package/client/dist/assets/setup-CRVULhtF.js +1 -0
- package/client/dist/assets/setup-DgvlxwpE.js +1 -0
- package/client/dist/assets/setup-Dw_wYeEY.js +1 -0
- package/client/dist/assets/setup-ZPtEeJPW.js +1 -0
- package/client/dist/assets/spending-BqbdPm_4.js +1 -0
- package/client/dist/assets/square-Ci-PFcFe.js +1 -0
- package/client/dist/assets/{terminal-BFRZnMPP.js → terminal-BAzpHtpY.js} +1 -1
- package/client/dist/assets/{terminal-IahE4LW9.js → terminal-C7dHeR3x.js} +1 -1
- package/client/dist/assets/{terminal-tITa81iz.js → terminal-CMla5Qsz.js} +1 -1
- package/client/dist/assets/{terminal-BZiIu3rf.js → terminal-CdNXH-n9.js} +1 -1
- package/client/dist/assets/{terminal-ScjyXNxQ.js → terminal-Cw5bZsmV.js} +1 -1
- package/client/dist/assets/{terminal-C2NAd7BW.js → terminal-DFUP4eNv.js} +1 -1
- package/client/dist/assets/{terminal-d7GadXbN.js → terminal-DGqljeK_.js} +1 -1
- package/client/dist/assets/{terminal-QpXZU2Bn.js → terminal-cU70NodO.js} +1 -1
- package/client/dist/assets/useDesktop-RSMTyy7V.js +1 -0
- package/client/dist/assets/useSharedWebSocket-BOdS5_Iz.js +2 -0
- package/client/dist/index.html +34 -35
- package/docs/README.md +3 -0
- package/docs/agent-live-steering.md +57 -0
- package/docs/ci-cd.md +57 -0
- package/docs/code-explorer.md +48 -0
- package/docs/codex.md +12 -1
- package/docs/features/detachable-mission-windows.md +59 -0
- package/docs/guide/de/integrations/5-mcp-server.md +13 -9
- package/docs/guide/de/integrations/6-agent-chat.md +1 -0
- package/docs/guide/en/integrations/5-mcp-server.md +13 -9
- package/docs/guide/en/integrations/6-agent-chat.md +1 -0
- package/docs/guide/es/integrations/5-mcp-server.md +13 -9
- package/docs/guide/es/integrations/6-agent-chat.md +1 -0
- package/docs/guide/fr/integrations/5-mcp-server.md +13 -9
- package/docs/guide/fr/integrations/6-agent-chat.md +1 -0
- package/docs/guide/it/integrations/5-mcp-server.md +13 -9
- package/docs/guide/it/integrations/6-agent-chat.md +1 -0
- package/docs/guide/ja/integrations/5-mcp-server.md +13 -9
- package/docs/guide/ja/integrations/6-agent-chat.md +1 -0
- package/docs/guide/pt/integrations/5-mcp-server.md +13 -9
- package/docs/guide/pt/integrations/6-agent-chat.md +1 -0
- package/docs/guide/zh/integrations/5-mcp-server.md +13 -9
- package/docs/guide/zh/integrations/6-agent-chat.md +1 -0
- package/docs/internals/README.md +1 -0
- package/docs/internals/browser-capture-performance.md +55 -3
- package/docs/internals/browser-login-popups.md +45 -0
- package/docs/internals/browser-native-retina-audit.md +39 -0
- package/docs/internals/ci-cd-audit.md +39 -0
- package/docs/internals/core-runtime-updates.md +68 -0
- package/docs/internals/embedded-browser-native-webview-evaluation.md +2 -0
- package/docs/internals/implementation-reliability-audit.md +93 -0
- package/docs/internals/interactive-jobs.md +110 -6
- package/docs/internals/mcp-mission-audit.md +76 -0
- package/docs/internals/project-builder.md +404 -42
- package/docs/internals/safe-pr-review-flow.md +52 -10
- package/docs/internals/startup-recovery-audit.md +58 -0
- package/docs/mcp.md +90 -41
- package/docs/mission-processes.md +59 -0
- package/docs/multi-repo-projects.md +51 -0
- package/docs/platforms/windows-parity.md +70 -0
- package/docs/platforms/windows.md +13 -11
- package/docs/running-pipelines.md +14 -0
- package/mcp-bridge/dist/specrails-mcp.js +7186 -0
- package/package.json +9 -4
- package/server/dist/agent-chat-manager.js +538 -67
- package/server/dist/agent-chat-router.js +87 -6
- package/server/dist/agent-context-resolver.js +129 -25
- package/server/dist/agent-defaults.js +19 -11
- package/server/dist/agent-input-store.js +206 -0
- package/server/dist/agent-mcp-config.js +147 -16
- package/server/dist/agent-operator-prompt.js +259 -65
- package/server/dist/agent-spec-framing.js +202 -0
- package/server/dist/agent-steering.js +218 -0
- package/server/dist/agent-store.js +153 -0
- package/server/dist/api-not-found.js +8 -0
- package/server/dist/background-process-control.js +164 -0
- package/server/dist/background-process-service.js +61 -0
- package/server/dist/background-process-store.js +242 -0
- package/server/dist/background-windows-bootstrap.js +238 -0
- package/server/dist/blueprint-chat-manager.js +476 -62
- package/server/dist/blueprint-commit.js +21 -1
- package/server/dist/blueprint-draft-parser.js +122 -17
- package/server/dist/blueprint-generation.js +238 -0
- package/server/dist/blueprint-operator-prompt.js +91 -63
- package/server/dist/blueprint-router.js +55 -3
- package/server/dist/blueprint-spec-fixtures.js +85 -0
- package/server/dist/blueprint-spec-quality.js +57 -16
- package/server/dist/blueprint-store.js +92 -1
- package/server/dist/browser-capture-manager.js +86 -30
- package/server/dist/browser-playwright.js +168 -45
- package/server/dist/browser-viewport.js +26 -0
- package/server/dist/chat-manager.js +23 -15
- package/server/dist/chromium-archive.cjs +83 -0
- package/server/dist/chromium-resolver.js +72 -64
- package/server/dist/code-activity.js +145 -0
- package/server/dist/code-explorer-router.js +714 -114
- package/server/dist/core-compat.js +28 -1
- package/server/dist/core-execution.js +111 -0
- package/server/dist/core-package.js +4 -16
- package/server/dist/core-runtime.js +202 -0
- package/server/dist/core-update-manager.js +161 -60
- package/server/dist/core-update-state.js +29 -0
- package/server/dist/db.js +66 -35
- package/server/dist/delivery-evidence.js +142 -39
- package/server/dist/desktop-db.js +239 -43
- package/server/dist/desktop-router.js +131 -9
- package/server/dist/dev-ports.js +8 -3
- package/server/dist/external-mcp.js +370 -0
- package/server/dist/file-provenance.js +19 -12
- package/server/dist/file-story-manager.js +62 -64
- package/server/dist/file-story.js +118 -35
- package/server/dist/file-summary-generator.js +46 -5
- package/server/dist/file-summary-manager.js +355 -106
- package/server/dist/framework-manager.js +98 -110
- package/server/dist/framework-reseed.js +76 -10
- package/server/dist/headroom-manager.js +38 -3
- package/server/dist/host-control.js +52 -0
- package/server/dist/index.js +43 -2
- package/server/dist/interactive-job-session.js +149 -16
- package/server/dist/internal-api.js +48 -0
- package/server/dist/jira/jira-materializer.js +1 -0
- package/server/dist/json-tolerant.js +174 -0
- package/server/dist/legacy-migration.js +4 -2
- package/server/dist/loop-command-catalog.js +83 -21
- package/server/dist/loop-constants.js +6 -1
- package/server/dist/loop-decider.js +11 -1
- package/server/dist/loop-executors.js +194 -45
- package/server/dist/loop-factory.js +11 -4
- package/server/dist/loop-graph.js +90 -4
- package/server/dist/loop-run-manager.js +301 -69
- package/server/dist/loop-shell-invocation.js +21 -0
- package/server/dist/loop-step-idle.js +55 -0
- package/server/dist/loop-templates.js +31 -7
- package/server/dist/mcp/agent-capability.js +37 -4
- package/server/dist/mcp/guide.js +149 -36
- package/server/dist/mcp/mcp-admin-router.js +20 -4
- package/server/dist/mcp/mcp-server.js +89 -43
- package/server/dist/mcp/mcp-tiers.js +6 -5
- package/server/dist/mcp/mcp-token.js +3 -1
- package/server/dist/mcp/resources.js +5 -4
- package/server/dist/mcp/tools/catalog.js +4 -0
- package/server/dist/mcp/tools/code.js +125 -14
- package/server/dist/mcp/tools/context.js +153 -0
- package/server/dist/mcp/tools/git.js +15 -5
- package/server/dist/mcp/tools/jobs.js +84 -71
- package/server/dist/mcp/tools/loops.js +17 -6
- package/server/dist/mcp/tools/meta.js +74 -27
- package/server/dist/mcp/tools/mission.js +29 -0
- package/server/dist/mcp/tools/projects.js +22 -6
- package/server/dist/mcp/tools/rails.js +48 -11
- package/server/dist/mcp/tools/specs.js +86 -14
- package/server/dist/mcp/tools/support.js +1 -1
- package/server/dist/mcp/tools/types.js +117 -43
- package/server/dist/mcp/tools/watch.js +144 -51
- package/server/dist/milestone-chain-store.js +164 -0
- package/server/dist/milestone-chain.js +487 -0
- package/server/dist/milestone-progress.js +348 -0
- package/server/dist/mobile/mobile-devices.js +10 -7
- package/server/dist/mobile/mobile-gateway.js +2 -0
- package/server/dist/mobile/mobile-missions.js +214 -0
- package/server/dist/mobile/mobile-redact.js +17 -3
- package/server/dist/mobile/mobile-router.js +46 -4
- package/server/dist/mobile/mobile-ws.js +49 -4
- package/server/dist/multi-repo-bases.js +56 -0
- package/server/dist/multi-repo-checkout.js +101 -0
- package/server/dist/multi-repo-delivery.js +100 -0
- package/server/dist/multi-repo-execution-store.js +65 -0
- package/server/dist/multi-repo-execution.js +453 -0
- package/server/dist/offline-assemble.js +10 -8
- package/server/dist/openspec-runtime-plugin-commands.json +18 -0
- package/server/dist/openspec-runtime-plugin.js +120 -0
- package/server/dist/plugins/codex-spawn.js +49 -0
- package/server/dist/plugins/serena/manifest.js +2 -2
- package/server/dist/plugins/serena/templates/instructions.md +16 -0
- package/server/dist/profiles-router.js +3 -18
- package/server/dist/project-code-discovery.js +142 -0
- package/server/dist/project-git.js +18 -20
- package/server/dist/project-registry.js +272 -20
- package/server/dist/project-repositories.js +157 -0
- package/server/dist/project-repository-provenance.js +31 -0
- package/server/dist/project-router-background-processes.js +105 -0
- package/server/dist/project-router-git.js +75 -16
- package/server/dist/project-router-jobs.js +2 -126
- package/server/dist/project-router-loop-runs.js +72 -2
- package/server/dist/project-router-tickets.js +53 -8
- package/server/dist/project-router.js +239 -38
- package/server/dist/provider-detection.js +18 -6
- package/server/dist/provider-limit.js +64 -0
- package/server/dist/providers/claude-adapter.js +26 -1
- package/server/dist/providers/claude-background-tasks.js +18 -0
- package/server/dist/providers/claude-live-session.js +383 -0
- package/server/dist/providers/codex-adapter.js +19 -0
- package/server/dist/providers/codex-live-session.js +545 -0
- package/server/dist/providers/gemini-adapter.js +1 -0
- package/server/dist/providers/live-session-types.js +12 -0
- package/server/dist/providers/live-session.js +13 -0
- package/server/dist/providers/terminal-result.js +24 -0
- package/server/dist/queue-manager.js +2 -0
- package/server/dist/rail-isolated-launch.js +141 -16
- package/server/dist/rail-pr-decision.js +364 -99
- package/server/dist/rail-pr-store.js +24 -10
- package/server/dist/rail-worktree-release.js +34 -2
- package/server/dist/rail-worktrees-store.js +4 -4
- package/server/dist/rails-router.js +249 -55
- package/server/dist/repo-lock.js +92 -17
- package/server/dist/review-packet.js +23 -13
- package/server/dist/schemas/file-summary.v1.json +59 -59
- package/server/dist/schemas/profile.v1.json +137 -137
- package/server/dist/setup-manager.js +45 -7
- package/server/dist/shell-integration/bash-shim.bash +45 -0
- package/server/dist/shell-integration/fish-shim.fish +36 -0
- package/server/dist/shell-integration/powershell-shim.ps1 +48 -0
- package/server/dist/shell-integration/zsh-shim.zsh +47 -0
- package/server/dist/smash-runner.js +1 -0
- package/server/dist/spawn-lifecycle.js +44 -27
- package/server/dist/spec-contract-prompt.js +143 -0
- package/server/dist/spec-draft-parser.js +6 -1
- package/server/dist/spec-models.js +1 -0
- package/server/dist/stuck-run-detector.js +1 -0
- package/server/dist/terminal-manager.js +1 -1
- package/server/dist/terminal-shell-integration.js +4 -1
- package/server/dist/transient-children.js +619 -230
- package/server/dist/util/cli-prompt.js +78 -5
- package/server/dist/util/sqlite-migrations.js +20 -0
- package/server/dist/util/win-spawn.js +18 -3
- package/server/dist/util/windows-provider-shell.js +29 -0
- package/server/dist/verification-sentinel.js +14 -0
- package/server/dist/vitest-setup.js +2 -0
- package/server/dist/windows-job-supervisor.js +133 -0
- package/server/dist/workspace-manager.js +9 -30
- package/server/dist/worktree-manager.js +17 -2
- package/server/dist/worktree-overlay.js +3 -1
- package/client/dist/assets/ActivityFeedPage-LCBJ5WiD.js +0 -1
- package/client/dist/assets/AgentBrowserCapture-B-xQpyZr.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BI1Vcf3T.js +0 -2
- package/client/dist/assets/AgentModeCodePane-BTRwCl9h.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-VzeBJLqP.js +0 -1
- package/client/dist/assets/AgentsPage-CJj1K24O.js +0 -87
- package/client/dist/assets/AnalyticsPage-CQpV5_fX.js +0 -1
- package/client/dist/assets/CodePage-9-tFh0jB.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-BcZ0_O_C.js +0 -1
- package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-D36fZWai.js +0 -19
- package/client/dist/assets/JobDetailModal-BxsktbkQ.js +0 -1
- package/client/dist/assets/JobDetailPage-WWyCy6Sp.js +0 -1
- package/client/dist/assets/JobsPage-CEbM7uxU.js +0 -1
- package/client/dist/assets/LoopBuilderPage-CYsSN8io.js +0 -7
- package/client/dist/assets/LoopPreviewModal-BJhgX5wY.js +0 -1
- package/client/dist/assets/LoopsPage-DB4OqMot.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-CcjBHMHd.js +0 -1
- package/client/dist/assets/PluginsPage-CR8K4nM_.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-C45-qhF4.js +0 -1
- package/client/dist/assets/ReviewPacketPage-Cz_QQeC3.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-wFBd2vQP.js +0 -1
- package/client/dist/assets/TicketDetailModal-BvZoyDAL.js +0 -14
- package/client/dist/assets/Trans-DXZ-BLXb.js +0 -1
- package/client/dist/assets/agent-BQgKv_2F.js +0 -1
- package/client/dist/assets/agent-C1NRDJqL.js +0 -1
- package/client/dist/assets/agent-CLNleiy6.js +0 -1
- package/client/dist/assets/agent-CmceArkQ.js +0 -1
- package/client/dist/assets/agent-D3UxQ3SR.js +0 -1
- package/client/dist/assets/agent-DB1JeE64.js +0 -1
- package/client/dist/assets/agent-DXkHcQ-D.js +0 -1
- package/client/dist/assets/agent-DjJJJ2M8.js +0 -1
- package/client/dist/assets/auth-D_5bQv5W.js +0 -1
- package/client/dist/assets/ban-E4EaZe3a.js +0 -1
- package/client/dist/assets/bot-DzuuuC5R.js +0 -1
- package/client/dist/assets/browser-BgqHDMXP.js +0 -1
- package/client/dist/assets/browser-CsKJtrpv.js +0 -1
- package/client/dist/assets/browser-Cu58EpZU.js +0 -1
- package/client/dist/assets/browser-CvXOJm-j.js +0 -1
- package/client/dist/assets/browser-DOrFh1c7.js +0 -1
- package/client/dist/assets/browser-DQv_Jo7x.js +0 -1
- package/client/dist/assets/browser-LPgxvJu2.js +0 -1
- package/client/dist/assets/browser-eL-3MIEI.js +0 -1
- package/client/dist/assets/builder-0IWtosUm.js +0 -1
- package/client/dist/assets/builder-Bf2N6XWn.js +0 -1
- package/client/dist/assets/builder-BnqaaBkT.js +0 -1
- package/client/dist/assets/builder-BxxfGgAO.js +0 -1
- package/client/dist/assets/builder-C5IIgWsq.js +0 -1
- package/client/dist/assets/builder-CHuahItu.js +0 -1
- package/client/dist/assets/builder-CjWosXL_.js +0 -1
- package/client/dist/assets/builder-CpCIOoKj.js +0 -1
- package/client/dist/assets/code-CHPkOkDS.js +0 -1
- package/client/dist/assets/commands-6tYQih5m.js +0 -1
- package/client/dist/assets/commands-BK2QinXN.js +0 -1
- package/client/dist/assets/commands-C2Nca76h.js +0 -1
- package/client/dist/assets/commands-CKdLihNX.js +0 -1
- package/client/dist/assets/commands-CYIBTAlc.js +0 -1
- package/client/dist/assets/commands-CjUtYJIM.js +0 -1
- package/client/dist/assets/commands-Cq_u_8y9.js +0 -1
- package/client/dist/assets/commands-D_ZHYQug.js +0 -1
- package/client/dist/assets/common-6MLbD-jM.js +0 -1
- package/client/dist/assets/common-BKGEu4rm.js +0 -1
- package/client/dist/assets/common-BNbnv8cE.js +0 -1
- package/client/dist/assets/common-BqGWtDbt.js +0 -1
- package/client/dist/assets/common-DBtTzeYm.js +0 -1
- package/client/dist/assets/common-DEW3Nr8n.js +0 -1
- package/client/dist/assets/common-DHsv9N5d.js +0 -1
- package/client/dist/assets/common-mkrEcW-Y.js +0 -1
- package/client/dist/assets/dashboard-B9GhC5ku.js +0 -1
- package/client/dist/assets/dashboard-CBnv6zOI.js +0 -1
- package/client/dist/assets/dashboard-CaP0fU87.js +0 -1
- package/client/dist/assets/dashboard-D4ZZ4x84.js +0 -1
- package/client/dist/assets/dashboard-DmuTQBX8.js +0 -1
- package/client/dist/assets/dashboard-DynLYj3d.js +0 -1
- package/client/dist/assets/dashboard-zj4gGFJ1.js +0 -1
- package/client/dist/assets/dialog-CdWCb02j.js +0 -45
- package/client/dist/assets/dist-bVqfYmby.js +0 -1
- package/client/dist/assets/dist-js-CWeUdf5Z.js +0 -1
- package/client/dist/assets/formatDistanceToNow-C0ImhVKp.js +0 -1
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-owhDdTXs.js +0 -1
- package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
- package/client/dist/assets/i18n-DwgvwTnx.js +0 -2
- package/client/dist/assets/index-DhjjFyU5.js +0 -66
- package/client/dist/assets/index-xBFsOEiJ.css +0 -2
- package/client/dist/assets/mcp-Czd_iyp0.js +0 -1
- package/client/dist/assets/mcp-D2pc5GGx.js +0 -1
- package/client/dist/assets/mcp-D4ZjtIx3.js +0 -1
- package/client/dist/assets/mcp-DIfBUv2D.js +0 -1
- package/client/dist/assets/mcp-DsTwmjST.js +0 -1
- package/client/dist/assets/mcp-SMkn8boG.js +0 -1
- package/client/dist/assets/mcp-fSfiVYAc.js +0 -1
- package/client/dist/assets/mcp-uDkXYu4L.js +0 -1
- package/client/dist/assets/narration-4Z2VUhZw.js +0 -1
- package/client/dist/assets/narration-BGuiEDj3.js +0 -1
- package/client/dist/assets/narration-BuSoqRmv.js +0 -1
- package/client/dist/assets/narration-CB9t29fy.js +0 -1
- package/client/dist/assets/narration-CU3x8FHD.js +0 -1
- package/client/dist/assets/narration-CWeXhfP5.js +0 -1
- package/client/dist/assets/narration-DRaDnhT5.js +0 -1
- package/client/dist/assets/narration-DtEbQgCw.js +0 -1
- package/client/dist/assets/nav-ApJVg3xu.js +0 -1
- package/client/dist/assets/nav-BKNky1Bg.js +0 -1
- package/client/dist/assets/nav-C6vWWKYx.js +0 -1
- package/client/dist/assets/nav-CXJ6Aw89.js +0 -1
- package/client/dist/assets/nav-CsmFBIDf.js +0 -1
- package/client/dist/assets/nav-DjkmQJjd.js +0 -1
- package/client/dist/assets/packet-B1IxaL5f.js +0 -1
- package/client/dist/assets/packet-Bh5kkbiB.js +0 -1
- package/client/dist/assets/packet-By8fqYxW.js +0 -1
- package/client/dist/assets/packet-CZdvA44E.js +0 -1
- package/client/dist/assets/packet-CrjHRQCP.js +0 -1
- package/client/dist/assets/packet-Dwi2pTtx.js +0 -1
- package/client/dist/assets/packet-_-ZIa0IR.js +0 -1
- package/client/dist/assets/packet-n0W6E1Hn.js +0 -1
- package/client/dist/assets/play-DNlrawbS.js +0 -1
- package/client/dist/assets/provider-capabilities-Doej9lfa.js +0 -1
- package/client/dist/assets/settings-Bd0x4TBa.js +0 -1
- package/client/dist/assets/settings-C1-7cXzC.js +0 -1
- package/client/dist/assets/settings-CAFaT38b.js +0 -1
- package/client/dist/assets/settings-Ct3v1nW2.js +0 -1
- package/client/dist/assets/settings-DHh7_IF5.js +0 -1
- package/client/dist/assets/settings-DQXAmdcv.js +0 -1
- package/client/dist/assets/settings-guzlhpup.js +0 -1
- package/client/dist/assets/settings-sZfTPMEH.js +0 -1
- package/client/dist/assets/setup-3jWaoVUO.js +0 -1
- package/client/dist/assets/setup-BjgRneb2.js +0 -1
- package/client/dist/assets/setup-BzFIjv-f.js +0 -1
- package/client/dist/assets/setup-C4VfQsxE.js +0 -1
- package/client/dist/assets/setup-DfUkpDbN.js +0 -1
- package/client/dist/assets/setup-ECe90Pmu.js +0 -1
- package/client/dist/assets/setup-dlqC4Jmm.js +0 -1
- package/client/dist/assets/setup-jwfLMiM2.js +0 -1
- package/client/dist/assets/spending-BQX3P7Cq.js +0 -1
- package/client/dist/assets/useDesktop-BzX769yH.js +0 -1
|
@@ -3,13 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.atomicWrite = exports.FrameworkManager = void 0;
|
|
7
|
-
exports.frameworkRoot = frameworkRoot;
|
|
8
|
-
exports.readCurrentFrameworkVersion = readCurrentFrameworkVersion;
|
|
6
|
+
exports.atomicWrite = exports.FrameworkManager = exports.readCurrentFrameworkVersion = exports.frameworkRoot = void 0;
|
|
9
7
|
const child_process_1 = require("child_process");
|
|
10
8
|
const fs_1 = __importDefault(require("fs"));
|
|
11
9
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const
|
|
10
|
+
const crypto_1 = require("crypto");
|
|
11
|
+
const core_runtime_1 = require("./core-runtime");
|
|
12
|
+
var core_runtime_2 = require("./core-runtime");
|
|
13
|
+
Object.defineProperty(exports, "frameworkRoot", { enumerable: true, get: function () { return core_runtime_2.frameworkRoot; } });
|
|
14
|
+
Object.defineProperty(exports, "readCurrentFrameworkVersion", { enumerable: true, get: function () { return core_runtime_2.readCurrentFrameworkVersion; } });
|
|
13
15
|
const artifact_registry_1 = require("./artifact-registry");
|
|
14
16
|
Object.defineProperty(exports, "atomicWrite", { enumerable: true, get: function () { return artifact_registry_1.atomicWrite; } });
|
|
15
17
|
const semver_lite_1 = require("./semver-lite");
|
|
@@ -42,85 +44,6 @@ function realpathOrSelf(p) {
|
|
|
42
44
|
return p;
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
|
-
/** `~/.specrails/framework` — same home as the registry. */
|
|
46
|
-
function frameworkRoot(home) {
|
|
47
|
-
return path_1.default.join((0, artifact_registry_1.resolveHome)(home), '.specrails', 'framework');
|
|
48
|
-
}
|
|
49
|
-
/** Path to the `current` symlink under the framework root. */
|
|
50
|
-
function currentLink(home) {
|
|
51
|
-
return path_1.default.join(frameworkRoot(home), 'current');
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Read the framework version from the documented per-version stamp marker that
|
|
55
|
-
* core's `install-framework` writes at `<versionDir>/.framework-stamp<providerDir>.json`
|
|
56
|
-
* (`{ version, provider, at }`). When `current` is a real dir (Windows copy
|
|
57
|
-
* fallback) or a junction, the version is NOT recoverable from the path basename
|
|
58
|
-
* (`current`), so the marker inside the dir is the source of truth.
|
|
59
|
-
*
|
|
60
|
-
* Returns the first non-empty `version` field found among any stamp markers
|
|
61
|
-
* directly under `dir`, or null when none is present/readable.
|
|
62
|
-
*/
|
|
63
|
-
function readFrameworkVersionMarker(dir) {
|
|
64
|
-
let entries;
|
|
65
|
-
try {
|
|
66
|
-
entries = fs_1.default.readdirSync(dir);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
for (const name of entries) {
|
|
72
|
-
// `.framework-stamp.claude.json` / `.framework-stamp.codex.json` / `.gemini`.
|
|
73
|
-
if (!name.startsWith('.framework-stamp') || !name.endsWith('.json'))
|
|
74
|
-
continue;
|
|
75
|
-
try {
|
|
76
|
-
const parsed = JSON.parse(fs_1.default.readFileSync(path_1.default.join(dir, name), 'utf8'));
|
|
77
|
-
if (typeof parsed.version === 'string') {
|
|
78
|
-
const v = parsed.version.trim();
|
|
79
|
-
if (v.length > 0)
|
|
80
|
-
return v;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
/* unreadable / malformed marker — try the next one */
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* The version `framework/current` resolves to, or null when `current` is absent
|
|
91
|
-
* / unresolvable. POSIX: reads the basename of the symlink target (the `<version>`
|
|
92
|
-
* dir name). Windows junction/copy fallback: `current` is a real dir (or a
|
|
93
|
-
* junction whose `lstat` is not a POSIX symlink), so the basename is `current`
|
|
94
|
-
* and the version is read from the documented `.framework-stamp*.json` marker
|
|
95
|
-
* inside it instead.
|
|
96
|
-
*/
|
|
97
|
-
function readCurrentFrameworkVersion(home) {
|
|
98
|
-
const link = currentLink(home);
|
|
99
|
-
try {
|
|
100
|
-
const st = fs_1.default.lstatSync(link);
|
|
101
|
-
if (st.isSymbolicLink()) {
|
|
102
|
-
const target = fs_1.default.readlinkSync(link);
|
|
103
|
-
const resolved = path_1.default.isAbsolute(target) ? target : path_1.default.resolve(frameworkRoot(home), target);
|
|
104
|
-
const base = path_1.default.basename(resolved);
|
|
105
|
-
// A POSIX symlink targets the `<version>` dir by name. A Windows junction
|
|
106
|
-
// read as a symlink also yields the absolute `<version>` target, so the
|
|
107
|
-
// basename is the version. Only when the basename is still `current`
|
|
108
|
-
// (unresolvable) do we fall back to the stamp marker inside the dir.
|
|
109
|
-
if (base !== 'current')
|
|
110
|
-
return base;
|
|
111
|
-
return readFrameworkVersionMarker(resolved);
|
|
112
|
-
}
|
|
113
|
-
if (st.isDirectory()) {
|
|
114
|
-
// Windows copy fallback / non-symlink junction: `current` is a real dir
|
|
115
|
-
// holding the materialized framework. Read the version from its marker.
|
|
116
|
-
return readFrameworkVersionMarker(link);
|
|
117
|
-
}
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
47
|
class FrameworkManager {
|
|
125
48
|
home;
|
|
126
49
|
broadcast;
|
|
@@ -139,30 +62,21 @@ class FrameworkManager {
|
|
|
139
62
|
const cli = path_1.default.join(this.coreRoot, 'dist', 'installer', 'cli.js');
|
|
140
63
|
return fs_1.default.existsSync(cli) ? realpathOrSelf(cli) : null;
|
|
141
64
|
}
|
|
142
|
-
|
|
143
|
-
return bundled ? realpathOrSelf(bundled) : null;
|
|
65
|
+
return (0, core_runtime_1.resolveCoreRuntime)(this.home)?.cli ?? null;
|
|
144
66
|
}
|
|
145
67
|
/** True when a usable core (override or bundled) is present (else methods no-op). */
|
|
146
68
|
isAvailable() {
|
|
147
69
|
return this.resolveCli() !== null;
|
|
148
70
|
}
|
|
149
71
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
72
|
+
* Historical API name: this is the SELECTED runtime's version (including a
|
|
73
|
+
* managed update or newer external CLI), not necessarily the shipped bundle. Null when
|
|
152
74
|
* neither is present / unreadable.
|
|
153
75
|
*/
|
|
154
76
|
bundledVersion() {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const v = pkg.version?.trim();
|
|
159
|
-
return v && v.length > 0 ? v : null;
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return (0, bundled_core_1.getBundledCoreVersion)();
|
|
77
|
+
return this.coreRoot
|
|
78
|
+
? (0, core_runtime_1.readCoreRuntime)(this.coreRoot, 'managed')?.version ?? null
|
|
79
|
+
: (0, core_runtime_1.resolveCoreRuntime)(this.home)?.version ?? null;
|
|
166
80
|
}
|
|
167
81
|
/**
|
|
168
82
|
* Materialize the framework for `version` (default: the bundled core version)
|
|
@@ -183,7 +97,7 @@ class FrameworkManager {
|
|
|
183
97
|
if (!ver) {
|
|
184
98
|
return { ran: false, version: null, providers: [], errors: [] };
|
|
185
99
|
}
|
|
186
|
-
const fwDir = frameworkRoot(this.home);
|
|
100
|
+
const fwDir = (0, core_runtime_1.frameworkRoot)(this.home);
|
|
187
101
|
fs_1.default.mkdirSync(fwDir, { recursive: true });
|
|
188
102
|
const errors = [];
|
|
189
103
|
const done = [];
|
|
@@ -233,16 +147,79 @@ class FrameworkManager {
|
|
|
233
147
|
const cli = this.resolveCli();
|
|
234
148
|
if (!cli)
|
|
235
149
|
return { ok: false, detail: 'no usable core CLI to run swap-current with' };
|
|
236
|
-
if (readCurrentFrameworkVersion(this.home) === version)
|
|
150
|
+
if ((0, core_runtime_1.readCurrentFrameworkVersion)(this.home) === version)
|
|
237
151
|
return { ok: true, detail: null };
|
|
238
|
-
const fwDir = frameworkRoot(this.home);
|
|
152
|
+
const fwDir = (0, core_runtime_1.frameworkRoot)(this.home);
|
|
153
|
+
const current = path_1.default.join(fwDir, 'current');
|
|
154
|
+
const previousVersion = (0, core_runtime_1.readCurrentFrameworkVersion)(this.home);
|
|
155
|
+
let backup = null;
|
|
156
|
+
if (previousVersion) {
|
|
157
|
+
backup = path_1.default.join(fwDir, `.current-recovery-${(0, crypto_1.randomUUID)()}`);
|
|
158
|
+
try {
|
|
159
|
+
if (fs_1.default.lstatSync(current).isSymbolicLink()) {
|
|
160
|
+
fs_1.default.symlinkSync(fs_1.default.realpathSync(current), backup, process.platform === 'win32' ? 'junction' : 'dir');
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
fs_1.default.cpSync(current, backup, {
|
|
164
|
+
recursive: true, dereference: false, verbatimSymlinks: true,
|
|
165
|
+
// Node 22's native recursive-copy fast path mishandles Unicode on
|
|
166
|
+
// Windows (nodejs/node#61878). Use JS traversal into the fresh backup
|
|
167
|
+
// so neither native directory copy nor file replacement is used.
|
|
168
|
+
filter: () => true, mode: fs_1.default.constants.COPYFILE_FICLONE,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return { ok: false, detail: `Could not preserve the active framework before updating: ${error instanceof Error ? error.message : String(error)}` };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const failed = (detail) => {
|
|
177
|
+
if (!backup)
|
|
178
|
+
return { ok: false, detail };
|
|
179
|
+
const actualVersion = (0, core_runtime_1.readCurrentFrameworkVersion)(this.home);
|
|
180
|
+
if (actualVersion === previousVersion) {
|
|
181
|
+
try {
|
|
182
|
+
fs_1.default.rmSync(backup, { recursive: true, force: true });
|
|
183
|
+
}
|
|
184
|
+
catch { /* safe recovery copy */ }
|
|
185
|
+
return { ok: false, detail };
|
|
186
|
+
}
|
|
187
|
+
// Never overwrite another operation's published version. Preserve the
|
|
188
|
+
// recovery copy for diagnosis instead of guessing which install owns it.
|
|
189
|
+
if (actualVersion !== null && actualVersion !== version)
|
|
190
|
+
return { ok: false, detail: `${detail}; recovery retained at ${backup} (current changed concurrently)` };
|
|
191
|
+
const failedCurrent = path_1.default.join(fwDir, `.failed-current-${(0, crypto_1.randomUUID)()}`);
|
|
192
|
+
try {
|
|
193
|
+
try {
|
|
194
|
+
fs_1.default.lstatSync(current);
|
|
195
|
+
fs_1.default.renameSync(current, failedCurrent);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (error.code !== 'ENOENT')
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
fs_1.default.renameSync(backup, current);
|
|
202
|
+
if ((0, core_runtime_1.readCurrentFrameworkVersion)(this.home) !== previousVersion)
|
|
203
|
+
throw new Error('restored framework identity did not match');
|
|
204
|
+
return { ok: false, detail: `${detail}; previous framework ${previousVersion} restored` };
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
return { ok: false, detail: `${detail}; recovery retained at ${backup}: ${error instanceof Error ? error.message : String(error)}` };
|
|
208
|
+
}
|
|
209
|
+
};
|
|
239
210
|
const res = (0, child_process_1.spawnSync)(nodeInterpreter(), [cli, 'swap-current', '--framework-dir', fwDir, '--version', version], { env: this.childEnv(), encoding: 'utf-8', timeout: 120_000 });
|
|
240
211
|
if (res.error || (res.status ?? 1) !== 0) {
|
|
241
212
|
const detail = (res.error?.message ?? `${res.stderr || res.stdout || ''}`.trim()) || `exit ${res.status ?? 'unknown'}`;
|
|
242
|
-
return
|
|
213
|
+
return failed(detail);
|
|
243
214
|
}
|
|
244
|
-
if (readCurrentFrameworkVersion(this.home) !== version) {
|
|
245
|
-
return
|
|
215
|
+
if ((0, core_runtime_1.readCurrentFrameworkVersion)(this.home) !== version) {
|
|
216
|
+
return failed(`swap-current exited 0 but current still resolves to ${(0, core_runtime_1.readCurrentFrameworkVersion)(this.home) ?? 'nothing'}`);
|
|
217
|
+
}
|
|
218
|
+
if (backup) {
|
|
219
|
+
try {
|
|
220
|
+
fs_1.default.rmSync(backup, { recursive: true, force: true });
|
|
221
|
+
}
|
|
222
|
+
catch { /* recovery copy is safe to retain */ }
|
|
246
223
|
}
|
|
247
224
|
return { ok: true, detail: null };
|
|
248
225
|
}
|
|
@@ -253,12 +230,23 @@ class FrameworkManager {
|
|
|
253
230
|
* no bundled core is present. Returns the resulting current version (or null).
|
|
254
231
|
*/
|
|
255
232
|
versionCheck(providers = ['claude']) {
|
|
256
|
-
|
|
233
|
+
// Keep an already activated framework even if its old updater discarded the
|
|
234
|
+
// corresponding package. Runtime consumers report the repair requirement.
|
|
235
|
+
let bundled;
|
|
236
|
+
try {
|
|
237
|
+
bundled = this.bundledVersion();
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return { swapped: false, version: (0, core_runtime_1.readCurrentFrameworkVersion)(this.home) };
|
|
241
|
+
}
|
|
257
242
|
if (!this.resolveCli() || !bundled) {
|
|
258
|
-
return { swapped: false, version: readCurrentFrameworkVersion(this.home) };
|
|
243
|
+
return { swapped: false, version: (0, core_runtime_1.readCurrentFrameworkVersion)(this.home) };
|
|
259
244
|
}
|
|
260
|
-
const current = readCurrentFrameworkVersion(this.home);
|
|
245
|
+
const current = (0, core_runtime_1.readCurrentFrameworkVersion)(this.home);
|
|
261
246
|
if (current === bundled) {
|
|
247
|
+
const mat = this.materialize(bundled, providers);
|
|
248
|
+
if (mat.errors.length)
|
|
249
|
+
this.broadcast?.({ type: 'framework.update_failed', version: bundled, errors: mat.errors });
|
|
262
250
|
return { swapped: false, version: current };
|
|
263
251
|
}
|
|
264
252
|
// Anti-downgrade guard: NEVER swap `current` down to the bundled version when
|
|
@@ -318,7 +306,7 @@ class FrameworkManager {
|
|
|
318
306
|
catch {
|
|
319
307
|
/* broadcast is best-effort */
|
|
320
308
|
}
|
|
321
|
-
return { swapped: false, version: readCurrentFrameworkVersion(this.home) };
|
|
309
|
+
return { swapped: false, version: (0, core_runtime_1.readCurrentFrameworkVersion)(this.home) };
|
|
322
310
|
}
|
|
323
311
|
/**
|
|
324
312
|
* Assemble a project workspace by SYMLINKING the materialized framework into
|
|
@@ -332,7 +320,7 @@ class FrameworkManager {
|
|
|
332
320
|
const ver = input.version ?? this.bundledVersion();
|
|
333
321
|
if (!ver)
|
|
334
322
|
return { ran: false };
|
|
335
|
-
const fwDir = frameworkRoot(this.home);
|
|
323
|
+
const fwDir = (0, core_runtime_1.frameworkRoot)(this.home);
|
|
336
324
|
const res = (0, child_process_1.spawnSync)(nodeInterpreter(), [
|
|
337
325
|
cli,
|
|
338
326
|
'assemble',
|
|
@@ -362,7 +350,7 @@ class FrameworkManager {
|
|
|
362
350
|
// Point the core CLI's scriptDir at the package whose framework we are
|
|
363
351
|
// materializing so its template/command sources resolve from there: the
|
|
364
352
|
// OVERRIDE core (update channel) when set, else the bundled package.
|
|
365
|
-
const coreRoot = this.coreRoot ??
|
|
353
|
+
const coreRoot = this.coreRoot ?? (0, core_runtime_1.resolveCoreRuntime)(this.home)?.root;
|
|
366
354
|
if (coreRoot)
|
|
367
355
|
env.SPECRAILS_CORE_SCRIPT_DIR = coreRoot;
|
|
368
356
|
return env;
|
|
@@ -46,6 +46,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
46
46
|
};
|
|
47
47
|
})();
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.assertWorkspaceCoreReady = void 0;
|
|
49
50
|
exports.isFrameworkAutoswapEnabled = isFrameworkAutoswapEnabled;
|
|
50
51
|
exports.readWorkspaceFrameworkVersion = readWorkspaceFrameworkVersion;
|
|
51
52
|
exports.reseedStaleWorkspaces = reseedStaleWorkspaces;
|
|
@@ -55,6 +56,10 @@ const providers_1 = require("./providers");
|
|
|
55
56
|
const workspace_manager_1 = require("./workspace-manager");
|
|
56
57
|
const workspace_resolution_1 = require("./workspace-resolution");
|
|
57
58
|
const offline_assemble_1 = require("./offline-assemble");
|
|
59
|
+
const core_runtime_1 = require("./core-runtime");
|
|
60
|
+
const core_update_state_1 = require("./core-update-state");
|
|
61
|
+
var core_update_state_2 = require("./core-update-state");
|
|
62
|
+
Object.defineProperty(exports, "assertWorkspaceCoreReady", { enumerable: true, get: function () { return core_update_state_2.assertWorkspaceCoreReady; } });
|
|
58
63
|
function isFrameworkAutoswapEnabled() {
|
|
59
64
|
const v = (process.env.SPECRAILS_FRAMEWORK_AUTOSWAP ?? '').toLowerCase();
|
|
60
65
|
return v !== 'false' && v !== '0' && v !== 'off';
|
|
@@ -84,15 +89,37 @@ async function reseedStaleWorkspaces(projects, currentVersion, io) {
|
|
|
84
89
|
const results = [];
|
|
85
90
|
if (!currentVersion)
|
|
86
91
|
return results;
|
|
92
|
+
// Mark every affected workspace before yielding for the first install. A
|
|
93
|
+
// later project must not launch against mixed templates while earlier ones
|
|
94
|
+
// are still being refreshed.
|
|
95
|
+
const pendingErrors = new Map();
|
|
96
|
+
for (const project of projects) {
|
|
97
|
+
const workspace = (0, workspace_manager_1.workspacePathFor)(project.slug);
|
|
98
|
+
if (!(0, workspace_resolution_1.isWorkspacePopulated)(workspace))
|
|
99
|
+
continue;
|
|
100
|
+
if (readWorkspaceFrameworkVersion(project.slug) === currentVersion && !fs.existsSync((0, core_update_state_1.coreUpdatePendingPath)(workspace)))
|
|
101
|
+
continue;
|
|
102
|
+
try {
|
|
103
|
+
fs.mkdirSync(path.dirname((0, core_update_state_1.coreUpdatePendingPath)(workspace)), { recursive: true });
|
|
104
|
+
fs.writeFileSync((0, core_update_state_1.coreUpdatePendingPath)(workspace), JSON.stringify({ version: currentVersion, projectId: project.id }), { mode: 0o600 });
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
pendingErrors.set(project.id, `Could not mark the workspace for Core refresh: ${String(error)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
87
110
|
const assemble = io?.assemble
|
|
88
111
|
?? (async (p, providers) => {
|
|
89
|
-
await (0, offline_assemble_1.assembleProjectOffline)({
|
|
112
|
+
const results = await (0, offline_assemble_1.assembleProjectOffline)({
|
|
90
113
|
projectPath: p.path,
|
|
91
114
|
slug: p.slug,
|
|
92
115
|
desktopProjectId: p.id,
|
|
93
116
|
providers,
|
|
94
117
|
continueOnError: true,
|
|
118
|
+
preserveExistingConfig: true,
|
|
95
119
|
});
|
|
120
|
+
const failed = results.filter(result => !result.ok);
|
|
121
|
+
if (failed.length)
|
|
122
|
+
throw new Error(failed.map(result => `${result.provider}: ${result.error}`).join('; '));
|
|
96
123
|
});
|
|
97
124
|
for (const project of projects) {
|
|
98
125
|
const workspace = (0, workspace_manager_1.workspacePathFor)(project.slug);
|
|
@@ -101,7 +128,7 @@ async function reseedStaleWorkspaces(projects, currentVersion, io) {
|
|
|
101
128
|
continue;
|
|
102
129
|
}
|
|
103
130
|
const recorded = readWorkspaceFrameworkVersion(project.slug);
|
|
104
|
-
if (recorded === currentVersion) {
|
|
131
|
+
if (recorded === currentVersion && !fs.existsSync((0, core_update_state_1.coreUpdatePendingPath)(workspace))) {
|
|
105
132
|
results.push({ projectId: project.id, reseeded: false, skippedReason: 'up-to-date' });
|
|
106
133
|
continue;
|
|
107
134
|
}
|
|
@@ -118,18 +145,27 @@ async function reseedStaleWorkspaces(projects, currentVersion, io) {
|
|
|
118
145
|
}
|
|
119
146
|
catch { /* absent */ }
|
|
120
147
|
try {
|
|
148
|
+
if (pendingErrors.has(project.id))
|
|
149
|
+
throw new Error(pendingErrors.get(project.id));
|
|
121
150
|
await assemble(project, providers);
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
151
|
+
if (readWorkspaceFrameworkVersion(project.slug) !== currentVersion)
|
|
152
|
+
throw new Error('Core assembly did not publish the expected workspace version.');
|
|
153
|
+
// Published Core 5 installations before the execution contract remain
|
|
154
|
+
// valid. Require the helper only when the selected package declares it.
|
|
155
|
+
if (!io?.assemble) {
|
|
156
|
+
const runtime = (0, core_runtime_1.resolveCoreRuntime)();
|
|
157
|
+
if (runtime) {
|
|
158
|
+
const contractPath = path.join(runtime.root, 'integration-contract.json');
|
|
159
|
+
let required;
|
|
160
|
+
try {
|
|
161
|
+
required = JSON.parse(fs.readFileSync(contractPath, 'utf8')).execution?.runtime;
|
|
162
|
+
}
|
|
163
|
+
catch { /* legacy contract */ }
|
|
164
|
+
if (typeof required === 'string' && required && !fs.existsSync(path.join(workspace, required)))
|
|
165
|
+
throw new Error('Core assembly did not install its declared execution runtime.');
|
|
126
166
|
}
|
|
127
|
-
catch { /* deleted by assemble */ }
|
|
128
|
-
if (mcpAfter !== mcpBefore)
|
|
129
|
-
fs.writeFileSync(mcpPath, mcpBefore, 'utf-8');
|
|
130
167
|
}
|
|
131
168
|
results.push({ projectId: project.id, reseeded: true });
|
|
132
|
-
console.log(`[framework-reseed] ${project.slug}: ${recorded ?? '(none)'} → ${currentVersion}`);
|
|
133
169
|
}
|
|
134
170
|
catch (err) {
|
|
135
171
|
results.push({
|
|
@@ -139,6 +175,36 @@ async function reseedStaleWorkspaces(projects, currentVersion, io) {
|
|
|
139
175
|
});
|
|
140
176
|
console.warn(`[framework-reseed] ${project.slug} failed (non-fatal):`, err);
|
|
141
177
|
}
|
|
178
|
+
finally {
|
|
179
|
+
if (mcpBefore !== null) {
|
|
180
|
+
let mcpAfter = null;
|
|
181
|
+
try {
|
|
182
|
+
mcpAfter = fs.readFileSync(mcpPath, 'utf-8');
|
|
183
|
+
}
|
|
184
|
+
catch { /* deleted by assemble */ }
|
|
185
|
+
if (mcpAfter !== mcpBefore) {
|
|
186
|
+
try {
|
|
187
|
+
fs.writeFileSync(mcpPath, mcpBefore, 'utf-8');
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
const result = results[results.length - 1];
|
|
191
|
+
result.reseeded = false;
|
|
192
|
+
result.error = `Could not restore the project's MCP configuration: ${String(error)}`;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const result = results[results.length - 1];
|
|
197
|
+
if (result.reseeded) {
|
|
198
|
+
try {
|
|
199
|
+
fs.rmSync((0, core_update_state_1.coreUpdatePendingPath)(workspace), { force: true });
|
|
200
|
+
console.log(`[framework-reseed] ${project.slug}: ${recorded ?? '(none)'} → ${currentVersion}`);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
result.reseeded = false;
|
|
204
|
+
result.error = `Could not finalize the Core refresh: ${String(error)}`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
142
208
|
}
|
|
143
209
|
return results;
|
|
144
210
|
}
|
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.HeadroomManager = exports.HEADROOM_ACTIVATION_SOURCE = exports.HEADROOM_ACTIVATION_SCHEMA_VERSION = exports.HEADROOM_MANAGED_PYTHON_VERSION = exports.HEADROOM_RELAY_PATH = void 0;
|
|
6
|
+
exports.HeadroomManager = exports.HEADROOM_ACTIVATION_SOURCE = exports.HEADROOM_ACTIVATION_SCHEMA_VERSION = exports.HEADROOM_MANAGED_PYTHON_VERSION = exports.HEADROOM_RELAY_PATH = exports.PROXY_START_TIMEOUT_MS = void 0;
|
|
7
|
+
exports.getProxyStartTimeoutMs = getProxyStartTimeoutMs;
|
|
7
8
|
exports.getHeadroomManagedInstallPlan = getHeadroomManagedInstallPlan;
|
|
8
9
|
exports.parseWindowsOwnershipSnapshot = parseWindowsOwnershipSnapshot;
|
|
9
10
|
exports.windowsSnapshotHasOwnedListener = windowsSnapshotHasOwnedListener;
|
|
@@ -22,7 +23,27 @@ const win_spawn_1 = require("./util/win-spawn");
|
|
|
22
23
|
const STATE_KEY = 'plugins.headroom.state';
|
|
23
24
|
const DEFAULT_PORT = 8787;
|
|
24
25
|
const PROXY_HEALTH_REQUEST_TIMEOUT_MS = 750;
|
|
26
|
+
/**
|
|
27
|
+
* How long a freshly spawned proxy gets to answer `/livez` before activation
|
|
28
|
+
* is declared failed. The proxy is a Python tool run through uv: a COLD start
|
|
29
|
+
* (first run after install, or a wiped bytecode cache) compiles ~3 000
|
|
30
|
+
* modules and only prints its banner ~4 s in, answering `/livez` after ~7.5 s
|
|
31
|
+
* on an M-series Mac — the previous 6 s budget killed it every time and left
|
|
32
|
+
* `proxyTail` empty, so the diagnostics said nothing. Warm starts take ~3.7 s.
|
|
33
|
+
* A child that EXITS is failed immediately regardless of this budget.
|
|
34
|
+
*/
|
|
35
|
+
exports.PROXY_START_TIMEOUT_MS = 30_000;
|
|
36
|
+
const PROXY_START_TIMEOUT_ENV = 'SPECRAILS_HEADROOM_START_TIMEOUT_MS';
|
|
25
37
|
const RELAY_CONNECT_TIMEOUT_MS = 3_000;
|
|
38
|
+
/** Effective proxy startup budget: the env override when it is a positive
|
|
39
|
+
* integer number of milliseconds, else the default. */
|
|
40
|
+
function getProxyStartTimeoutMs(env = process.env) {
|
|
41
|
+
const raw = env[PROXY_START_TIMEOUT_ENV];
|
|
42
|
+
if (raw === undefined || raw.trim() === '')
|
|
43
|
+
return exports.PROXY_START_TIMEOUT_MS;
|
|
44
|
+
const parsed = Number(raw);
|
|
45
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : exports.PROXY_START_TIMEOUT_MS;
|
|
46
|
+
}
|
|
26
47
|
exports.HEADROOM_RELAY_PATH = '/_specrails/headroom';
|
|
27
48
|
exports.HEADROOM_MANAGED_PYTHON_VERSION = '3.12';
|
|
28
49
|
exports.HEADROOM_ACTIVATION_SCHEMA_VERSION = 1;
|
|
@@ -1437,6 +1458,9 @@ class HeadroomManager {
|
|
|
1437
1458
|
HEADROOM_LEARN: state.learning.enabled ? '1' : '0',
|
|
1438
1459
|
HEADROOM_SAVINGS_PROFILE: process.env.HEADROOM_SAVINGS_PROFILE ?? 'agent-90',
|
|
1439
1460
|
HEADROOM_MODE: process.env.HEADROOM_MODE ?? 'token',
|
|
1461
|
+
// stdout is a pipe here, so Python block-buffers it: without this the
|
|
1462
|
+
// banner (and any traceback) never reaches `proxyTail` before a kill.
|
|
1463
|
+
PYTHONUNBUFFERED: '1',
|
|
1440
1464
|
},
|
|
1441
1465
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1442
1466
|
});
|
|
@@ -1472,13 +1496,24 @@ class HeadroomManager {
|
|
|
1472
1496
|
// fallback for unusual ChildProcess implementations and stdio teardown.
|
|
1473
1497
|
child.on('exit', invalidateOwnedProxy);
|
|
1474
1498
|
child.on('close', invalidateOwnedProxy);
|
|
1475
|
-
const
|
|
1499
|
+
const startTimeoutMs = getProxyStartTimeoutMs();
|
|
1500
|
+
const childAlive = () => child.exitCode == null && child.signalCode == null;
|
|
1501
|
+
const healthy = await this.waitForProxyHealthy(state.port, startTimeoutMs,
|
|
1502
|
+
// A dead child can never become healthy — stop waiting the moment it
|
|
1503
|
+
// exits so a crash surfaces at once instead of after the whole budget.
|
|
1504
|
+
() => this.isProxyGenerationCurrent(generation) && childAlive());
|
|
1476
1505
|
if (!this.isProxyGenerationCurrent(generation)) {
|
|
1477
1506
|
await this.discardProxyChild(child);
|
|
1478
1507
|
return this.cancelledProxyStart();
|
|
1479
1508
|
}
|
|
1480
1509
|
if (!healthy) {
|
|
1481
|
-
|
|
1510
|
+
// Prefer the process's own words; otherwise say precisely which of the two
|
|
1511
|
+
// things happened so "did not become healthy" is never an empty box.
|
|
1512
|
+
const tail = this.proxyTail.trim();
|
|
1513
|
+
const detail = tail || (childAlive()
|
|
1514
|
+
? `The Headroom proxy did not answer /livez within ${Math.round(startTimeoutMs / 1000)} s.`
|
|
1515
|
+
: `The Headroom proxy exited during startup (${child.signalCode ?? `exit code ${child.exitCode}`}).`);
|
|
1516
|
+
const failure = classifyProxyFailure(detail, command);
|
|
1482
1517
|
await this.discardProxyChild(child);
|
|
1483
1518
|
this.updatePersisted({ lastIssue: failure });
|
|
1484
1519
|
this.emit('failed', failure.title);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.consumeHostControlToken = consumeHostControlToken;
|
|
4
|
+
exports.createHostControlRouter = createHostControlRouter;
|
|
5
|
+
const express_1 = require("express");
|
|
6
|
+
const auth_1 = require("./auth");
|
|
7
|
+
const TOKEN_ENV = 'SPECRAILS_HOST_CONTROL_TOKEN';
|
|
8
|
+
/** Consume the host-only capability before provider/terminal environments are copied. */
|
|
9
|
+
function consumeHostControlToken(env = process.env) {
|
|
10
|
+
let token;
|
|
11
|
+
for (const key of Object.keys(env)) {
|
|
12
|
+
if (key.toUpperCase() !== TOKEN_ENV)
|
|
13
|
+
continue;
|
|
14
|
+
token ??= env[key];
|
|
15
|
+
delete env[key];
|
|
16
|
+
}
|
|
17
|
+
return token && /^[a-zA-Z0-9_-]{32,256}$/.test(token) ? token : undefined;
|
|
18
|
+
}
|
|
19
|
+
/** Private host control; deliberately independent of the renderer's desktop token. */
|
|
20
|
+
function createHostControlRouter(options) {
|
|
21
|
+
const router = (0, express_1.Router)();
|
|
22
|
+
let requested = false;
|
|
23
|
+
router.post('/shutdown', (req, res) => {
|
|
24
|
+
if (!options.token) {
|
|
25
|
+
res.status(404).json({ error: 'Not found' });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const supplied = req.headers['x-specrails-host-token'];
|
|
29
|
+
if (!(0, auth_1.isLoopbackAddress)(req.socket.remoteAddress) || req.headers.origin !== undefined
|
|
30
|
+
|| typeof supplied !== 'string' || !(0, auth_1.safeEqual)(supplied, options.token)) {
|
|
31
|
+
res.status(403).json({ error: 'Forbidden' });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (!requested) {
|
|
35
|
+
// The host must receive the owned PID before shutdown closes all sockets.
|
|
36
|
+
res.once('finish', () => {
|
|
37
|
+
if (requested)
|
|
38
|
+
return;
|
|
39
|
+
requested = true;
|
|
40
|
+
setImmediate(() => {
|
|
41
|
+
void Promise.resolve().then(options.onShutdown).catch(() => {
|
|
42
|
+
// No credentials or environment values in this diagnostic. The host
|
|
43
|
+
// retains its bounded, identity-checked force-stop fallback.
|
|
44
|
+
console.error('[host-control] graceful shutdown failed');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
res.status(202).json({ ok: true, pid: options.pid ?? process.pid });
|
|
50
|
+
});
|
|
51
|
+
return router;
|
|
52
|
+
}
|
package/server/dist/index.js
CHANGED
|
@@ -44,12 +44,16 @@ const framework_manager_1 = require("./framework-manager");
|
|
|
44
44
|
const artifact_registry_1 = require("./artifact-registry");
|
|
45
45
|
const path_resolver_1 = require("./path-resolver");
|
|
46
46
|
const dev_ports_1 = require("./dev-ports");
|
|
47
|
+
const api_not_found_1 = require("./api-not-found");
|
|
47
48
|
const process_admission_1 = require("./process-admission");
|
|
49
|
+
const host_control_1 = require("./host-control");
|
|
50
|
+
const transient_children_1 = require("./transient-children");
|
|
48
51
|
// Side-effect import: registers every bundled ProviderAdapter (claude, codex,
|
|
49
52
|
// future providers) so `getAdapter`/`hasAdapter`/`listAdapters` are populated
|
|
50
53
|
// before any manager constructs a project context. See
|
|
51
54
|
// openspec/changes/add-multi-provider-support/specs/multi-provider-architecture/spec.md.
|
|
52
55
|
require("./providers");
|
|
56
|
+
const hostControlToken = (0, host_control_1.consumeHostControlToken)();
|
|
53
57
|
const inheritedPathBeforeResolve = (process.env.PATH ?? '').split(process.platform === 'win32' ? ';' : ':').filter(Boolean).length;
|
|
54
58
|
// Backfill SystemRoot/ComSpec/etc into process.env BEFORE anything spawns, so a
|
|
55
59
|
// stripped GUI-launch sidecar env never breaks cmd.exe-mediated spawns (PTY,
|
|
@@ -155,6 +159,7 @@ function corsMiddleware(req, res, next) {
|
|
|
155
159
|
next();
|
|
156
160
|
}
|
|
157
161
|
app.use(corsMiddleware);
|
|
162
|
+
app.use('/api/host', (0, host_control_1.createHostControlRouter)({ token: hostControlToken, onShutdown: shutdown }));
|
|
158
163
|
// Provider traffic uses the desktop listener as a stable relay. Mount before
|
|
159
164
|
// every body parser so large/streaming request bodies and Codex event streams
|
|
160
165
|
// pass through byte-for-byte; the relay verifies the connected Headroom backend
|
|
@@ -504,9 +509,17 @@ function applyPtyWsRateLimiting(ws) {
|
|
|
504
509
|
// ─── Super-mode bootstrap ─────────────────────────────────────────────────────
|
|
505
510
|
{
|
|
506
511
|
const registry = new project_registry_1.ProjectRegistry(broadcast, undefined, port);
|
|
512
|
+
try {
|
|
513
|
+
(0, transient_children_1.initializeBackgroundProcessPersistence)(path_1.default.join(path_1.default.dirname(registry.desktopDb.name), 'background-processes.sqlite'));
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
// A log-store failure must not take mission chat or the project catalog
|
|
517
|
+
// offline. New background launches report the persistence error directly.
|
|
518
|
+
console.error('[background-process] persistent history unavailable:', error);
|
|
519
|
+
}
|
|
507
520
|
registry.loadAll();
|
|
508
521
|
_registry = registry;
|
|
509
|
-
_getProjectCount = () => registry.
|
|
522
|
+
_getProjectCount = () => registry.listProjects().length;
|
|
510
523
|
// ─── Bundled framework update channel (Phase 6) ─────────────────────────────
|
|
511
524
|
// After the registry + projects have loaded, run a single framework
|
|
512
525
|
// version-check. On FIRST run it materializes the bundled framework to
|
|
@@ -606,6 +619,15 @@ function applyPtyWsRateLimiting(ws) {
|
|
|
606
619
|
broadcast,
|
|
607
620
|
}));
|
|
608
621
|
if (mcpManager.isEnabledSetting()) {
|
|
622
|
+
// MCP is on by default, so mint + persist the scoped token at boot: the
|
|
623
|
+
// stdio bridge reads ~/.specrails/mcp.token locally, and on a fresh install
|
|
624
|
+
// nothing else would have written it until Settings ▸ MCP was opened.
|
|
625
|
+
try {
|
|
626
|
+
(0, mcp_1.getMcpToken)();
|
|
627
|
+
}
|
|
628
|
+
catch (err) {
|
|
629
|
+
console.warn('[mcp] could not prepare the scoped token at boot:', err);
|
|
630
|
+
}
|
|
609
631
|
mcpManager.start().catch((err) => console.error('[mcp] boot start failed:', err));
|
|
610
632
|
}
|
|
611
633
|
// ─── App-global agent chat (drives the app via its own MCP) ────────────────
|
|
@@ -660,7 +682,7 @@ function applyPtyWsRateLimiting(ws) {
|
|
|
660
682
|
state.subscribedProjectId = frame.projectId;
|
|
661
683
|
});
|
|
662
684
|
// Send app state init
|
|
663
|
-
const projects = registry.
|
|
685
|
+
const projects = registry.listProjects();
|
|
664
686
|
ws.send(JSON.stringify({
|
|
665
687
|
type: 'desktop.projects',
|
|
666
688
|
projects,
|
|
@@ -680,6 +702,7 @@ app.use((err, _req, res, _next) => {
|
|
|
680
702
|
}
|
|
681
703
|
});
|
|
682
704
|
// ─── Serve built React client (production) ────────────────────────────────────
|
|
705
|
+
app.use(['/api', '/hooks'], api_not_found_1.apiNotFound);
|
|
683
706
|
const clientDist = path_1.default.resolve(__dirname, '../../client/dist');
|
|
684
707
|
if (fs_1.default.existsSync(clientDist)) {
|
|
685
708
|
app.use(express_1.default.static(clientDist));
|
|
@@ -699,6 +722,13 @@ server.on('error', (err) => {
|
|
|
699
722
|
server.listen(port, '127.0.0.1', () => {
|
|
700
723
|
console.log(`specrails web manager running on http://127.0.0.1:${port}`);
|
|
701
724
|
writePidFile();
|
|
725
|
+
// Milestone launch chains launch chunks over loopback REST, so a chain whose
|
|
726
|
+
// chunk settled while the server was down can only replay once we listen.
|
|
727
|
+
for (const ctx of _registry?.listContexts() ?? []) {
|
|
728
|
+
void ctx.milestoneChains?.recoverOnStartup().catch((err) => {
|
|
729
|
+
console.error(`[milestone-chain] startup recovery failed for ${ctx.project.id}:`, err);
|
|
730
|
+
});
|
|
731
|
+
}
|
|
702
732
|
// Sweep stale shell-integration shim directories left behind by previous runs.
|
|
703
733
|
try {
|
|
704
734
|
const removed = (0, terminal_shell_integration_1.cleanupStaleShimDirs)();
|
|
@@ -744,6 +774,17 @@ async function shutdown() {
|
|
|
744
774
|
Promise.resolve().then(() => _agentChatManager?.shutdown()),
|
|
745
775
|
Promise.resolve().then(() => _blueprintChatManager?.shutdown()),
|
|
746
776
|
]);
|
|
777
|
+
// The shell can exit before its application descendants. Keep the event
|
|
778
|
+
// loop alive for the owned-group escalation before server.close exits us.
|
|
779
|
+
const remainingBackground = await (0, transient_children_1.awaitBackgroundProcessesStopped)(6000);
|
|
780
|
+
if (remainingBackground.length)
|
|
781
|
+
console.error('[shutdown] background applications still alive after stop deadline:', remainingBackground.map(({ pid, processId, error }) => ({ pid, processId, error })));
|
|
782
|
+
try {
|
|
783
|
+
(0, transient_children_1.closeBackgroundProcessPersistence)();
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
console.error('[shutdown] background process history could not be flushed:', error);
|
|
787
|
+
}
|
|
747
788
|
try {
|
|
748
789
|
await _headroomManager?.shutdown();
|
|
749
790
|
}
|