specrails-desktop 2.26.2 → 2.28.0
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/client/dist/assets/{ActivityFeedPage-bDe4VitF.js → ActivityFeedPage-DcMmDAP3.js} +1 -1
- package/client/dist/assets/AgentBrowserCapture-CZ3oc1UL.js +1 -0
- package/client/dist/assets/AgentIntegrationsModal-DkqooCFn.js +1 -0
- package/client/dist/assets/{AgentModeCodePane-Ci39DcOo.js → AgentModeCodePane-CM77ljte.js} +2 -2
- package/client/dist/assets/AgentModeJobsPane-DbZzogbi.js +1 -0
- package/client/dist/assets/AgentsPage-BZIml7eL.js +86 -0
- package/client/dist/assets/{AnalyticsPage-Da21wWWw.js → AnalyticsPage-B4qflNxi.js} +1 -1
- package/client/dist/assets/{BarChart-BiLTq3uC.js → BarChart-Ckstq9fl.js} +1 -1
- package/client/dist/assets/CodePage-DFZXLbI1.js +2 -0
- package/client/dist/assets/{DesktopAnalyticsPage-COkzgMmd.js → DesktopAnalyticsPage-DbgMa8OO.js} +1 -1
- package/client/dist/assets/DocsDialog-BQc9Kwev.js +11 -0
- package/client/dist/assets/DocsPage-OsV44ECy.js +11 -0
- package/client/dist/assets/{ExportDropdown-DDhJH-Jr.js → ExportDropdown-a43XHO3Y.js} +1 -1
- package/client/dist/assets/IntegrationsPage-C3E8hZhB.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-zj8SrOAl.js +19 -0
- package/client/dist/assets/JobDetailModal-Cm6ZeVoh.js +1 -0
- package/client/dist/assets/JobDetailPage-DN67LsdX.js +1 -0
- package/client/dist/assets/JobsPage-DF9wKGbw.js +1 -0
- package/client/dist/assets/{LoopBuilderPage-DsLLrtHa.js → LoopBuilderPage-RJ6GGjli.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-C-fDEgnu.js → LoopPreviewModal-B2wmifF6.js} +1 -1
- package/client/dist/assets/LoopsPage-DdlvH-dJ.js +1 -0
- package/client/dist/assets/PluginsPage-9DI0a6jI.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-dgy5hqPd.js +1 -0
- package/client/dist/assets/{TemplatePreviewModal-DcHFbLA8.js → TemplatePreviewModal-CERf1IYB.js} +1 -1
- package/client/dist/assets/agent-BRJCESkc2.js +1 -0
- package/client/dist/assets/agent-BuYz6fJd.js +1 -0
- package/client/dist/assets/agent-CxnRzL6Z.js +1 -0
- package/client/dist/assets/agent-D4JAuEGN2.js +1 -0
- package/client/dist/assets/agent-DNQlI8EN.js +1 -0
- package/client/dist/assets/agent-DyzUH89R.js +1 -0
- package/client/dist/assets/agent-MaTxBlg5.js +1 -0
- package/client/dist/assets/agent-ostmMYNz2.js +1 -0
- package/client/dist/assets/{brain-BBNUUrUI.js → brain-BPbhM1Jx.js} +1 -1
- package/client/dist/assets/dashboard-5C2FOZQX.js +1 -0
- package/client/dist/assets/{dashboard-C6w0rICe.js → dashboard-B6iQ-IL_.js} +1 -1
- package/client/dist/assets/dashboard-BFfnCtq52.js +1 -0
- package/client/dist/assets/dashboard-BIC8KtRf2.js +1 -0
- package/client/dist/assets/dashboard-CEgg9NAr.js +1 -0
- package/client/dist/assets/dashboard-CiOyDh9L.js +1 -0
- package/client/dist/assets/dashboard-D6x5OnNj2.js +1 -0
- package/client/dist/assets/dashboard-DBuLfWo9.js +1 -0
- package/client/dist/assets/{dist-js-BxjTIvdT.js → dist-js-Byjcju1W.js} +1 -1
- package/client/dist/assets/{dist-js-DdP_Gl3p.js → dist-js-Dh-9lbLD.js} +1 -1
- package/client/dist/assets/index-89SuhD4Y.js +158 -0
- package/client/dist/assets/index-DOGzWJhO.css +2 -0
- package/client/dist/assets/integrations-By80EBkf.js +1 -0
- package/client/dist/assets/integrations-CAjq5tdW2.js +1 -0
- package/client/dist/assets/integrations-CG2dp3Kd.js +1 -0
- package/client/dist/assets/integrations-CP_aiV_8.js +1 -0
- package/client/dist/assets/integrations-CUNe3t9l2.js +1 -0
- package/client/dist/assets/integrations-CWjh5VRk.js +1 -0
- package/client/dist/assets/integrations-JOHSCvB3.js +1 -0
- package/client/dist/assets/integrations-RJfzXkPx2.js +1 -0
- package/client/dist/assets/{lib-BviHZ_gK.js → lib-yKyTNJ1c.js} +1 -1
- package/client/dist/assets/nav-B2_ToNqS.js +1 -0
- package/client/dist/assets/nav-BAMkVEId.js +1 -0
- package/client/dist/assets/nav-BTg8s-cD.js +1 -0
- package/client/dist/assets/nav-BY_4EWRv.js +1 -0
- package/client/dist/assets/nav-CVXtv0wX2.js +1 -0
- package/client/dist/assets/nav-D0Erv9ZJ2.js +1 -0
- package/client/dist/assets/nav-DUuw9VIU2.js +1 -0
- package/client/dist/assets/nav-S4HM94vW.js +1 -0
- package/client/dist/assets/settings-BYCMtxOz.js +1 -0
- package/client/dist/assets/{settings-BHdPchJP.js → settings-B_v3zgJf.js} +1 -1
- package/client/dist/assets/{settings-BeCczBFS2.js → settings-BemR_e3F.js} +1 -1
- package/client/dist/assets/{settings-D54_cgl12.js → settings-Bp24JHlv2.js} +1 -1
- package/client/dist/assets/{settings-C_nY14Yz.js → settings-BvIwQ3A1.js} +1 -1
- package/client/dist/assets/{settings-BlLi719f.js → settings-Bx2ObkTR2.js} +1 -1
- package/client/dist/assets/{settings-CaZ26a0S.js → settings-C_T0-j9d.js} +1 -1
- package/client/dist/assets/{settings-cn0tbve62.js → settings-DhaFKpcw2.js} +1 -1
- package/client/dist/assets/{upload-CRuuzzvp.js → upload-Qh1NE3xi.js} +1 -1
- package/client/dist/assets/{value-CIr5hDMt.js → value-BMPeyInx.js} +1 -1
- package/client/dist/index.html +29 -29
- package/package.json +1 -1
- package/server/dist/agent-chat-router.js +2 -0
- package/server/dist/agent-operator-prompt.js +17 -1
- package/server/dist/db.js +51 -2
- package/server/dist/dev-ports.js +35 -0
- package/server/dist/global-plugins-router.js +62 -0
- package/server/dist/headroom-manager.js +971 -0
- package/server/dist/headroom-routing.js +53 -0
- package/server/dist/index.js +9 -6
- package/server/dist/mcp/guide.js +3 -0
- package/server/dist/mcp/tools/catalog.js +2 -0
- package/server/dist/mcp/tools/env.js +72 -0
- package/server/dist/mcp/tools/jobs.js +110 -2
- package/server/dist/path-resolver.js +84 -18
- package/server/dist/project-env.js +30 -0
- package/server/dist/project-registry.js +4 -2
- package/server/dist/project-router-jobs.js +126 -0
- package/server/dist/project-router-settings.js +10 -1
- package/server/dist/queue-manager.js +9 -1
- package/server/dist/rail-isolated-launch.js +5 -4
- package/server/dist/rail-pr-decision.js +27 -17
- package/server/dist/rail-pr-store.js +36 -0
- package/server/dist/rails-router.js +7 -1
- package/server/dist/setup-manager.js +32 -29
- package/server/dist/setup-prerequisites.js +3 -1
- package/server/dist/transient-children.js +266 -9
- package/server/dist/util/cli-prompt.js +3 -0
- package/server/dist/workspace-resolution.js +3 -3
- package/server/dist/worktree-env-discovery.js +172 -0
- package/client/dist/assets/AgentBrowserCapture-DQyeEm2V.js +0 -1
- package/client/dist/assets/AgentModeJobsPane-C-g1WNVG.js +0 -1
- package/client/dist/assets/AgentsPage-BWgglK7V.js +0 -86
- package/client/dist/assets/CodePage-BUcAp7SC.js +0 -2
- package/client/dist/assets/DocsDialog-PZHMN18B.js +0 -11
- package/client/dist/assets/DocsPage-D3fChfkj.js +0 -11
- package/client/dist/assets/IntegrationsPage-F87uxxVH.js +0 -3
- package/client/dist/assets/InteractiveJobComposer-DqDCiCa0.js +0 -19
- package/client/dist/assets/JobDetailModal-BWJR60fi.js +0 -1
- package/client/dist/assets/JobDetailPage-CjWE_jyd.js +0 -1
- package/client/dist/assets/JobsPage-LmnXkyCa.js +0 -1
- package/client/dist/assets/LoopsPage-DDNO9h9T.js +0 -1
- package/client/dist/assets/ProjectSettingsDialog-CX854C8e.js +0 -1
- package/client/dist/assets/agent-BI6vr-To.js +0 -1
- package/client/dist/assets/agent-BUGXI3Ok2.js +0 -1
- package/client/dist/assets/agent-Bph_-xt7.js +0 -1
- package/client/dist/assets/agent-Bvy2Csyd.js +0 -1
- package/client/dist/assets/agent-Bwkyg2oX2.js +0 -1
- package/client/dist/assets/agent-CLCyDaZh.js +0 -1
- package/client/dist/assets/agent-Cqs14QMr2.js +0 -1
- package/client/dist/assets/agent-DJ0EBUuP.js +0 -1
- package/client/dist/assets/dashboard-BjjS1OrL.js +0 -1
- package/client/dist/assets/dashboard-Bk6oSk1H2.js +0 -1
- package/client/dist/assets/dashboard-BrH-GdOg2.js +0 -1
- package/client/dist/assets/dashboard-BurhUI8_.js +0 -1
- package/client/dist/assets/dashboard-CAUnfDQL2.js +0 -1
- package/client/dist/assets/dashboard-C_ljpeYO.js +0 -1
- package/client/dist/assets/dashboard-D6OqDdj6.js +0 -1
- package/client/dist/assets/index-C695aPdV.css +0 -2
- package/client/dist/assets/index-Dw1N2-ci.js +0 -156
- package/client/dist/assets/integrations-4KZnYECp.js +0 -1
- package/client/dist/assets/integrations-7W266oqF2.js +0 -1
- package/client/dist/assets/integrations-BNuauK3o.js +0 -1
- package/client/dist/assets/integrations-CHy8tnpV.js +0 -1
- package/client/dist/assets/integrations-Cl_wD5AI.js +0 -1
- package/client/dist/assets/integrations-CuWnMR5L.js +0 -1
- package/client/dist/assets/integrations-Cy8nZ_dv2.js +0 -1
- package/client/dist/assets/integrations-zMqY4VU12.js +0 -1
- package/client/dist/assets/nav-40HzpIFb.js +0 -1
- package/client/dist/assets/nav-8yPhBn00.js +0 -1
- package/client/dist/assets/nav-B6PkGfTk2.js +0 -1
- package/client/dist/assets/nav-BHqMLrS72.js +0 -1
- package/client/dist/assets/nav-CGy4yx3C.js +0 -1
- package/client/dist/assets/nav-CXEDcRdv2.js +0 -1
- package/client/dist/assets/nav-CzKzuwH8.js +0 -1
- package/client/dist/assets/nav-EI_OhEAo.js +0 -1
- package/client/dist/assets/settings-D9bDDe2O.js +0 -1
- /package/client/dist/assets/{activity-BZNfOdiS.js → activity-BZZlEG3a.js} +0 -0
- /package/client/dist/assets/{activity--iN4Ad2R2.js → activity-BbWiDfn-2.js} +0 -0
- /package/client/dist/assets/{activity-CIOLxGvh.js → activity-D3Ic-HTS.js} +0 -0
- /package/client/dist/assets/{activity-DtxrAUMo2.js → activity-DFbvNqhE2.js} +0 -0
- /package/client/dist/assets/{activity-D8vQJrpW.js → activity-Dbl71S-E.js} +0 -0
- /package/client/dist/assets/{activity-BuVo5sPB.js → activity-DtzRy92x.js} +0 -0
- /package/client/dist/assets/{activity-6hM_ZSg9.js → activity-DvECTYGt.js} +0 -0
- /package/client/dist/assets/{activity-D2r2h3SE2.js → activity-FleOlK0l2.js} +0 -0
- /package/client/dist/assets/{addspec-D-JLDsUV.js → addspec-BKrH2Ucz.js} +0 -0
- /package/client/dist/assets/{addspec-DfhDbg5c.js → addspec-B_LQMHW3.js} +0 -0
- /package/client/dist/assets/{addspec-bsNDdqRm2.js → addspec-Bw_YQwo62.js} +0 -0
- /package/client/dist/assets/{addspec-BMtLZLW62.js → addspec-CFxGvmJR2.js} +0 -0
- /package/client/dist/assets/{addspec-mgPOt5q02.js → addspec-CR8MB5Fr2.js} +0 -0
- /package/client/dist/assets/{addspec-CP0HRuxY.js → addspec-DaA-CbTt.js} +0 -0
- /package/client/dist/assets/{addspec-DxW6oPsk.js → addspec-DrXqP8ip.js} +0 -0
- /package/client/dist/assets/{addspec-CRuvWn8d.js → addspec-NBbcTbLc.js} +0 -0
- /package/client/dist/assets/{agents-BpoUaoW92.js → agents-B9pFSJQI2.js} +0 -0
- /package/client/dist/assets/{agents-78bTZGZh.js → agents-BHgn--3j.js} +0 -0
- /package/client/dist/assets/{agents-BUdEi3-N.js → agents-B_qGaB0F.js} +0 -0
- /package/client/dist/assets/{agents-Cev2oFfX.js → agents-CGDn_3kB.js} +0 -0
- /package/client/dist/assets/{agents-bvyRLczL.js → agents-CP8_ezYG.js} +0 -0
- /package/client/dist/assets/{agents-BLhWCmDS2.js → agents-CV-_xBvX2.js} +0 -0
- /package/client/dist/assets/{agents-CWo-KyAB.js → agents-HFCqQH0Z.js} +0 -0
- /package/client/dist/assets/{agents-DDtCJ5M02.js → agents-VsyFhGOS2.js} +0 -0
- /package/client/dist/assets/{agentstudio-BMQxc_bz.js → agentstudio-BEp7Sb46.js} +0 -0
- /package/client/dist/assets/{agentstudio-Dtq3dGYk.js → agentstudio-BOjG6tgJ.js} +0 -0
- /package/client/dist/assets/{agentstudio-C_Jq8Tl4.js → agentstudio-Br3WD7Xs.js} +0 -0
- /package/client/dist/assets/{agentstudio-sI27TpBu2.js → agentstudio-CuTt3ZGe2.js} +0 -0
- /package/client/dist/assets/{agentstudio-CWgXG5W52.js → agentstudio-D2A5peWV2.js} +0 -0
- /package/client/dist/assets/{agentstudio-CZm2rOzt.js → agentstudio-Dxe_94PK.js} +0 -0
- /package/client/dist/assets/{agentstudio-COZriimI2.js → agentstudio-RfZavULt2.js} +0 -0
- /package/client/dist/assets/{agentstudio-jKMTdT70.js → agentstudio-RsxnZtUA.js} +0 -0
- /package/client/dist/assets/{aiedit-igmw-ZoZ2.js → aiedit-26p5A_HY2.js} +0 -0
- /package/client/dist/assets/{aiedit-6CHdb_iv2.js → aiedit-CV520D4W2.js} +0 -0
- /package/client/dist/assets/{aiedit-pc5JDUq1.js → aiedit-CcugGPbS.js} +0 -0
- /package/client/dist/assets/{aiedit-eMN4eQPF.js → aiedit-D0Syl-mC.js} +0 -0
- /package/client/dist/assets/{aiedit-hknZlly82.js → aiedit-DnBzjlVz2.js} +0 -0
- /package/client/dist/assets/{aiedit-B03pD_Na.js → aiedit-DrIjSojo.js} +0 -0
- /package/client/dist/assets/{aiedit-DYtnZU9H.js → aiedit-DrfHQnlz.js} +0 -0
- /package/client/dist/assets/{aiedit-BtRqRxML.js → aiedit-a-Z-g5iC.js} +0 -0
- /package/client/dist/assets/{analytics-C5dpiS0o.js → analytics-4-eskbtX.js} +0 -0
- /package/client/dist/assets/{analytics-CR1TJqUi2.js → analytics-4HvXb0Vn2.js} +0 -0
- /package/client/dist/assets/{analytics-rmHuJv0V.js → analytics-Bd9rHGjV.js} +0 -0
- /package/client/dist/assets/{analytics-BWMUZ2Pq2.js → analytics-Bpe41fnX2.js} +0 -0
- /package/client/dist/assets/{analytics-Bj-SmKHa.js → analytics-CpSTNfui.js} +0 -0
- /package/client/dist/assets/{analytics-Ce-sPORu.js → analytics-CxDYcBl9.js} +0 -0
- /package/client/dist/assets/{analytics-CCprAqk3.js → analytics-DEe5BMz8.js} +0 -0
- /package/client/dist/assets/{analytics-DfNdXR0y2.js → analytics-DcOITHla2.js} +0 -0
- /package/client/dist/assets/{attachments-BjyMmrwE.js → attachments-CA2f3qL3.js} +0 -0
- /package/client/dist/assets/{attachments-DCZOL1RU.js → attachments-CHDbhKeH.js} +0 -0
- /package/client/dist/assets/{attachments-rNn1ekRy2.js → attachments-CKAY5aYG2.js} +0 -0
- /package/client/dist/assets/{attachments-BgBNSqQd.js → attachments-CgOQ9K-6.js} +0 -0
- /package/client/dist/assets/{attachments-DPCmkFZu2.js → attachments-D2jwJXQ42.js} +0 -0
- /package/client/dist/assets/{attachments-C9GTTJsx2.js → attachments-DlEQu56g2.js} +0 -0
- /package/client/dist/assets/{attachments-Ci6_42Kh.js → attachments-DvxQbBTC.js} +0 -0
- /package/client/dist/assets/{attachments-DL-bPy9E.js → attachments-jVdKzAXn.js} +0 -0
- /package/client/dist/assets/{auth-Cvsb7-Ek.js → auth-DpZq5p1e.js} +0 -0
- /package/client/dist/assets/{browser-Cvg-QHvd2.js → browser-BQFDxrRq.js} +0 -0
- /package/client/dist/assets/{browser-rWKj-RR_2.js → browser-BbJ_q_OT2.js} +0 -0
- /package/client/dist/assets/{browser-Cc0iDqUi.js → browser-C4VJ5CW12.js} +0 -0
- /package/client/dist/assets/{browser-BPkhILSJ.js → browser-Cl-FwPXs.js} +0 -0
- /package/client/dist/assets/{browser-CdS6O1Qb.js → browser-DM8G151q.js} +0 -0
- /package/client/dist/assets/{browser-FRv8P02E.js → browser-Do2jf8_N.js} +0 -0
- /package/client/dist/assets/{browser-BqLqJVVT.js → browser-JsiohNmW.js} +0 -0
- /package/client/dist/assets/{browser-BijVYG0Y2.js → browser-kA31eYhu2.js} +0 -0
- /package/client/dist/assets/{chat-VMflhzxn.js → chat-0dg5hOo_.js} +0 -0
- /package/client/dist/assets/{chat-DdwZjT8K2.js → chat-BMWD_Rx12.js} +0 -0
- /package/client/dist/assets/{chat-Bom6bqK3.js → chat-BalviUAD.js} +0 -0
- /package/client/dist/assets/{chat-DsbwJhus.js → chat-CJddjN3D2.js} +0 -0
- /package/client/dist/assets/{chat-Dnwg-NpQ.js → chat-D5xEtsf3.js} +0 -0
- /package/client/dist/assets/{chat-vylxqR0P2.js → chat-Ds8GvCnv.js} +0 -0
- /package/client/dist/assets/{chat-BgL3zv6x2.js → chat-SaG-ng1x2.js} +0 -0
- /package/client/dist/assets/{chat-Bh3NdgJs.js → chat-n43nZGvn.js} +0 -0
- /package/client/dist/assets/{code-thSEptMx.js → code-B62fClk-.js} +0 -0
- /package/client/dist/assets/{code-DzC1rMk12.js → code-Bit_z2Xd2.js} +0 -0
- /package/client/dist/assets/{code-BRcDlze52.js → code-BotxM43N2.js} +0 -0
- /package/client/dist/assets/{code-UPVvmAKM2.js → code-C9U6M5-e2.js} +0 -0
- /package/client/dist/assets/{code-BdMy3Ffq.js → code-CIPmDEd0.js} +0 -0
- /package/client/dist/assets/{code-DgeHz9CB.js → code-DUe1Bouz.js} +0 -0
- /package/client/dist/assets/{code-CwnBPPyA.js → code-F_KjccGf.js} +0 -0
- /package/client/dist/assets/{code-CeQ0FZpC.js → code-HDK4P6Rx.js} +0 -0
- /package/client/dist/assets/{commands-DkU0qliq.js → commands-BFQZc9AG.js} +0 -0
- /package/client/dist/assets/{commands-u-4eEHCN.js → commands-BMkC5-ki.js} +0 -0
- /package/client/dist/assets/{commands-CSDs9q_2.js → commands-BllkJauO.js} +0 -0
- /package/client/dist/assets/{commands-xHDDGfcP2.js → commands-CWX9WmDX2.js} +0 -0
- /package/client/dist/assets/{commands-C4Y3C-PF2.js → commands-CzFSoIiq2.js} +0 -0
- /package/client/dist/assets/{commands-CckaBWdV.js → commands-DCJ-R9E4.js} +0 -0
- /package/client/dist/assets/{commands-JWMfGDmr.js → commands-KBbhsnw8.js} +0 -0
- /package/client/dist/assets/{commands-RHOchN4n2.js → commands-cKTaLwwV2.js} +0 -0
- /package/client/dist/assets/{common-DmqBPwZG.js → common-BfQD173j.js} +0 -0
- /package/client/dist/assets/{common-DhWTkocg.js → common-CF8iGXrG.js} +0 -0
- /package/client/dist/assets/{common-Bwafujnc2.js → common-Cfmwcrl52.js} +0 -0
- /package/client/dist/assets/{common-C-vU-j-I.js → common-D2cimsul.js} +0 -0
- /package/client/dist/assets/{common-C5HKSbbz.js → common-DH7fjyL1.js} +0 -0
- /package/client/dist/assets/{common-CYaT2yHp.js → common-DXrmj6WD.js} +0 -0
- /package/client/dist/assets/{common-BM3LFLCE2.js → common-DZbXlmq72.js} +0 -0
- /package/client/dist/assets/{common-DiPTT7V_2.js → common-DtXmzX2o2.js} +0 -0
- /package/client/dist/assets/{explore-UvW1jEMa.js → explore-BEpIs343.js} +0 -0
- /package/client/dist/assets/{explore-CNBzrEPS.js → explore-BO_OinBD.js} +0 -0
- /package/client/dist/assets/{explore-C0k8HrkN.js → explore-Bhy7WWXv.js} +0 -0
- /package/client/dist/assets/{explore-B7fblXBb2.js → explore-CAc8Qzu32.js} +0 -0
- /package/client/dist/assets/{explore-D_mWBYCS2.js → explore-DCZL9gRN2.js} +0 -0
- /package/client/dist/assets/{explore-Dahc1lOx.js → explore-DW_YTkQh.js} +0 -0
- /package/client/dist/assets/{explore-BHtEpoNN2.js → explore-ELbrSZyQ2.js} +0 -0
- /package/client/dist/assets/{explore-BKVV5JSu.js → explore-_XyU5e7R.js} +0 -0
- /package/client/dist/assets/{format-command-CuWuRvka.js → format-command-Db1viey1.js} +0 -0
- /package/client/dist/assets/{jira-uNH1gETL2.js → jira-B5wszJ1-2.js} +0 -0
- /package/client/dist/assets/{jira-BHAKtc4B.js → jira-C5UjxHE4.js} +0 -0
- /package/client/dist/assets/{jira-D5efgwp_.js → jira-CQ7mrZde.js} +0 -0
- /package/client/dist/assets/{jira-bDs7Im2C.js → jira-DIRkZk9U.js} +0 -0
- /package/client/dist/assets/{jira-CPpgA-5z2.js → jira-DTYu3hJi2.js} +0 -0
- /package/client/dist/assets/{jira-B3613-gj.js → jira-DeIjwCK3.js} +0 -0
- /package/client/dist/assets/{jira-DS7ownDy.js → jira-DtZpokjJ.js} +0 -0
- /package/client/dist/assets/{jira-Be369RIR2.js → jira-ZdKb-50F2.js} +0 -0
- /package/client/dist/assets/{jobs-CE8qeIYj.js → jobs-BFIdeDTw.js} +0 -0
- /package/client/dist/assets/{jobs-BgQzB0Sx2.js → jobs-BJ-Gcao22.js} +0 -0
- /package/client/dist/assets/{jobs-1GV2-4zd2.js → jobs-DAGiPVIS2.js} +0 -0
- /package/client/dist/assets/{jobs-26F9gGcE.js → jobs-DAU8C_tZ.js} +0 -0
- /package/client/dist/assets/{jobs-BqWui9_f.js → jobs-Da0GaaSc.js} +0 -0
- /package/client/dist/assets/{jobs-D5sZzgLs.js → jobs-DrYmsKy5.js} +0 -0
- /package/client/dist/assets/{jobs-Wh7dn28q2.js → jobs-P3unxtxo2.js} +0 -0
- /package/client/dist/assets/{jobs-DzeuASRf.js → jobs-p360zdf1.js} +0 -0
- /package/client/dist/assets/{loops-CvOWsi5r.js → loops-Bi4ywYGn.js} +0 -0
- /package/client/dist/assets/{loops-BvqQ-bHR2.js → loops-BjK8lmoZ2.js} +0 -0
- /package/client/dist/assets/{loops-Brnx4Fji.js → loops-CO6q75W8.js} +0 -0
- /package/client/dist/assets/{loops-K5_HXfJa.js → loops-D3wdUkZV.js} +0 -0
- /package/client/dist/assets/{loops-D6D3XHjj.js → loops-DDs4KPu_.js} +0 -0
- /package/client/dist/assets/{loops-LIgO_-eM.js → loops-DKzYFcq6.js} +0 -0
- /package/client/dist/assets/{loops-DJNhp17p2.js → loops-Du-bNVpc2.js} +0 -0
- /package/client/dist/assets/{loops-vhJdbcz92.js → loops-DvGbSfxw2.js} +0 -0
- /package/client/dist/assets/{mcp-XJNRfxVp2.js → mcp-BG-OCJca2.js} +0 -0
- /package/client/dist/assets/{mcp-DtbxuH3V2.js → mcp-CbJ04oFS2.js} +0 -0
- /package/client/dist/assets/{mcp-sRmWE8WJ.js → mcp-CrWdq5db.js} +0 -0
- /package/client/dist/assets/{mcp-H-gnQUdN.js → mcp-Dd844F5_.js} +0 -0
- /package/client/dist/assets/{mcp-Dcl-XxkR.js → mcp-DfmzoT5t.js} +0 -0
- /package/client/dist/assets/{mcp-CjVKXAhD2.js → mcp-DyULurYS2.js} +0 -0
- /package/client/dist/assets/{mcp-CIH0pH1K.js → mcp-HwBbQDk0.js} +0 -0
- /package/client/dist/assets/{mcp-N1nzp8oL.js → mcp-zSJ2TsJj.js} +0 -0
- /package/client/dist/assets/{setup-BuQNKztt2.js → setup-1vN48B-m2.js} +0 -0
- /package/client/dist/assets/{setup-BwMovM9z2.js → setup-BmJ0Kx16.js} +0 -0
- /package/client/dist/assets/{setup-RiUHzq7E.js → setup-D2CmgAro.js} +0 -0
- /package/client/dist/assets/{setup-BXpw07r-.js → setup-DQWsu2TR.js} +0 -0
- /package/client/dist/assets/{setup-D84y3wx-.js → setup-JeBwoXoM.js} +0 -0
- /package/client/dist/assets/{setup-DU0ULX8q2.js → setup-T_ApeQDV2.js} +0 -0
- /package/client/dist/assets/{setup-BiduSX9d.js → setup-mXnzww4f.js} +0 -0
- /package/client/dist/assets/{setup-7J8TjG4O.js → setup-q0wRHUp1.js} +0 -0
- /package/client/dist/assets/{specs-CWtKcVDC.js → specs-BGRKhM8t.js} +0 -0
- /package/client/dist/assets/{specs-CBDvEYKv.js → specs-C6J2zrLM.js} +0 -0
- /package/client/dist/assets/{specs-D2clrt2c2.js → specs-CT8exGOy2.js} +0 -0
- /package/client/dist/assets/{specs-C9Sd7BhH2.js → specs-Ce5y2iFc2.js} +0 -0
- /package/client/dist/assets/{specs-CIPr9reA2.js → specs-DZDoZsDq.js} +0 -0
- /package/client/dist/assets/{specs-PozUNhqb.js → specs-DbtIzV8a.js} +0 -0
- /package/client/dist/assets/{specs-DTplt2XM.js → specs-GkiL-snY.js} +0 -0
- /package/client/dist/assets/{specs-CmEZY57v.js → specs-PGIOzMtc.js} +0 -0
- /package/client/dist/assets/{terminal-xGbCZJHg.js → terminal-BNSg2lR7.js} +0 -0
- /package/client/dist/assets/{terminal-De2Brd23.js → terminal-BTpE7iki.js} +0 -0
- /package/client/dist/assets/{terminal-DFPNRLKp.js → terminal-CFt56A5f.js} +0 -0
- /package/client/dist/assets/{terminal-BRm03iFA2.js → terminal-Cvabyzm42.js} +0 -0
- /package/client/dist/assets/{terminal-DxQWCqSD2.js → terminal-D0z12_sJ2.js} +0 -0
- /package/client/dist/assets/{terminal-Z7KsDM_n.js → terminal-DDk_bTw0.js} +0 -0
- /package/client/dist/assets/{terminal-cqfd3EDV.js → terminal-DEyt0prF.js} +0 -0
- /package/client/dist/assets/{terminal-WSAvHXoU.js → terminal-a6Uq8B0p.js} +0 -0
- /package/client/dist/assets/{tickets-CkfbISuK.js → tickets-BHv3ewvq.js} +0 -0
- /package/client/dist/assets/{tickets-C3b8cHhd2.js → tickets-BIxU86j12.js} +0 -0
- /package/client/dist/assets/{tickets-SuX_HkK4.js → tickets-C2BhwzwR.js} +0 -0
- /package/client/dist/assets/{tickets-DTHo9c3C.js → tickets-D1IeX2EW.js} +0 -0
- /package/client/dist/assets/{tickets-BoXom7l8.js → tickets-DZut8Ryt.js} +0 -0
- /package/client/dist/assets/{tickets-CWYFvx1W2.js → tickets-LfK6LzKl2.js} +0 -0
- /package/client/dist/assets/{tickets-CFXnJsYF.js → tickets-dQfsc7Ph.js} +0 -0
- /package/client/dist/assets/{tickets-B7zT4IRf.js → tickets-sVun0I6v.js} +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setHeadroomRoutingState = setHeadroomRoutingState;
|
|
4
|
+
exports.getHeadroomRoutingState = getHeadroomRoutingState;
|
|
5
|
+
exports.providerForBinary = providerForBinary;
|
|
6
|
+
exports.applyHeadroomEnvForBinary = applyHeadroomEnvForBinary;
|
|
7
|
+
exports.withHeadroomSpawnEnv = withHeadroomSpawnEnv;
|
|
8
|
+
const DEFAULT_PORT = 8787;
|
|
9
|
+
let routingState = {
|
|
10
|
+
port: DEFAULT_PORT,
|
|
11
|
+
activeProviders: {},
|
|
12
|
+
};
|
|
13
|
+
function setHeadroomRoutingState(next) {
|
|
14
|
+
routingState = {
|
|
15
|
+
port: Number.isInteger(next.port) ? next.port : DEFAULT_PORT,
|
|
16
|
+
activeProviders: { ...next.activeProviders },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function getHeadroomRoutingState() {
|
|
20
|
+
return {
|
|
21
|
+
port: routingState.port,
|
|
22
|
+
activeProviders: { ...routingState.activeProviders },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function providerForBinary(binary) {
|
|
26
|
+
const name = binary.toLowerCase().replace(/\.cmd$|\.exe$/, '');
|
|
27
|
+
if (name === 'codex')
|
|
28
|
+
return 'codex';
|
|
29
|
+
if (name === 'claude')
|
|
30
|
+
return 'claude';
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function applyHeadroomEnvForBinary(binary, env) {
|
|
34
|
+
const provider = providerForBinary(binary);
|
|
35
|
+
if (!provider || !routingState.activeProviders[provider])
|
|
36
|
+
return env ?? process.env;
|
|
37
|
+
const base = `http://127.0.0.1:${routingState.port}`;
|
|
38
|
+
const merged = { ...(env ?? process.env) };
|
|
39
|
+
if (provider === 'codex') {
|
|
40
|
+
merged.OPENAI_BASE_URL = `${base}/v1`;
|
|
41
|
+
}
|
|
42
|
+
else if (provider === 'claude') {
|
|
43
|
+
merged.ANTHROPIC_BASE_URL = base;
|
|
44
|
+
}
|
|
45
|
+
merged.HEADROOM_PORT = String(routingState.port);
|
|
46
|
+
return merged;
|
|
47
|
+
}
|
|
48
|
+
function withHeadroomSpawnEnv(binary, options = {}) {
|
|
49
|
+
return {
|
|
50
|
+
...options,
|
|
51
|
+
env: applyHeadroomEnvForBinary(binary, options.env),
|
|
52
|
+
};
|
|
53
|
+
}
|
package/server/dist/index.js
CHANGED
|
@@ -29,10 +29,13 @@ const agent_chat_router_1 = require("./agent-chat-router");
|
|
|
29
29
|
const agent_chat_registry_1 = require("./agent-chat-registry");
|
|
30
30
|
const browser_playwright_1 = require("./browser-playwright");
|
|
31
31
|
const telemetry_receiver_1 = require("./telemetry-receiver");
|
|
32
|
+
const headroom_manager_1 = require("./headroom-manager");
|
|
33
|
+
const global_plugins_router_1 = require("./global-plugins-router");
|
|
32
34
|
const telemetry_compactor_1 = require("./telemetry-compactor");
|
|
33
35
|
const framework_manager_1 = require("./framework-manager");
|
|
34
36
|
const artifact_registry_1 = require("./artifact-registry");
|
|
35
37
|
const path_resolver_1 = require("./path-resolver");
|
|
38
|
+
const dev_ports_1 = require("./dev-ports");
|
|
36
39
|
// Side-effect import: registers every bundled ProviderAdapter (claude, codex,
|
|
37
40
|
// future providers) so `getAdapter`/`hasAdapter`/`listAdapters` are populated
|
|
38
41
|
// before any manager constructs a project context. See
|
|
@@ -85,12 +88,7 @@ if (parentPidArg) {
|
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
90
|
// ─── Parse CLI args ───────────────────────────────────────────────────────────
|
|
88
|
-
|
|
89
|
-
for (let i = 2; i < process.argv.length; i++) {
|
|
90
|
-
if (process.argv[i] === '--port' && process.argv[i + 1]) {
|
|
91
|
-
port = parseInt(process.argv[++i], 10);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
91
|
+
const port = (0, dev_ports_1.resolveServerPort)(process.argv);
|
|
94
92
|
// ─── PID file management ──────────────────────────────────────────────────────
|
|
95
93
|
const PID_DIR = path_1.default.join(os_1.default.homedir(), '.specrails');
|
|
96
94
|
const PID_FILE = path_1.default.join(PID_DIR, 'manager.pid');
|
|
@@ -541,6 +539,11 @@ function applyPtyWsRateLimiting(ws) {
|
|
|
541
539
|
mcpManager.start().catch((err) => console.error('[mcp] boot start failed:', err));
|
|
542
540
|
}
|
|
543
541
|
// ─── App-global agent chat (drives the app via its own MCP) ────────────────
|
|
542
|
+
const headroomManager = new headroom_manager_1.HeadroomManager(registry.desktopDb, broadcast, () => registry.installedProvidersUnion().filter((provider) => provider === 'codex' || provider === 'claude'));
|
|
543
|
+
app.use('/api/global-plugins', (0, global_plugins_router_1.createGlobalPluginsRouter)(headroomManager));
|
|
544
|
+
headroomManager.startActiveProxyOnBoot().catch((err) => {
|
|
545
|
+
console.error('[headroom] boot start failed:', err);
|
|
546
|
+
});
|
|
544
547
|
const agentChatManager = new agent_chat_manager_1.AgentChatManager(broadcast, registry.desktopDb, port, registry);
|
|
545
548
|
_agentChatManager = agentChatManager;
|
|
546
549
|
// Publish the instance to the process-wide registry so the rails layer can
|
package/server/dist/mcp/guide.js
CHANGED
|
@@ -149,6 +149,9 @@ heading inside the description); \`labels\`; \`priority\`. Spec content is Engli
|
|
|
149
149
|
- \`specrails_jobs(spawn, command)\` bypasses rails and enqueues an arbitrary
|
|
150
150
|
slash-command job (e.g. \`/specrails:implement #5 --yes\`); \`queue\`, \`pause\`,
|
|
151
151
|
\`resume\`, \`reorder\`, \`priority\` manage the queue.
|
|
152
|
+
- Long-running shell commands launched with \`specrails_jobs(background_start)\`
|
|
153
|
+
create chat chips; use \`specrails_jobs(background_logs, pid)\` to read bounded
|
|
154
|
+
stdout/stderr tail when a chip exits or fails.
|
|
152
155
|
|
|
153
156
|
## Async results (IMPORTANT)
|
|
154
157
|
|
|
@@ -18,6 +18,7 @@ const setup_1 = require("./setup");
|
|
|
18
18
|
const analytics_1 = require("./analytics");
|
|
19
19
|
const git_1 = require("./git");
|
|
20
20
|
const support_1 = require("./support");
|
|
21
|
+
const env_1 = require("./env");
|
|
21
22
|
/**
|
|
22
23
|
* The full MCP tool catalog. Domain-facade tools each expose an `action` enum
|
|
23
24
|
* and call the app's REST API over loopback (see apiCall). Meta tools receive a
|
|
@@ -42,6 +43,7 @@ function buildToolSpecs() {
|
|
|
42
43
|
specs.push(...(0, setup_1.setupTools)());
|
|
43
44
|
specs.push(...(0, analytics_1.analyticsTools)());
|
|
44
45
|
specs.push(...(0, git_1.gitTools)());
|
|
46
|
+
specs.push(...(0, env_1.envTools)());
|
|
45
47
|
specs.push(...(0, support_1.supportTools)());
|
|
46
48
|
// Meta last (needs the full list for search/describe)
|
|
47
49
|
specs.push(...(0, meta_1.metaTools)(() => specs));
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.envTools = envTools;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
const worktree_env_discovery_1 = require("../../worktree-env-discovery");
|
|
7
|
+
function uniq(names) {
|
|
8
|
+
const out = [];
|
|
9
|
+
for (const v of names) {
|
|
10
|
+
if (typeof v !== 'string')
|
|
11
|
+
continue;
|
|
12
|
+
const name = v.trim();
|
|
13
|
+
if (name && !out.includes(name))
|
|
14
|
+
out.push(name);
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
function envTools() {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
name: 'specrails_env',
|
|
22
|
+
title: 'Project worktree environment',
|
|
23
|
+
description: 'Read, scan, and configure the per-project environment variable NAME allowlist used for rail jobs and isolated loop worktrees. ' +
|
|
24
|
+
'Values are never read or stored by this tool; runs read them from the server environment and can recover configured names from the login shell when the desktop process is missing them. scan inspects safe repo config files such as package.json, .npmrc, .yarnrc.yml and pnpm-workspace.yaml for ${VAR} / process.env.VAR references and private scoped package auth hints. ' +
|
|
25
|
+
'Actions: get (read current names), scan (discover candidates), set (write names), auto_configure (scan then merge discovered names into settings).',
|
|
26
|
+
hintTier: 'read',
|
|
27
|
+
tier: (args) => (args.action === 'set' || args.action === 'auto_configure' ? 'write' : 'read'),
|
|
28
|
+
inputSchema: {
|
|
29
|
+
action: zod_1.z.enum(['get', 'scan', 'set', 'auto_configure']).describe('Operation'),
|
|
30
|
+
projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
|
|
31
|
+
names: zod_1.z.array(zod_1.z.string()).optional().describe('set: env var names to configure'),
|
|
32
|
+
merge: zod_1.z.boolean().optional().describe('set: merge with existing names instead of replacing them (default true)'),
|
|
33
|
+
},
|
|
34
|
+
async handler(ctx, args) {
|
|
35
|
+
const action = args.action;
|
|
36
|
+
const base = (0, types_1.projectPath)(ctx, args.projectId);
|
|
37
|
+
if (action === 'get') {
|
|
38
|
+
const settings = await (0, types_1.apiCall)(ctx, 'GET', `${base}/settings`);
|
|
39
|
+
return { names: Array.isArray(settings.worktreeEnvPassthrough) ? settings.worktreeEnvPassthrough : [] };
|
|
40
|
+
}
|
|
41
|
+
if (action === 'scan') {
|
|
42
|
+
const project = (0, types_1.requireProject)(ctx, args.projectId).project;
|
|
43
|
+
return (0, worktree_env_discovery_1.scanWorktreeEnvRequirements)(project.path);
|
|
44
|
+
}
|
|
45
|
+
if (action === 'set') {
|
|
46
|
+
const incoming = uniq(Array.isArray(args.names) ? args.names : []);
|
|
47
|
+
if (incoming.length === 0)
|
|
48
|
+
throw new Error('set requires at least one env var name in "names".');
|
|
49
|
+
const merge = args.merge !== false;
|
|
50
|
+
const current = await (0, types_1.apiCall)(ctx, 'GET', `${base}/settings`);
|
|
51
|
+
const existing = Array.isArray(current.worktreeEnvPassthrough) ? uniq(current.worktreeEnvPassthrough) : [];
|
|
52
|
+
const next = merge ? uniq([...existing, ...incoming]) : incoming;
|
|
53
|
+
const res = await (0, types_1.apiCall)(ctx, 'PATCH', `${base}/settings`, { worktreeEnvPassthrough: next });
|
|
54
|
+
return { ok: true, names: res.settings?.worktreeEnvPassthrough ?? next, changed: next.filter((n) => !existing.includes(n)) };
|
|
55
|
+
}
|
|
56
|
+
if (action === 'auto_configure') {
|
|
57
|
+
const project = (0, types_1.requireProject)(ctx, args.projectId).project;
|
|
58
|
+
const scan = (0, worktree_env_discovery_1.scanWorktreeEnvRequirements)(project.path);
|
|
59
|
+
const discovered = scan.candidates.map((c) => c.name);
|
|
60
|
+
const current = await (0, types_1.apiCall)(ctx, 'GET', `${base}/settings`);
|
|
61
|
+
const existing = Array.isArray(current.worktreeEnvPassthrough) ? uniq(current.worktreeEnvPassthrough) : [];
|
|
62
|
+
if (discovered.length === 0)
|
|
63
|
+
return { ok: true, names: existing, changed: [], scan };
|
|
64
|
+
const next = uniq([...existing, ...discovered]);
|
|
65
|
+
const res = await (0, types_1.apiCall)(ctx, 'PATCH', `${base}/settings`, { worktreeEnvPassthrough: next });
|
|
66
|
+
return { ok: true, names: res.settings?.worktreeEnvPassthrough ?? next, changed: next.filter((n) => !existing.includes(n)), scan };
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Unknown action "${action}".`);
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
}
|
|
@@ -1,8 +1,52 @@
|
|
|
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.jobsTools = jobsTools;
|
|
4
7
|
const zod_1 = require("zod");
|
|
5
8
|
const types_1 = require("./types");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const transient_children_1 = require("../../transient-children");
|
|
11
|
+
const loop_runs_store_1 = require("../../loop-runs-store");
|
|
12
|
+
function resolveBackgroundCwd(projectRoot, cwd) {
|
|
13
|
+
const resolved = path_1.default.resolve(projectRoot, cwd && cwd.trim() ? cwd : '.');
|
|
14
|
+
const root = path_1.default.resolve(projectRoot);
|
|
15
|
+
if (resolved !== root && !resolved.startsWith(root + path_1.default.sep)) {
|
|
16
|
+
throw new Error('background_start cwd must stay within the selected project.');
|
|
17
|
+
}
|
|
18
|
+
return resolved;
|
|
19
|
+
}
|
|
20
|
+
function backgroundHooks(broadcast) {
|
|
21
|
+
return {
|
|
22
|
+
onStarted: (process) => broadcast({
|
|
23
|
+
type: 'background_process.started',
|
|
24
|
+
process,
|
|
25
|
+
projectId: process.projectId,
|
|
26
|
+
timestamp: new Date().toISOString(),
|
|
27
|
+
}),
|
|
28
|
+
onExited: (process) => broadcast({
|
|
29
|
+
type: 'background_process.exited',
|
|
30
|
+
process,
|
|
31
|
+
projectId: process.projectId,
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function backgroundStartBusyReason(projectCtx) {
|
|
37
|
+
const activeJobId = typeof projectCtx.queueManager?.getActiveJobId === 'function' ? projectCtx.queueManager.getActiveJobId() : null;
|
|
38
|
+
if (activeJobId)
|
|
39
|
+
return `job ${activeJobId} is still running`;
|
|
40
|
+
try {
|
|
41
|
+
const runningLoops = (0, loop_runs_store_1.listRunningLoopRuns)(projectCtx.db, projectCtx.project.id);
|
|
42
|
+
if (runningLoops.length > 0)
|
|
43
|
+
return `loop run ${runningLoops[0].id} is still running`;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Some unit harnesses stub the DB narrowly; absence of loop metadata is not a blocker.
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
6
50
|
/**
|
|
7
51
|
* Jobs domain facade. Maps every in-scope jobs/queue action to its REST
|
|
8
52
|
* endpoint on the loopback API (server/project-router-jobs.ts, plus the
|
|
@@ -20,6 +64,7 @@ function jobsTools() {
|
|
|
20
64
|
title: 'Jobs & Queue',
|
|
21
65
|
description: 'Manage a project\'s AI-pipeline job queue and individual jobs. ' +
|
|
22
66
|
'Actions: list, get, queue, spawn (ai-spawn — enqueues an arbitrary slash-command job, returns {jobId,position}, async; from the in-app agent chat the engine defaults to your conversation\'s provider — pass aiEngine to override), ' +
|
|
67
|
+
'background_start (operate-level shell command tied to the current agent chat; requires confirmed:true after explicit user confirmation before use), background_logs, background_kill, ' +
|
|
23
68
|
'cancel (destructive — cancels a running/queued job or deletes a terminal one), ' +
|
|
24
69
|
'purge (destructive — bulk-delete persisted job rows in a date range), ' +
|
|
25
70
|
'pause / resume (queue), reorder (queued-job order), priority (change a queued job\'s priority), ' +
|
|
@@ -34,7 +79,7 @@ function jobsTools() {
|
|
|
34
79
|
const action = a.action;
|
|
35
80
|
if (['cancel', 'purge'].includes(action))
|
|
36
81
|
return 'destructive';
|
|
37
|
-
if (['spawn', 'interactive_turn'].includes(action))
|
|
82
|
+
if (['spawn', 'interactive_turn', 'background_start', 'background_kill'].includes(action))
|
|
38
83
|
return 'ai-spawn';
|
|
39
84
|
if (['pause', 'resume', 'reorder', 'priority', 'finalize'].includes(action))
|
|
40
85
|
return 'write';
|
|
@@ -64,6 +109,9 @@ function jobsTools() {
|
|
|
64
109
|
'default_spec_model',
|
|
65
110
|
'interactive_turn',
|
|
66
111
|
'finalize',
|
|
112
|
+
'background_start',
|
|
113
|
+
'background_logs',
|
|
114
|
+
'background_kill',
|
|
67
115
|
])
|
|
68
116
|
.describe('Operation to perform'),
|
|
69
117
|
projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
|
|
@@ -73,7 +121,7 @@ function jobsTools() {
|
|
|
73
121
|
.optional()
|
|
74
122
|
.describe('Job id (for get / cancel / priority / diagnostic / interactive_turn / finalize)'),
|
|
75
123
|
// ── list / export pagination + filters ──
|
|
76
|
-
limit: zod_1.z.number().optional().describe('Page size for list (1-200, default 50) / activity (1-100, default 50)'),
|
|
124
|
+
limit: zod_1.z.number().optional().describe('Page size for list (1-200, default 50) / activity (1-100, default 50); background_logs returns the last N buffered log lines'),
|
|
77
125
|
offset: zod_1.z.number().optional().describe('Page offset for list'),
|
|
78
126
|
status: zod_1.z.string().optional().describe('Filter by job status (list)'),
|
|
79
127
|
from: zod_1.z.string().optional().describe('ISO date lower bound (list / export / purge)'),
|
|
@@ -99,6 +147,11 @@ function jobsTools() {
|
|
|
99
147
|
.describe('reorder: exact set of currently-queued job ids in the desired order; compare: exactly 2 job ids'),
|
|
100
148
|
// ── interactive_turn ──
|
|
101
149
|
text: zod_1.z.string().optional().describe('Prompt text for interactive_turn'),
|
|
150
|
+
chatId: zod_1.z.string().optional().describe('Agent chat conversation id for background_start/background_logs/background_kill ownership; defaults to the in-app agent conversation that called the tool'),
|
|
151
|
+
cwd: zod_1.z.string().optional().describe('Optional cwd for background_start; resolved inside the selected project root'),
|
|
152
|
+
pid: zod_1.z.number().optional().describe('Background process pid for background_logs/background_kill'),
|
|
153
|
+
confirmed: zod_1.z.boolean().optional().describe('background_start only: must be true after explicit user confirmation for this exact command'),
|
|
154
|
+
allowWhileBusy: zod_1.z.boolean().optional().describe('background_start only: override active job/loop guard after explicit user confirmation'),
|
|
102
155
|
// ── export ──
|
|
103
156
|
format: zod_1.z.enum(['json', 'csv']).optional().describe('Export format (default json)'),
|
|
104
157
|
// ── default_spec_model ──
|
|
@@ -270,6 +323,61 @@ function jobsTools() {
|
|
|
270
323
|
throw new Error('finalize requires a "jobId".');
|
|
271
324
|
return (0, types_1.apiCall)(ctx, 'POST', `${base}/jobs/${encodeURIComponent(id)}/finalize`);
|
|
272
325
|
}
|
|
326
|
+
case 'background_start': {
|
|
327
|
+
const command = args.command;
|
|
328
|
+
if (!command || !command.trim())
|
|
329
|
+
throw new Error('background_start requires a "command".');
|
|
330
|
+
if (args.confirmed !== true)
|
|
331
|
+
throw new Error('background_start requires confirmed:true after explicit user confirmation for this exact command.');
|
|
332
|
+
const chatId = args.chatId ?? ctx.originConversationId ?? undefined;
|
|
333
|
+
if (!chatId || !chatId.trim())
|
|
334
|
+
throw new Error('background_start requires a "chatId".');
|
|
335
|
+
const projectCtx = (0, types_1.requireProject)(ctx, args.projectId);
|
|
336
|
+
const busyReason = backgroundStartBusyReason(projectCtx);
|
|
337
|
+
if (busyReason && args.allowWhileBusy !== true) {
|
|
338
|
+
throw new Error(`background_start refused because ${busyReason}. Wait for it to finish, or pass allowWhileBusy:true only after explicit user confirmation.`);
|
|
339
|
+
}
|
|
340
|
+
const cwd = resolveBackgroundCwd(projectCtx.project.path, args.cwd);
|
|
341
|
+
const process = (0, transient_children_1.startBackgroundProcess)(command.trim(), cwd, chatId.trim(), projectCtx.project.id, backgroundHooks(ctx.broadcast));
|
|
342
|
+
return { ok: true, process };
|
|
343
|
+
}
|
|
344
|
+
case 'background_kill': {
|
|
345
|
+
const pid = args.pid;
|
|
346
|
+
if (typeof pid !== 'number' || !Number.isFinite(pid))
|
|
347
|
+
throw new Error('background_kill requires a numeric "pid".');
|
|
348
|
+
const chatId = args.chatId ?? ctx.originConversationId ?? undefined;
|
|
349
|
+
if (!chatId || !chatId.trim())
|
|
350
|
+
throw new Error('background_kill requires a "chatId".');
|
|
351
|
+
const projectCtx = (0, types_1.requireProject)(ctx, args.projectId);
|
|
352
|
+
const killed = (0, transient_children_1.killOwnedBackgroundProcess)(pid, { projectId: projectCtx.project.id, chatId: chatId.trim() });
|
|
353
|
+
if (!killed)
|
|
354
|
+
throw new Error('background_kill requires a registered pid in the same project/chat.');
|
|
355
|
+
return { ok: true, pid, status: 'killing' };
|
|
356
|
+
}
|
|
357
|
+
case 'background_logs': {
|
|
358
|
+
const pid = args.pid;
|
|
359
|
+
if (typeof pid !== 'number' || !Number.isFinite(pid))
|
|
360
|
+
throw new Error('background_logs requires a numeric "pid".');
|
|
361
|
+
const chatId = args.chatId ?? ctx.originConversationId ?? undefined;
|
|
362
|
+
if (!chatId || !chatId.trim())
|
|
363
|
+
throw new Error('background_logs requires a "chatId".');
|
|
364
|
+
const projectCtx = (0, types_1.requireProject)(ctx, args.projectId);
|
|
365
|
+
const limit = typeof args.limit === 'number' && Number.isFinite(args.limit) ? args.limit : undefined;
|
|
366
|
+
const logs = (0, transient_children_1.getBackgroundProcessLogs)(pid, {
|
|
367
|
+
projectId: projectCtx.project.id,
|
|
368
|
+
chatId: chatId.trim(),
|
|
369
|
+
...(limit !== undefined ? { limit } : {}),
|
|
370
|
+
});
|
|
371
|
+
if (!logs)
|
|
372
|
+
throw new Error('background_logs requires a registered pid in the same project/chat; logs may have expired.');
|
|
373
|
+
return {
|
|
374
|
+
ok: true,
|
|
375
|
+
...logs,
|
|
376
|
+
hint: logs.lines.length > 0
|
|
377
|
+
? 'Inspect stdout/stderr to explain why the background process exited or failed.'
|
|
378
|
+
: 'No stdout/stderr lines have been captured yet; the process may not have emitted newline-terminated output.',
|
|
379
|
+
};
|
|
380
|
+
}
|
|
273
381
|
default:
|
|
274
382
|
throw new Error(`Unknown action "${action}".`);
|
|
275
383
|
}
|
|
@@ -11,6 +11,8 @@ exports.resolveStartupPath = resolveStartupPath;
|
|
|
11
11
|
exports.parseLoginShellOutput = parseLoginShellOutput;
|
|
12
12
|
exports.augmentPathFromLoginShell = augmentPathFromLoginShell;
|
|
13
13
|
exports.parseLoginShellEnv = parseLoginShellEnv;
|
|
14
|
+
exports.augmentEnvFromLoginShell = augmentEnvFromLoginShell;
|
|
15
|
+
exports.augmentEnvFromLoginShellSync = augmentEnvFromLoginShellSync;
|
|
14
16
|
exports.augmentAuthEnvFromLoginShell = augmentAuthEnvFromLoginShell;
|
|
15
17
|
exports.getPathDiagnostic = getPathDiagnostic;
|
|
16
18
|
exports.__resetPathResolverForTest = __resetPathResolverForTest;
|
|
@@ -103,7 +105,14 @@ function resolveBundledBinDirs(runtimesPath) {
|
|
|
103
105
|
else if (fileExists(path_1.default.join(runtimesPath, 'git', 'bin', 'git'))) {
|
|
104
106
|
gitBinDir = path_1.default.join(runtimesPath, 'git', 'bin');
|
|
105
107
|
}
|
|
106
|
-
|
|
108
|
+
const uvBinDir = isWin
|
|
109
|
+
? (fileExists(path_1.default.join(runtimesPath, 'uv', 'uv.exe')) ? path_1.default.join(runtimesPath, 'uv')
|
|
110
|
+
: fileExists(path_1.default.join(runtimesPath, 'uv', 'bin', 'uv.exe')) ? path_1.default.join(runtimesPath, 'uv', 'bin')
|
|
111
|
+
: null)
|
|
112
|
+
: (fileExists(path_1.default.join(runtimesPath, 'uv', 'bin', 'uv')) ? path_1.default.join(runtimesPath, 'uv', 'bin')
|
|
113
|
+
: fileExists(path_1.default.join(runtimesPath, 'uv', 'uv')) ? path_1.default.join(runtimesPath, 'uv')
|
|
114
|
+
: null);
|
|
115
|
+
return { nodeBinDir, gitBinDir, uvBinDir };
|
|
107
116
|
}
|
|
108
117
|
/**
|
|
109
118
|
* Bin dir of the bundled GitHub CLI (`runtimes/gh/bin/gh[.exe]`), or null.
|
|
@@ -264,7 +273,7 @@ function resolveStartupPathBase() {
|
|
|
264
273
|
if (process.env.SPECRAILS_IS_DESKTOP === '1') {
|
|
265
274
|
const runtimesPath = process.env.SPECRAILS_BUNDLED_RUNTIMES_PATH;
|
|
266
275
|
if (runtimesPath) {
|
|
267
|
-
const { nodeBinDir, gitBinDir } = resolveBundledBinDirs(runtimesPath);
|
|
276
|
+
const { nodeBinDir, gitBinDir, uvBinDir } = resolveBundledBinDirs(runtimesPath);
|
|
268
277
|
// Activate the bundle only when BOTH node and git are present. A partial
|
|
269
278
|
// bundle (one tool present, the other missing — a botched extraction) is
|
|
270
279
|
// treated as NOT active so the full system fallback (fast-path + login-shell)
|
|
@@ -273,7 +282,7 @@ function resolveStartupPathBase() {
|
|
|
273
282
|
if (nodeBinDir && gitBinDir) {
|
|
274
283
|
const inherited = splitPath(process.env.PATH);
|
|
275
284
|
const inheritedSet = new Set(inherited);
|
|
276
|
-
const bundledDirs = [nodeBinDir, gitBinDir].filter((d) => !inheritedSet.has(d));
|
|
285
|
+
const bundledDirs = [nodeBinDir, gitBinDir, uvBinDir].filter((d) => !!d && !inheritedSet.has(d));
|
|
277
286
|
bundledDirs.forEach((d) => inheritedSet.add(d));
|
|
278
287
|
// Provider CLIs (claude/codex/gemini) are NEVER bundled — they always
|
|
279
288
|
// come from the system. On Windows their `.cmd` shims live in
|
|
@@ -464,6 +473,24 @@ exports.AUTH_ENV_VARS = [
|
|
|
464
473
|
'GOOGLE_CLOUD_LOCATION',
|
|
465
474
|
'GOOGLE_APPLICATION_CREDENTIALS',
|
|
466
475
|
];
|
|
476
|
+
const LOGIN_SHELL_ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
477
|
+
const loginShellEnvProbeCache = new Set();
|
|
478
|
+
function normalizeLoginShellEnvNames(names) {
|
|
479
|
+
const out = [];
|
|
480
|
+
for (const raw of names) {
|
|
481
|
+
const name = String(raw).trim();
|
|
482
|
+
if (!LOGIN_SHELL_ENV_NAME_RE.test(name) || out.includes(name))
|
|
483
|
+
continue;
|
|
484
|
+
out.push(name);
|
|
485
|
+
}
|
|
486
|
+
return out;
|
|
487
|
+
}
|
|
488
|
+
function buildLoginShellEnvCommand(names) {
|
|
489
|
+
// `printf` reuses POSIX positional `$VAR` expansion (works in sh/bash/zsh).
|
|
490
|
+
const fmt = ENV_BEGIN + names.map((v) => `${v}=%s\n`).join('') + ENV_END;
|
|
491
|
+
const refs = names.map((v) => `"$${v}"`).join(' ');
|
|
492
|
+
return `printf '${fmt}' ${refs}`;
|
|
493
|
+
}
|
|
467
494
|
/** Parse the `KEY=value` block emitted between the env sentinels. */
|
|
468
495
|
function parseLoginShellEnv(stdout) {
|
|
469
496
|
const begin = stdout.indexOf(ENV_BEGIN);
|
|
@@ -486,27 +513,23 @@ function parseLoginShellEnv(stdout) {
|
|
|
486
513
|
}
|
|
487
514
|
return out;
|
|
488
515
|
}
|
|
489
|
-
/**
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
|
|
495
|
-
* need to recover the gemini key. Async, fire-and-forget — must not block
|
|
496
|
-
* startup. No-op on Windows (GUI inherits user env) and in tests.
|
|
497
|
-
*/
|
|
498
|
-
async function augmentAuthEnvFromLoginShell(opts = {}) {
|
|
516
|
+
/** Backfill arbitrary, pre-validated environment variable names from the user's
|
|
517
|
+
* login shell into `process.env` (only when not already set — never override an
|
|
518
|
+
* explicit value). POSIX-only and runs regardless of bundle state, so macOS GUI
|
|
519
|
+
* launches can recover tokens exported in `.zshrc`/`.bashrc` without hardcoding
|
|
520
|
+
* every possible package-manager/provider variable. No-op on Windows and tests. */
|
|
521
|
+
async function augmentEnvFromLoginShell(names, opts = {}) {
|
|
499
522
|
if (process.platform === 'win32')
|
|
500
523
|
return;
|
|
501
524
|
if (process.env.NODE_ENV === 'test' || process.env.VITEST === 'true')
|
|
502
525
|
return;
|
|
526
|
+
const wanted = normalizeLoginShellEnvNames(names).filter((name) => !process.env[name]);
|
|
527
|
+
if (wanted.length === 0)
|
|
528
|
+
return;
|
|
503
529
|
const spawnFn = opts.spawnFn ?? child_process_1.spawn;
|
|
504
530
|
const timeoutMs = opts.timeoutMs ?? LOGIN_SHELL_TIMEOUT_MS;
|
|
505
531
|
const shell = process.env.SHELL || '/bin/sh';
|
|
506
|
-
|
|
507
|
-
const fmt = ENV_BEGIN + exports.AUTH_ENV_VARS.map((v) => `${v}=%s\n`).join('') + ENV_END;
|
|
508
|
-
const refs = exports.AUTH_ENV_VARS.map((v) => `"$${v}"`).join(' ');
|
|
509
|
-
const command = `printf '${fmt}' ${refs}`;
|
|
532
|
+
const command = buildLoginShellEnvCommand(wanted);
|
|
510
533
|
await new Promise((resolve) => {
|
|
511
534
|
let child;
|
|
512
535
|
try {
|
|
@@ -536,7 +559,7 @@ async function augmentAuthEnvFromLoginShell(opts = {}) {
|
|
|
536
559
|
child.on('error', finish);
|
|
537
560
|
child.on('close', () => {
|
|
538
561
|
const recovered = parseLoginShellEnv(stdout);
|
|
539
|
-
for (const key of
|
|
562
|
+
for (const key of wanted) {
|
|
540
563
|
const val = recovered[key];
|
|
541
564
|
// Backfill only when unset/empty — never clobber an explicit value.
|
|
542
565
|
if (val && !process.env[key])
|
|
@@ -546,6 +569,48 @@ async function augmentAuthEnvFromLoginShell(opts = {}) {
|
|
|
546
569
|
});
|
|
547
570
|
});
|
|
548
571
|
}
|
|
572
|
+
/** Synchronous, cached variant for spawn-time env assembly. It probes each
|
|
573
|
+
* missing name at most once per process to keep loops from paying the login
|
|
574
|
+
* shell timeout on every step. */
|
|
575
|
+
function augmentEnvFromLoginShellSync(names, opts = {}) {
|
|
576
|
+
if (process.platform === 'win32')
|
|
577
|
+
return;
|
|
578
|
+
if (process.env.NODE_ENV === 'test' || process.env.VITEST === 'true')
|
|
579
|
+
return;
|
|
580
|
+
const wanted = normalizeLoginShellEnvNames(names).filter((name) => !process.env[name] && !loginShellEnvProbeCache.has(name));
|
|
581
|
+
if (wanted.length === 0)
|
|
582
|
+
return;
|
|
583
|
+
for (const name of wanted)
|
|
584
|
+
loginShellEnvProbeCache.add(name);
|
|
585
|
+
const spawnSyncFn = opts.spawnSyncFn ?? child_process_1.spawnSync;
|
|
586
|
+
const timeoutMs = opts.timeoutMs ?? LOGIN_SHELL_TIMEOUT_MS;
|
|
587
|
+
const shell = process.env.SHELL || '/bin/sh';
|
|
588
|
+
const command = buildLoginShellEnvCommand(wanted);
|
|
589
|
+
let res;
|
|
590
|
+
try {
|
|
591
|
+
res = spawnSyncFn(shell, ['-l', '-i', '-c', command], {
|
|
592
|
+
encoding: 'utf8',
|
|
593
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
594
|
+
timeout: timeoutMs,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (res.error)
|
|
601
|
+
return;
|
|
602
|
+
const stdout = typeof res.stdout === 'string' ? res.stdout : res.stdout?.toString('utf8') ?? '';
|
|
603
|
+
const recovered = parseLoginShellEnv(stdout);
|
|
604
|
+
for (const key of wanted) {
|
|
605
|
+
const val = recovered[key];
|
|
606
|
+
if (val && !process.env[key])
|
|
607
|
+
process.env[key] = val;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
/** Startup backfill for the built-in provider auth variables. */
|
|
611
|
+
async function augmentAuthEnvFromLoginShell(opts = {}) {
|
|
612
|
+
await augmentEnvFromLoginShell(exports.AUTH_ENV_VARS, opts);
|
|
613
|
+
}
|
|
549
614
|
function mergeLoginShellPath(rawPath) {
|
|
550
615
|
const current = splitPath(process.env.PATH);
|
|
551
616
|
const currentSet = new Set(current);
|
|
@@ -582,4 +647,5 @@ function __resetPathResolverForTest() {
|
|
|
582
647
|
diagnostic = { pathSegments: [], pathSources: [], loginShellStatus: 'skipped' };
|
|
583
648
|
warnedLoginShell = false;
|
|
584
649
|
bundledRuntimesActive = false;
|
|
650
|
+
loginShellEnvProbeCache.clear();
|
|
585
651
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveWorktreeEnvPassthrough = resolveWorktreeEnvPassthrough;
|
|
4
|
+
exports.applyWorktreeEnvPassthrough = applyWorktreeEnvPassthrough;
|
|
5
|
+
const db_1 = require("./db");
|
|
6
|
+
const path_resolver_1 = require("./path-resolver");
|
|
7
|
+
/** Resolve the per-project env passthrough overlay for a spawn.
|
|
8
|
+
*
|
|
9
|
+
* The project setting stores names only. Values are read from `sourceEnv` at
|
|
10
|
+
* spawn time so credentials can rotate without touching SQLite. When the
|
|
11
|
+
* source is `process.env`, missing configured names get one bounded login-shell
|
|
12
|
+
* recovery attempt first; unresolved names are still ignored instead of
|
|
13
|
+
* blocking the run.
|
|
14
|
+
*/
|
|
15
|
+
function resolveWorktreeEnvPassthrough(db, sourceEnv = process.env) {
|
|
16
|
+
const out = {};
|
|
17
|
+
const names = (0, db_1.getProjectSettings)(db).worktreeEnvPassthrough;
|
|
18
|
+
if (sourceEnv === process.env)
|
|
19
|
+
(0, path_resolver_1.augmentEnvFromLoginShellSync)(names);
|
|
20
|
+
for (const name of names) {
|
|
21
|
+
const value = sourceEnv[name];
|
|
22
|
+
if (value !== undefined)
|
|
23
|
+
out[name] = value;
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function applyWorktreeEnvPassthrough(db, env, sourceEnv = process.env) {
|
|
28
|
+
const passthrough = resolveWorktreeEnvPassthrough(db, sourceEnv);
|
|
29
|
+
return Object.keys(passthrough).length === 0 ? env : { ...env, ...passthrough };
|
|
30
|
+
}
|
|
@@ -29,6 +29,7 @@ const transient_children_1 = require("./transient-children");
|
|
|
29
29
|
const telemetry_receiver_1 = require("./telemetry-receiver");
|
|
30
30
|
const artifact_registry_1 = require("./artifact-registry");
|
|
31
31
|
const workspace_resolution_1 = require("./workspace-resolution");
|
|
32
|
+
const project_env_1 = require("./project-env");
|
|
32
33
|
const workspace_manager_1 = require("./workspace-manager");
|
|
33
34
|
const ticket_store_1 = require("./ticket-store");
|
|
34
35
|
const jira_sync_manager_1 = require("./jira/jira-sync-manager");
|
|
@@ -689,9 +690,10 @@ class ProjectRegistry {
|
|
|
689
690
|
// `${ENV:-legacy}` defaults resolve inside the worktree, where only tracked
|
|
690
691
|
// files exist — still reads the real project state. SPECRAILS_REPO_DIR stays
|
|
691
692
|
// per-run (the worktree for isolated runs). Legacy projects keep process.env
|
|
692
|
-
// byte-identical
|
|
693
|
+
// byte-identical except for the explicit per-project passthrough overlay.
|
|
694
|
+
// See resolveLoopBaseEnv.
|
|
693
695
|
const loopRunManager = new loop_run_manager_1.LoopRunManager(db, boundBroadcast, (0, loop_executors_1.createLoopExecutors)({
|
|
694
|
-
env: () => (0, workspace_resolution_1.resolveLoopBaseEnv)({ slug: project.slug, path: project.path }),
|
|
696
|
+
env: () => (0, workspace_resolution_1.resolveLoopBaseEnv)({ slug: project.slug, path: project.path }, undefined, (0, project_env_1.applyWorktreeEnvPassthrough)(db, process.env)),
|
|
695
697
|
}));
|
|
696
698
|
const getTicketSpec = (ticketId) => {
|
|
697
699
|
try {
|