specrails-desktop 2.36.0 → 2.42.2
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-C76VdBJC.js → DocsDialog-Dj6mt4ga.js} +2 -2
- package/client/dist/assets/{DocsPage-ziMlj6B6.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-B1-MUehg.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 +74 -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 +537 -68
- 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 +54 -15
- package/server/dist/agent-operator-prompt.js +255 -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 +109 -9
- package/server/dist/dev-ports.js +8 -3
- package/server/dist/file-provenance.js +34 -22
- 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/git-diagnostics.js +2 -2
- 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/pr-publisher.js +1 -1
- package/server/dist/profiles-router.js +3 -18
- package/server/dist/project-code-discovery.js +142 -0
- package/server/dist/project-git.js +19 -21
- 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 +52 -8
- 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 +21 -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-tAQcWs1R.js +0 -1
- package/client/dist/assets/AgentBrowserCapture-BX8BNae5.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BVAqCL0K.js +0 -2
- package/client/dist/assets/AgentModeCodePane-9XfqQc9c.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-BCjLsPhc.js +0 -1
- package/client/dist/assets/AgentsPage-C8J_42sm.js +0 -87
- package/client/dist/assets/AnalyticsPage-BtQuyfJm.js +0 -1
- package/client/dist/assets/CodePage-BZ2MwdCu.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-Bh_29V4g.js +0 -1
- package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-BCZiSfzc.js +0 -19
- package/client/dist/assets/JobDetailModal-NAJmuYIT.js +0 -1
- package/client/dist/assets/JobDetailPage-fE0ZycUX.js +0 -1
- package/client/dist/assets/JobsPage-z5M_HVJG.js +0 -1
- package/client/dist/assets/LoopBuilderPage-j1FQS47c.js +0 -7
- package/client/dist/assets/LoopPreviewModal-DhDJjdCe.js +0 -1
- package/client/dist/assets/LoopsPage-Ll7zORZb.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-Bd3OqR_S.js +0 -1
- package/client/dist/assets/PluginsPage-DN4kGvJt.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-OgsgxjzF.js +0 -1
- package/client/dist/assets/ReviewPacketPage-DFog4sXn.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-L2XxqiOD.js +0 -1
- package/client/dist/assets/TicketDetailModal-CsF-AWZX.js +0 -14
- package/client/dist/assets/Trans-Bsz9_gyq.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-DQCvMgUc.js +0 -1
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-C16cxSe1.js +0 -1
- package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
- package/client/dist/assets/i18n-hnX2DPmo.js +0 -2
- package/client/dist/assets/index-DIhMEAVw.js +0 -66
- package/client/dist/assets/index-DwygcU7s.css +0 -2
- package/client/dist/assets/mcp-B8X8wBLu.js +0 -1
- package/client/dist/assets/mcp-C2AojZZN.js +0 -1
- package/client/dist/assets/mcp-D8BT78_L.js +0 -1
- package/client/dist/assets/mcp-Df5RWBNk.js +0 -1
- package/client/dist/assets/mcp-Dj7BgMQW.js +0 -1
- package/client/dist/assets/mcp-Q7iT7rcz.js +0 -1
- package/client/dist/assets/mcp-YlvNvJbW.js +0 -1
- package/client/dist/assets/mcp-mKFpTSRb.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-EC9xqio-.js +0 -1
- package/client/dist/assets/useDesktop-BQVt8lFd.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
|
}
|
|
@@ -17,7 +17,7 @@ const win_spawn_1 = require("./util/win-spawn");
|
|
|
17
17
|
* SAFETY: the command list is a FIXED ALLOWLIST — the caller only picks an
|
|
18
18
|
* action name, never arguments, so no flag/ref/path can be smuggled in. Every
|
|
19
19
|
* command is read-only (no push/commit/checkout/pr-create). git runs with the
|
|
20
|
-
* hardened `
|
|
20
|
+
* hardened `gitExecEnv()` (hostile-repo config stripped, prompts disabled); gh
|
|
21
21
|
* runs with the normal env so it finds the user's `~/.config/gh` auth and the
|
|
22
22
|
* bundled `gh` on PATH.
|
|
23
23
|
*/
|
|
@@ -43,7 +43,7 @@ const OUTPUT_CAP = 8_000;
|
|
|
43
43
|
const defaultExec = (cmd, args, cwd) => new Promise((resolve) => {
|
|
44
44
|
// git: hardened, cwd-pinned env. gh: user env (needs ~/.config/gh auth) with
|
|
45
45
|
// the Windows shell-critical vars guaranteed present.
|
|
46
|
-
const env = cmd === 'gh' ? (0, win_spawn_1.windowsSpawnEnv)() : file_provenance_1.
|
|
46
|
+
const env = cmd === 'gh' ? (0, win_spawn_1.windowsSpawnEnv)() : (0, file_provenance_1.gitExecEnv)();
|
|
47
47
|
(0, child_process_1.execFile)(cmd, args, { cwd, env, timeout: 15_000, maxBuffer: 8 * 1024 * 1024, windowsHide: true }, (err, stdout, stderr) => {
|
|
48
48
|
const code = err && typeof err.code === 'number'
|
|
49
49
|
? err.code
|
|
@@ -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
|
+
}
|