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
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RepositoryValidationError = void 0;
|
|
7
|
+
exports.repositoryPathKey = repositoryPathKey;
|
|
8
|
+
exports.canonicalRepositoryPath = canonicalRepositoryPath;
|
|
9
|
+
exports.repositoryAvailable = repositoryAvailable;
|
|
10
|
+
exports.legacyRepositoryKind = legacyRepositoryKind;
|
|
11
|
+
exports.getProjectRepositories = getProjectRepositories;
|
|
12
|
+
exports.resolveProjectRepository = resolveProjectRepository;
|
|
13
|
+
exports.validateTicketRepositoryIds = validateTicketRepositoryIds;
|
|
14
|
+
exports.inspectRepositoryPath = inspectRepositoryPath;
|
|
15
|
+
exports.assertDistinctRepositories = assertDistinctRepositories;
|
|
16
|
+
exports.isRepositoryPathWithin = isRepositoryPathWithin;
|
|
17
|
+
exports.resolveRepositoryProject = resolveRepositoryProject;
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const child_process_1 = require("child_process");
|
|
21
|
+
class RepositoryValidationError extends Error {
|
|
22
|
+
code;
|
|
23
|
+
status;
|
|
24
|
+
details;
|
|
25
|
+
constructor(message, code = 'invalid_repository', status = 400, details) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.status = status;
|
|
29
|
+
this.details = details;
|
|
30
|
+
this.name = 'RepositoryValidationError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.RepositoryValidationError = RepositoryValidationError;
|
|
34
|
+
function repositoryPathKey(value) {
|
|
35
|
+
const absolute = path_1.default.resolve(value);
|
|
36
|
+
return process.platform === 'win32' || process.platform === 'darwin' ? absolute.toLowerCase() : absolute;
|
|
37
|
+
}
|
|
38
|
+
function canonicalRepositoryPath(value) {
|
|
39
|
+
const absolute = path_1.default.resolve(value);
|
|
40
|
+
let existing = absolute;
|
|
41
|
+
while (true) {
|
|
42
|
+
try {
|
|
43
|
+
return path_1.default.resolve(fs_1.default.realpathSync(existing), path_1.default.relative(existing, absolute));
|
|
44
|
+
}
|
|
45
|
+
catch { /* Resolve through the nearest existing parent (e.g. an unsaved src path). */ }
|
|
46
|
+
const parent = path_1.default.dirname(existing);
|
|
47
|
+
if (parent === existing)
|
|
48
|
+
return absolute;
|
|
49
|
+
existing = parent;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function repositoryAvailable(value) {
|
|
53
|
+
try {
|
|
54
|
+
return fs_1.default.statSync(value).isDirectory();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Cheap fallback for old fixtures and missing roots; persisted memberships carry the inspected kind. */
|
|
61
|
+
function legacyRepositoryKind(value) {
|
|
62
|
+
let dir = path_1.default.resolve(value);
|
|
63
|
+
while (true) {
|
|
64
|
+
if (fs_1.default.existsSync(path_1.default.join(dir, '.git')))
|
|
65
|
+
return 'git';
|
|
66
|
+
const parent = path_1.default.dirname(dir);
|
|
67
|
+
if (parent === dir)
|
|
68
|
+
return 'folder';
|
|
69
|
+
dir = parent;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function getProjectRepositories(project) {
|
|
73
|
+
if (project.repositories?.length)
|
|
74
|
+
return project.repositories;
|
|
75
|
+
return [{
|
|
76
|
+
id: `primary-${project.id}`, projectId: project.id,
|
|
77
|
+
name: project.name ?? path_1.default.basename(project.path), path: project.path, isPrimary: true,
|
|
78
|
+
kind: legacyRepositoryKind(project.path), integrationBranch: null,
|
|
79
|
+
addedAt: project.added_at ?? '', available: repositoryAvailable(project.path),
|
|
80
|
+
}];
|
|
81
|
+
}
|
|
82
|
+
function resolveProjectRepository(project, repositoryId) {
|
|
83
|
+
const repositories = getProjectRepositories(project);
|
|
84
|
+
const repository = repositoryId === undefined
|
|
85
|
+
? repositories.find((item) => item.isPrimary)
|
|
86
|
+
: repositories.find((item) => item.id === repositoryId && item.projectId === project.id);
|
|
87
|
+
if (!repository)
|
|
88
|
+
throw new RepositoryValidationError('Repository does not belong to this project', 'repository_not_found', 404);
|
|
89
|
+
return repository;
|
|
90
|
+
}
|
|
91
|
+
function validateTicketRepositoryIds(project, value) {
|
|
92
|
+
if (value === undefined)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((id) => typeof id !== 'string' || !id.trim())) {
|
|
95
|
+
throw new RepositoryValidationError('repositoryIds must be a nonempty array of repository IDs', 'invalid_repository_ids');
|
|
96
|
+
}
|
|
97
|
+
if (new Set(value).size !== value.length)
|
|
98
|
+
throw new RepositoryValidationError('repositoryIds must not contain duplicates', 'invalid_repository_ids');
|
|
99
|
+
for (const id of value) {
|
|
100
|
+
try {
|
|
101
|
+
resolveProjectRepository(project, id);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
throw new RepositoryValidationError(`Repository ${id} does not belong to this project`, 'invalid_repository_ids');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return [...value];
|
|
108
|
+
}
|
|
109
|
+
function inspectRepositoryPath(input, requireAvailable = true) {
|
|
110
|
+
if (!input || typeof input.path !== 'string' || !input.path.trim())
|
|
111
|
+
throw new RepositoryValidationError('Repository path is required');
|
|
112
|
+
if (input.name !== undefined && (typeof input.name !== 'string' || !input.name.trim()))
|
|
113
|
+
throw new RepositoryValidationError('Repository name must not be empty');
|
|
114
|
+
if (input.integrationBranch !== undefined && input.integrationBranch !== null && (typeof input.integrationBranch !== 'string' || !input.integrationBranch.trim() || /[\r\n\0]/.test(input.integrationBranch))) {
|
|
115
|
+
throw new RepositoryValidationError('integrationBranch must be a branch name or null');
|
|
116
|
+
}
|
|
117
|
+
const canonical = canonicalRepositoryPath(input.path);
|
|
118
|
+
if (requireAvailable && !repositoryAvailable(canonical))
|
|
119
|
+
throw new RepositoryValidationError(`Repository directory is unavailable: ${canonical}`, 'repository_unavailable');
|
|
120
|
+
let gitIdentity = null;
|
|
121
|
+
if (repositoryAvailable(canonical)) {
|
|
122
|
+
try {
|
|
123
|
+
const commonDir = (0, child_process_1.execFileSync)('git', ['-C', canonical, 'rev-parse', '--git-common-dir'], { encoding: 'utf8', timeout: 3000, stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
124
|
+
gitIdentity = repositoryPathKey(canonicalRepositoryPath(path_1.default.resolve(canonical, commonDir)));
|
|
125
|
+
}
|
|
126
|
+
catch { /* Non-Git context folders are supported. */ }
|
|
127
|
+
}
|
|
128
|
+
return { ...input, path: canonical, name: input.name?.trim(), integrationBranch: input.integrationBranch?.trim() || null, canonicalKey: repositoryPathKey(canonical), gitIdentity, kind: gitIdentity ? 'git' : 'folder' };
|
|
129
|
+
}
|
|
130
|
+
function assertDistinctRepositories(repositories) {
|
|
131
|
+
for (let i = 0; i < repositories.length; i++) {
|
|
132
|
+
for (let j = 0; j < i; j++) {
|
|
133
|
+
const a = repositories[i], b = repositories[j];
|
|
134
|
+
const overlapping = isRepositoryPathWithin(a.canonicalKey, b.canonicalKey) || isRepositoryPathWithin(b.canonicalKey, a.canonicalKey);
|
|
135
|
+
if (overlapping || (a.gitIdentity !== null && a.gitIdentity === b.gitIdentity)) {
|
|
136
|
+
throw new RepositoryValidationError('Repositories in one project must have distinct, non-overlapping roots and Git identities', 'duplicate_repository', 409);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function isRepositoryPathWithin(root, candidate) {
|
|
142
|
+
const relative = path_1.default.relative(repositoryPathKey(root), repositoryPathKey(candidate));
|
|
143
|
+
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path_1.default.sep}`) && !path_1.default.isAbsolute(relative));
|
|
144
|
+
}
|
|
145
|
+
/** Explicit project context wins; legacy primary matches precede shared secondary matches. */
|
|
146
|
+
function resolveRepositoryProject(projects, inputPath, projectId) {
|
|
147
|
+
const canonical = canonicalRepositoryPath(inputPath);
|
|
148
|
+
const candidates = projects.filter((project) => (!projectId || project.id === projectId) && getProjectRepositories(project).some((repo) => isRepositoryPathWithin(canonicalRepositoryPath(repo.path), canonical)));
|
|
149
|
+
if (projectId)
|
|
150
|
+
return candidates[0];
|
|
151
|
+
const primaryMatches = candidates.filter((project) => isRepositoryPathWithin(canonicalRepositoryPath(project.path), canonical));
|
|
152
|
+
const selected = primaryMatches.length ? primaryMatches : candidates;
|
|
153
|
+
if (selected.length > 1 && !primaryMatches.length) {
|
|
154
|
+
throw new RepositoryValidationError('This path belongs to several projects; provide projectId', 'ambiguous_project_path', 409, { projectIds: selected.map((project) => project.id) });
|
|
155
|
+
}
|
|
156
|
+
return selected.sort((a, b) => b.path.length - a.path.length)[0];
|
|
157
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migrateRepositoryProvenance = migrateRepositoryProvenance;
|
|
4
|
+
exports.provenanceRepositoryFilter = provenanceRepositoryFilter;
|
|
5
|
+
/** Project migration 60. Historical rows belong to the primary repository. */
|
|
6
|
+
function migrateRepositoryProvenance(db) {
|
|
7
|
+
const columns = db.prepare('PRAGMA table_info(file_provenance)').all();
|
|
8
|
+
if (!columns.some(column => column.name === 'repository_id')) {
|
|
9
|
+
db.exec('ALTER TABLE file_provenance ADD COLUMN repository_id TEXT');
|
|
10
|
+
}
|
|
11
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_file_provenance_repository_path
|
|
12
|
+
ON file_provenance(repository_id, file_path, at);
|
|
13
|
+
CREATE INDEX IF NOT EXISTS idx_file_provenance_repository_job
|
|
14
|
+
ON file_provenance(repository_id, job_id, file_path);`);
|
|
15
|
+
}
|
|
16
|
+
/** Unscoped internal callers retain the project-wide history view. REST/code
|
|
17
|
+
* callers always supply a resolved member; a missing column never exposes a
|
|
18
|
+
* primary's historical files as a secondary repository's history. */
|
|
19
|
+
function provenanceRepositoryFilter(db, scope, alias = '') {
|
|
20
|
+
if (!scope)
|
|
21
|
+
return { sql: '1 = 1', params: [] };
|
|
22
|
+
const columns = db.prepare('PRAGMA table_info(file_provenance)').all();
|
|
23
|
+
if (!columns.some(column => column.name === 'repository_id')) {
|
|
24
|
+
return { sql: scope.includeLegacy ? '1 = 1' : '0 = 1', params: [] };
|
|
25
|
+
}
|
|
26
|
+
const column = `${alias ? `${alias}.` : ''}repository_id`;
|
|
27
|
+
return {
|
|
28
|
+
sql: scope.includeLegacy ? `(${column} = ? OR ${column} IS NULL)` : `${column} = ?`,
|
|
29
|
+
params: [scope.repositoryId],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerBackgroundProcessRoutes = registerBackgroundProcessRoutes;
|
|
4
|
+
const transient_children_1 = require("./transient-children");
|
|
5
|
+
const project_repositories_1 = require("./project-repositories");
|
|
6
|
+
const background_process_service_1 = require("./background-process-service");
|
|
7
|
+
function processQuery(req, res) {
|
|
8
|
+
const pid = Number(req.params.pid);
|
|
9
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) {
|
|
10
|
+
res.status(400).json({ error: 'pid must be a positive integer' });
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const chatId = typeof req.query.chatId === 'string' ? req.query.chatId.trim() : '';
|
|
14
|
+
if (!chatId) {
|
|
15
|
+
res.status(400).json({ error: 'chatId is required' });
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const processId = req.query.processId;
|
|
19
|
+
if (processId !== undefined && (typeof processId !== 'string' || !processId.trim() || processId.length > 200)) {
|
|
20
|
+
res.status(400).json({ error: 'processId must be a nonempty execution identity' });
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return { pid, chatId, ...(typeof processId === 'string' ? { processId } : {}) };
|
|
24
|
+
}
|
|
25
|
+
function registerBackgroundProcessRoutes({ router, ctx }) {
|
|
26
|
+
router.get('/:projectId/background-processes', (req, res) => {
|
|
27
|
+
const c = ctx(req);
|
|
28
|
+
const chatId = typeof req.query.chatId === 'string' && req.query.chatId.trim() ? req.query.chatId.trim() : undefined;
|
|
29
|
+
res.json({ processes: (0, transient_children_1.listBackgroundProcesses)({ projectId: c.project.id,
|
|
30
|
+
...(chatId ? { chatId } : {}), ...(req.query.includeFinished === 'true' ? { includeFinished: true } : {}) }) });
|
|
31
|
+
});
|
|
32
|
+
router.get('/:projectId/background-processes/:pid/logs', (req, res) => {
|
|
33
|
+
const c = ctx(req);
|
|
34
|
+
const query = processQuery(req, res);
|
|
35
|
+
if (!query)
|
|
36
|
+
return;
|
|
37
|
+
const { pid, ...owner } = query;
|
|
38
|
+
const limit = req.query.limit === undefined ? undefined : Number(req.query.limit);
|
|
39
|
+
if (limit !== undefined && (!Number.isSafeInteger(limit) || limit <= 0)) {
|
|
40
|
+
res.status(400).json({ error: 'limit must be a positive integer' });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const logs = (0, transient_children_1.getBackgroundProcessLogs)(pid, { projectId: c.project.id, ...owner, ...(limit !== undefined ? { limit } : {}) });
|
|
44
|
+
if (!logs) {
|
|
45
|
+
res.status(404).json({ error: 'background process logs not found for project/chat or execution has expired' });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
res.json(logs);
|
|
49
|
+
});
|
|
50
|
+
router.post('/:projectId/background-processes', (req, res) => {
|
|
51
|
+
const { command, cwd, chatId, repositoryId, confirmed, allowWhileBusy } = req.body ?? {};
|
|
52
|
+
const c = ctx(req);
|
|
53
|
+
if (typeof command !== 'string' || !command.trim()) {
|
|
54
|
+
res.status(400).json({ error: 'command is required' });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (typeof chatId !== 'string' || !chatId.trim()) {
|
|
58
|
+
res.status(400).json({ error: 'chatId is required' });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (confirmed !== true) {
|
|
62
|
+
res.status(400).json({ error: 'confirmed must be true after explicit user confirmation' });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const busyReason = (0, background_process_service_1.backgroundStartBusyReason)(c);
|
|
67
|
+
if (busyReason && allowWhileBusy !== true) {
|
|
68
|
+
res.status(409).json({ error: 'project is busy', reason: busyReason, hint: 'Wait for the running job/loop to finish, or pass allowWhileBusy:true only after explicit user confirmation.' });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (repositoryId !== undefined && (typeof repositoryId !== 'string' || !repositoryId.trim()))
|
|
72
|
+
throw new project_repositories_1.RepositoryValidationError('repositoryId must be a nonempty string');
|
|
73
|
+
if ((0, project_repositories_1.getProjectRepositories)(c.project).length > 1 && repositoryId === undefined)
|
|
74
|
+
throw new project_repositories_1.RepositoryValidationError('repositoryId is required for multi-repository projects', 'repository_required');
|
|
75
|
+
const repository = (0, project_repositories_1.resolveProjectRepository)(c.project, repositoryId);
|
|
76
|
+
const process = (0, transient_children_1.startBackgroundProcess)(command.trim(), (0, background_process_service_1.resolveBackgroundCwd)(repository.path, cwd), chatId.trim(), c.project.id, (0, background_process_service_1.backgroundProcessHooks)(c.broadcast), { repositoryId: repository.id, repositoryName: repository.name });
|
|
77
|
+
res.status(202).json({ ok: true, repositoryId: repository.id, process });
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
res.status(err instanceof project_repositories_1.RepositoryValidationError ? err.status : 500).json({ error: err instanceof Error ? err.message : 'background process failed to start' });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
router.delete('/:projectId/background-processes/:pid', (req, res) => {
|
|
84
|
+
const c = ctx(req);
|
|
85
|
+
const query = processQuery(req, res);
|
|
86
|
+
if (!query)
|
|
87
|
+
return;
|
|
88
|
+
const { pid, ...owner } = query;
|
|
89
|
+
try {
|
|
90
|
+
const process = (0, background_process_service_1.stopOwnedBackgroundProcess)(pid, { projectId: c.project.id, ...owner });
|
|
91
|
+
if (!process) {
|
|
92
|
+
res.status(404).json({ error: 'background process not found for project/chat or execution identity' });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (process.error && !['stopping', 'exited', 'killed', 'failed'].includes(process.status)) {
|
|
96
|
+
res.status(500).json({ error: process.error, process });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
res.status(process.status === 'stopping' ? 202 : 200).json({ ok: true, pid, status: process.status, process });
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
res.status(err instanceof background_process_service_1.BackgroundProcessOwnershipLostError ? err.status : 500).json({ error: err instanceof Error ? err.message : 'background process could not be stopped' });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.registerGitRoutes = registerGitRoutes;
|
|
4
7
|
const project_git_1 = require("./project-git");
|
|
@@ -6,31 +9,66 @@ const git_diagnostics_1 = require("./git-diagnostics");
|
|
|
6
9
|
const pr_publisher_1 = require("./pr-publisher");
|
|
7
10
|
const repo_lock_1 = require("./repo-lock");
|
|
8
11
|
const process_admission_1 = require("./process-admission");
|
|
12
|
+
const project_repositories_1 = require("./project-repositories");
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
14
|
// ─── Git domain routes (/api/projects/:projectId/git) ─────────────────────────
|
|
10
15
|
//
|
|
11
16
|
// Backs the Agent-Mode git bar: current branch + last commit + local branches,
|
|
12
|
-
// and a user-initiated branch switch. Git
|
|
13
|
-
//
|
|
17
|
+
// and a user-initiated branch switch. Git runs against the selected member's
|
|
18
|
+
// checkout; legacy routes default to the primary, never the artifact workspace.
|
|
14
19
|
function registerGitRoutes(deps) {
|
|
15
20
|
const { router, ctx } = deps;
|
|
16
21
|
const exec = deps.exec ?? pr_publisher_1.defaultExec;
|
|
17
|
-
|
|
22
|
+
function selectedRepository(req, res, repositoryId = req.params.repositoryId) {
|
|
23
|
+
let repository;
|
|
18
24
|
try {
|
|
19
|
-
|
|
25
|
+
repository = (0, project_repositories_1.resolveProjectRepository)(ctx(req).project, repositoryId);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
res.status(404).json({ error: 'repository_not_found', detail: err.message });
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
if (!node_fs_1.default.statSync(repository.path).isDirectory())
|
|
33
|
+
throw new Error('not a directory');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
res.status(503).json({ error: 'repository_unavailable', repositoryId: repository.id });
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return repository;
|
|
40
|
+
}
|
|
41
|
+
function requireGit(req, res, repositoryId = req.params.repositoryId) {
|
|
42
|
+
const repository = selectedRepository(req, res, repositoryId);
|
|
43
|
+
if (repository?.kind === 'folder') {
|
|
44
|
+
res.status(409).json({ error: 'repository_is_not_git', repositoryId: repository.id });
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return repository;
|
|
48
|
+
}
|
|
49
|
+
router.get(['/:projectId/repositories/:repositoryId/git', '/:projectId/git'], async (req, res) => {
|
|
50
|
+
const repository = selectedRepository(req, res);
|
|
51
|
+
if (!repository)
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
res.json({ ...(repository.kind === 'folder' ? { git: false } : await (0, project_git_1.getProjectGitInfo)(repository.path)), repositoryId: repository.id });
|
|
20
55
|
}
|
|
21
56
|
catch (err) {
|
|
22
57
|
console.error('[project-git] info failed:', err);
|
|
23
58
|
res.status(500).json({ error: 'Failed to read git info' });
|
|
24
59
|
}
|
|
25
60
|
});
|
|
26
|
-
router.get('/:projectId/git/pull-requests/:number', async (req, res) => {
|
|
61
|
+
router.get(['/:projectId/repositories/:repositoryId/git/pull-requests/:number', '/:projectId/git/pull-requests/:number'], async (req, res) => {
|
|
62
|
+
const repository = requireGit(req, res);
|
|
63
|
+
if (!repository)
|
|
64
|
+
return;
|
|
27
65
|
const prNumber = Number.parseInt(String(req.params.number ?? ''), 10);
|
|
28
66
|
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
29
67
|
res.status(400).json({ error: 'invalid_pull_request_number' });
|
|
30
68
|
return;
|
|
31
69
|
}
|
|
32
70
|
try {
|
|
33
|
-
const r = await exec.run('gh', ['pr', 'view', String(prNumber), '--json', 'url'],
|
|
71
|
+
const r = await exec.run('gh', ['pr', 'view', String(prNumber), '--json', 'url'], repository.path);
|
|
34
72
|
if (r.code !== 0) {
|
|
35
73
|
res.status(404).json({ error: 'pull_request_not_found' });
|
|
36
74
|
return;
|
|
@@ -40,7 +78,7 @@ function registerGitRoutes(deps) {
|
|
|
40
78
|
res.status(404).json({ error: 'pull_request_not_found' });
|
|
41
79
|
return;
|
|
42
80
|
}
|
|
43
|
-
res.json({ prNumber, url: parsed.url });
|
|
81
|
+
res.json({ prNumber, url: parsed.url, repositoryId: repository.id });
|
|
44
82
|
}
|
|
45
83
|
catch (err) {
|
|
46
84
|
console.error('[project-git] pull request lookup failed:', err);
|
|
@@ -49,35 +87,56 @@ function registerGitRoutes(deps) {
|
|
|
49
87
|
});
|
|
50
88
|
// READ-ONLY git/gh diagnostics (backs the specrails_git MCP tool). A fixed
|
|
51
89
|
// allowlist of read-only commands — the query only names an action, never
|
|
52
|
-
// arguments.
|
|
53
|
-
|
|
54
|
-
router.get('/:projectId/git/diagnostic', async (req, res) => {
|
|
90
|
+
// arguments. Validate that allowlist before resolving filesystem state.
|
|
91
|
+
router.get(['/:projectId/repositories/:repositoryId/git/diagnostic', '/:projectId/git/diagnostic'], async (req, res) => {
|
|
55
92
|
const action = String(req.query.action ?? '');
|
|
56
93
|
if (!(0, git_diagnostics_1.isGitDiagnosticAction)(action)) {
|
|
57
94
|
res.status(400).json({ error: 'invalid_action', allowed: git_diagnostics_1.GIT_DIAGNOSTIC_ACTIONS });
|
|
58
95
|
return;
|
|
59
96
|
}
|
|
97
|
+
// The legacy diagnostic reports Git's own informative nonzero result for a
|
|
98
|
+
// primary folder without Git. Explicit context-only members reject Git.
|
|
99
|
+
const repository = req.params.repositoryId === undefined ? selectedRepository(req, res) : requireGit(req, res);
|
|
100
|
+
if (!repository)
|
|
101
|
+
return;
|
|
60
102
|
try {
|
|
61
|
-
res.json(await (0, git_diagnostics_1.runGitDiagnostic)(action,
|
|
103
|
+
res.json({ ...await (0, git_diagnostics_1.runGitDiagnostic)(action, repository.path), repositoryId: repository.id });
|
|
62
104
|
}
|
|
63
105
|
catch (err) {
|
|
64
106
|
console.error('[project-git] diagnostic failed:', err);
|
|
65
107
|
res.status(500).json({ error: 'diagnostic_failed', detail: err instanceof Error ? err.message : String(err) });
|
|
66
108
|
}
|
|
67
109
|
});
|
|
68
|
-
router.post('/:projectId/git/checkout', async (req, res) => {
|
|
110
|
+
router.post(['/:projectId/repositories/:repositoryId/git/checkout', '/:projectId/git/checkout'], async (req, res) => {
|
|
69
111
|
const body = (req.body ?? {});
|
|
112
|
+
const routeRepositoryId = req.params.repositoryId;
|
|
113
|
+
if (body.repositoryId !== undefined && (typeof body.repositoryId !== 'string' || !body.repositoryId || (routeRepositoryId && body.repositoryId !== routeRepositoryId))) {
|
|
114
|
+
res.status(400).json({ error: 'invalid_repository_id' });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const repositoryId = routeRepositoryId ?? body.repositoryId;
|
|
118
|
+
const project = ctx(req).project;
|
|
119
|
+
if (repositoryId === undefined && (0, project_repositories_1.getProjectRepositories)(project).length > 1) {
|
|
120
|
+
res.status(400).json({ error: 'repository_required', repositories: (0, project_repositories_1.getProjectRepositories)(project).map(({ id, name }) => ({ id, name })) });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const repository = requireGit(req, res, repositoryId);
|
|
124
|
+
if (!repository)
|
|
125
|
+
return;
|
|
70
126
|
const branch = typeof body.branch === 'string' ? body.branch.trim() : '';
|
|
71
127
|
if (!branch) {
|
|
72
128
|
res.status(400).json({ error: 'branch is required' });
|
|
73
129
|
return;
|
|
74
130
|
}
|
|
75
|
-
const
|
|
76
|
-
const repoDir = project.path;
|
|
131
|
+
const repoDir = repository.path;
|
|
77
132
|
try {
|
|
78
133
|
const admission = (0, process_admission_1.captureProcessAdmission)(project.id);
|
|
79
|
-
const result = await (0, repo_lock_1.withRepoLock)(repoDir, () => {
|
|
134
|
+
const result = await (0, repo_lock_1.withRepoLock)(repoDir, async () => {
|
|
80
135
|
admission.assertCurrent();
|
|
136
|
+
const current = (0, project_repositories_1.getProjectRepositories)(ctx(req).project).find(member => member.id === repository.id);
|
|
137
|
+
if (!current || current.path !== repoDir || current.kind !== 'git') {
|
|
138
|
+
return { ok: false, error: 'Repository membership changed; refresh before switching branches.' };
|
|
139
|
+
}
|
|
81
140
|
return (0, project_git_1.checkoutProjectBranch)(repoDir, branch);
|
|
82
141
|
});
|
|
83
142
|
if (!result.ok) {
|
|
@@ -85,7 +144,7 @@ function registerGitRoutes(deps) {
|
|
|
85
144
|
res.status(409).json({ error: result.error });
|
|
86
145
|
return;
|
|
87
146
|
}
|
|
88
|
-
res.json(await (0, project_git_1.getProjectGitInfo)(repoDir));
|
|
147
|
+
res.json({ ...await (0, project_git_1.getProjectGitInfo)(repoDir), repositoryId: repository.id });
|
|
89
148
|
}
|
|
90
149
|
catch (err) {
|
|
91
150
|
if (err instanceof process_admission_1.ProcessAdmissionClosedError) {
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.registerJobsRoutes = registerJobsRoutes;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
4
|
const ids_1 = require("./ids");
|
|
9
5
|
const db_1 = require("./db");
|
|
10
6
|
const queue_manager_1 = require("./queue-manager");
|
|
@@ -21,22 +17,8 @@ const loop_runs_store_1 = require("./loop-runs-store");
|
|
|
21
17
|
const metrics_1 = require("./metrics");
|
|
22
18
|
const job_listing_1 = require("./job-listing");
|
|
23
19
|
const ticket_store_1 = require("./ticket-store");
|
|
24
|
-
const
|
|
20
|
+
const project_router_background_processes_1 = require("./project-router-background-processes");
|
|
25
21
|
const project_router_helpers_1 = require("./project-router-helpers");
|
|
26
|
-
function backgroundStartBusyReason(c) {
|
|
27
|
-
const activeJobId = typeof c.queueManager?.getActiveJobId === 'function' ? c.queueManager.getActiveJobId() : null;
|
|
28
|
-
if (activeJobId)
|
|
29
|
-
return `job ${activeJobId} is still running`;
|
|
30
|
-
try {
|
|
31
|
-
const activeLoops = (0, loop_runs_store_1.listActiveLoopRuns)(c.db, c.project.id);
|
|
32
|
-
if (activeLoops.length > 0)
|
|
33
|
-
return `loop run ${activeLoops[0].id} is still running`;
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// If the loop table is unavailable in a test harness, do not block startup.
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
22
|
function registerJobsRoutes(deps) {
|
|
41
23
|
const { router, registry, ctx, ticketPath } = deps;
|
|
42
24
|
// ─── Queue / Spawn routes ────────────────────────────────────────────────────
|
|
@@ -218,113 +200,7 @@ function registerJobsRoutes(deps) {
|
|
|
218
200
|
},
|
|
219
201
|
});
|
|
220
202
|
});
|
|
221
|
-
|
|
222
|
-
const c = ctx(req);
|
|
223
|
-
const chatId = typeof req.query.chatId === 'string' && req.query.chatId.trim() ? req.query.chatId.trim() : undefined;
|
|
224
|
-
res.json({
|
|
225
|
-
processes: (0, transient_children_1.listBackgroundProcesses)({
|
|
226
|
-
projectId: c.project.id,
|
|
227
|
-
...(chatId ? { chatId } : {}),
|
|
228
|
-
}),
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
router.get('/:projectId/background-processes/:pid/logs', (req, res) => {
|
|
232
|
-
const c = ctx(req);
|
|
233
|
-
const pid = Number(req.params.pid);
|
|
234
|
-
const chatId = typeof req.query.chatId === 'string' ? req.query.chatId : '';
|
|
235
|
-
const limit = typeof req.query.limit === 'string' ? Number(req.query.limit) : undefined;
|
|
236
|
-
if (!Number.isFinite(pid)) {
|
|
237
|
-
res.status(400).json({ error: 'pid must be numeric' });
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
if (!chatId.trim()) {
|
|
241
|
-
res.status(400).json({ error: 'chatId is required' });
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (limit !== undefined && (!Number.isFinite(limit) || limit <= 0)) {
|
|
245
|
-
res.status(400).json({ error: 'limit must be a positive number' });
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const logs = (0, transient_children_1.getBackgroundProcessLogs)(pid, {
|
|
249
|
-
projectId: c.project.id,
|
|
250
|
-
chatId: chatId.trim(),
|
|
251
|
-
...(limit !== undefined ? { limit } : {}),
|
|
252
|
-
});
|
|
253
|
-
if (!logs) {
|
|
254
|
-
res.status(404).json({ error: 'background process logs not found for project/chat' });
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
res.json(logs);
|
|
258
|
-
});
|
|
259
|
-
router.post('/:projectId/background-processes', (req, res) => {
|
|
260
|
-
const { command, cwd, chatId, confirmed, allowWhileBusy } = req.body ?? {};
|
|
261
|
-
const c = ctx(req);
|
|
262
|
-
if (!command || typeof command !== 'string' || !command.trim()) {
|
|
263
|
-
res.status(400).json({ error: 'command is required' });
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (!chatId || typeof chatId !== 'string' || !chatId.trim()) {
|
|
267
|
-
res.status(400).json({ error: 'chatId is required' });
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (confirmed !== true) {
|
|
271
|
-
res.status(400).json({ error: 'confirmed must be true after explicit user confirmation' });
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const busyReason = backgroundStartBusyReason(c);
|
|
275
|
-
if (busyReason && allowWhileBusy !== true) {
|
|
276
|
-
res.status(409).json({
|
|
277
|
-
error: 'project is busy',
|
|
278
|
-
reason: busyReason,
|
|
279
|
-
hint: 'Wait for the running job/loop to finish, or pass allowWhileBusy:true only after explicit user confirmation.',
|
|
280
|
-
});
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const resolvedCwd = path_1.default.resolve(c.project.path, typeof cwd === 'string' && cwd.trim() ? cwd : '.');
|
|
284
|
-
const root = path_1.default.resolve(c.project.path);
|
|
285
|
-
if (resolvedCwd !== root && !resolvedCwd.startsWith(root + path_1.default.sep)) {
|
|
286
|
-
res.status(400).json({ error: 'cwd must stay within the project' });
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
try {
|
|
290
|
-
const process = (0, transient_children_1.startBackgroundProcess)(command.trim(), resolvedCwd, chatId.trim(), c.project.id, {
|
|
291
|
-
onStarted: (process) => c.broadcast({
|
|
292
|
-
type: 'background_process.started',
|
|
293
|
-
process,
|
|
294
|
-
projectId: process.projectId,
|
|
295
|
-
timestamp: new Date().toISOString(),
|
|
296
|
-
}),
|
|
297
|
-
onExited: (process) => c.broadcast({
|
|
298
|
-
type: 'background_process.exited',
|
|
299
|
-
process,
|
|
300
|
-
projectId: process.projectId,
|
|
301
|
-
timestamp: new Date().toISOString(),
|
|
302
|
-
}),
|
|
303
|
-
});
|
|
304
|
-
res.status(202).json({ ok: true, process });
|
|
305
|
-
}
|
|
306
|
-
catch (err) {
|
|
307
|
-
res.status(500).json({ error: err instanceof Error ? err.message : 'background process failed to start' });
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
router.delete('/:projectId/background-processes/:pid', (req, res) => {
|
|
311
|
-
const c = ctx(req);
|
|
312
|
-
const pid = Number(req.params.pid);
|
|
313
|
-
const chatId = typeof req.query.chatId === 'string' ? req.query.chatId : '';
|
|
314
|
-
if (!Number.isFinite(pid)) {
|
|
315
|
-
res.status(400).json({ error: 'pid must be numeric' });
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
if (!chatId.trim()) {
|
|
319
|
-
res.status(400).json({ error: 'chatId is required' });
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
if (!(0, transient_children_1.killOwnedBackgroundProcess)(pid, { projectId: c.project.id, chatId: chatId.trim() })) {
|
|
323
|
-
res.status(404).json({ error: 'background process not found for project/chat' });
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
res.json({ ok: true, pid, status: 'killing' });
|
|
327
|
-
});
|
|
203
|
+
(0, project_router_background_processes_1.registerBackgroundProcessRoutes)(deps);
|
|
328
204
|
// Returns the resolved default model for Add Spec + the full provider
|
|
329
205
|
// allow-list so the modal can render its picker without maintaining its
|
|
330
206
|
// own copy of the model lists. Source of truth is `server/spec-models.ts`.
|