specrails-desktop 2.30.2 → 2.31.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/README.md +28 -24
- package/client/dist/assets/{ActivityFeedPage-9DZOvIcY.js → ActivityFeedPage-CAhFRUO4.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-DqyHbzk7.js → AgentBrowserCapture-Ct4G5lsE.js} +1 -1
- package/client/dist/assets/AgentModeCodePane-DQt9WIvu.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-CTIHMUKN.js → AgentModeJobsPane-podaJVl9.js} +1 -1
- package/client/dist/assets/AgentsPage-DTZdbW80.js +87 -0
- package/client/dist/assets/AnalyticsPage-BskgGPm0.js +1 -0
- package/client/dist/assets/CodePage-CvtYo1DU.js +2 -0
- package/client/dist/assets/{DesktopAnalyticsPage-CkXUFj3F.js → DesktopAnalyticsPage-Dnf99E8e.js} +1 -1
- package/client/dist/assets/{DocsDialog-PhdCJNVj.js → DocsDialog-DHi36rZ4.js} +1 -1
- package/client/dist/assets/{DocsPage-BY-wDthx.js → DocsPage-DIdVrQz1.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-C9I0A4KR.js → InteractiveJobComposer-BHeB2inv.js} +1 -1
- package/client/dist/assets/{JobDetailModal-I_RoQJV4.js → JobDetailModal-CF1SKUHH.js} +1 -1
- package/client/dist/assets/JobDetailPage-De3rY18h.js +1 -0
- package/client/dist/assets/{JobsPage-DcllM4Ip.js → JobsPage-CfAqqJTx.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-DGm_iuff.js → LoopBuilderPage-DSBMuTb3.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-BC-TeFdD.js → LoopPreviewModal-B-c37kA0.js} +1 -1
- package/client/dist/assets/LoopsPage-DjnNXdmm.js +1 -0
- package/client/dist/assets/{MinimizedChatsContext-Cs34FPTz.js → MinimizedChatsContext-DIsnQMVz.js} +1 -1
- package/client/dist/assets/PluginsPage-DA5pQ_6u.js +2 -0
- package/client/dist/assets/{ProjectSettingsDialog-nEs-ENrE.js → ProjectSettingsDialog-DA23PSi6.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-D4qW-J9B.js → TemplatePreviewModal-Cp7IQ-g2.js} +1 -1
- package/client/dist/assets/TicketDetailModal-BPD2i_0R.js +14 -0
- package/client/dist/assets/{Trans-CPISVji8.js → Trans-cWtExGkC.js} +1 -1
- package/client/dist/assets/addspec-4WmLJmBG.js +1 -0
- package/client/dist/assets/addspec-BRlk8jON.js +1 -0
- package/client/dist/assets/addspec-C7XRejU4.js +1 -0
- package/client/dist/assets/addspec-CHW9xf_X.js +1 -0
- package/client/dist/assets/addspec-DTmRrWrF.js +1 -0
- package/client/dist/assets/addspec-DhmnXER8.js +1 -0
- package/client/dist/assets/addspec-Di41DpYV.js +1 -0
- package/client/dist/assets/addspec-x3mizDxc.js +1 -0
- package/client/dist/assets/agent-BKsuIgIT.js +1 -0
- package/client/dist/assets/agent-Bs9NilfI.js +1 -0
- package/client/dist/assets/agent-C23h684q.js +1 -0
- package/client/dist/assets/agent-CqEjy3Z2.js +1 -0
- package/client/dist/assets/agent-D5DZTks1.js +1 -0
- package/client/dist/assets/agent-DE8hUYPl.js +1 -0
- package/client/dist/assets/agent-DsTuJGSc.js +1 -0
- package/client/dist/assets/agent-DwZM05-x.js +1 -0
- package/client/dist/assets/agents-45GbINfh.js +1 -0
- package/client/dist/assets/agents-BHp0_FQt.js +1 -0
- package/client/dist/assets/agents-Bvyi3y6R.js +1 -0
- package/client/dist/assets/agents-DMKFStHL.js +1 -0
- package/client/dist/assets/agents-DaYcxAr1.js +1 -0
- package/client/dist/assets/agents-IUOgqT5t.js +1 -0
- package/client/dist/assets/agents-ms7TwG16.js +1 -0
- package/client/dist/assets/agents-rzPnqoBR.js +1 -0
- package/client/dist/assets/agentstudio-8TQqBenW.js +1 -0
- package/client/dist/assets/agentstudio-B8Z_mmEX.js +1 -0
- package/client/dist/assets/agentstudio-BZR9YmLs.js +1 -0
- package/client/dist/assets/agentstudio-Bge9pAWb.js +1 -0
- package/client/dist/assets/agentstudio-Bz169Vzt.js +1 -0
- package/client/dist/assets/agentstudio-D6L2jpNi.js +1 -0
- package/client/dist/assets/agentstudio-K2-Z7CL0.js +1 -0
- package/client/dist/assets/agentstudio-Zmgzthv8.js +1 -0
- package/client/dist/assets/analytics-B8Qtptbl.js +1 -0
- package/client/dist/assets/analytics-BHqUazhP.js +1 -0
- package/client/dist/assets/analytics-BPptRY4f.js +1 -0
- package/client/dist/assets/analytics-BxDGOIwf.js +1 -0
- package/client/dist/assets/analytics-C9RaQcE3.js +1 -0
- package/client/dist/assets/analytics-CJIwqrEl.js +1 -0
- package/client/dist/assets/analytics-D_OZdQ9O.js +1 -0
- package/client/dist/assets/analytics-We6lUMI8.js +1 -0
- package/client/dist/assets/builder-3bygQtZi.js +1 -0
- package/client/dist/assets/builder-BiUG9Z5K.js +1 -0
- package/client/dist/assets/builder-CGRz_-zP.js +1 -0
- package/client/dist/assets/builder-CWYPw7Cw.js +1 -0
- package/client/dist/assets/builder-CX1437hF.js +1 -0
- package/client/dist/assets/{builder-CwA4T7gP.js → builder-Cowk9jtL.js} +1 -1
- package/client/dist/assets/builder-DwDexYRE.js +1 -0
- package/client/dist/assets/builder-skfnZ1uC.js +1 -0
- package/client/dist/assets/code-B0O8-p8k.js +1 -0
- package/client/dist/assets/code-B1VLZDhS.js +1 -0
- package/client/dist/assets/code-B86IMAfz.js +1 -0
- package/client/dist/assets/code-Bpj4Ddml.js +1 -0
- package/client/dist/assets/code-BykBZ5-e.js +1 -0
- package/client/dist/assets/code-CHPkOkDS.js +1 -0
- package/client/dist/assets/code-CkUuW4m9.js +1 -0
- package/client/dist/assets/code-cAqaqMTx.js +1 -0
- package/client/dist/assets/{dashboard-Dy2Um-US.js → dashboard-BVq6kRJe.js} +1 -1
- package/client/dist/assets/{dashboard-CHCEkds4.js → dashboard-BaKPPmLH.js} +1 -1
- package/client/dist/assets/{dashboard-CrlTV1ms.js → dashboard-Bm8e4zYG.js} +1 -1
- package/client/dist/assets/{dashboard-WhzOQw1S.js → dashboard-Bt6IesnM.js} +1 -1
- package/client/dist/assets/{dashboard-Cslu_GCH.js → dashboard-C_NQZle_.js} +1 -1
- package/client/dist/assets/{dashboard-DweT3Y4R.js → dashboard-CyZKoGoI.js} +1 -1
- package/client/dist/assets/{dashboard-DUU8-_Rg.js → dashboard-HIUdUBQ0.js} +1 -1
- package/client/dist/assets/{dashboard-Cqmmbh3l.js → dashboard-pm4Mr229.js} +1 -1
- package/client/dist/assets/{dist-js-jwoXo0YD.js → dist-js-BaP-xLOy.js} +1 -1
- package/client/dist/assets/{dist-js-Dj72W6qo.js → dist-js-CLlgOa5a.js} +1 -1
- package/client/dist/assets/format-command-C0D9IPmV.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-ByKli7Q_.js → formatDistanceToNow-DUyzS4rr.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-qrcBaLW1.js → getTimezoneOffsetInMilliseconds-BCIwmmTj.js} +1 -1
- package/client/dist/assets/{i18n-Bf9Ckd10.js → i18n-D21Pr8Sj.js} +2 -2
- package/client/dist/assets/index-BdSDUFnA.css +2 -0
- package/client/dist/assets/index-Ci4-JKDr.js +77 -0
- package/client/dist/assets/{jobs-CVFD8rar.js → jobs-BAls3bWq.js} +1 -1
- package/client/dist/assets/{jobs-DoSaFzAa.js → jobs-B_TeSQxb.js} +1 -1
- package/client/dist/assets/jobs-Bgq-PZkC.js +1 -0
- package/client/dist/assets/jobs-Bpks6doU.js +1 -0
- package/client/dist/assets/jobs-CN_zv4Gr.js +1 -0
- package/client/dist/assets/jobs-DUlWSYq2.js +1 -0
- package/client/dist/assets/{jobs-B-sDHEay.js → jobs-DhxKgWFq.js} +1 -1
- package/client/dist/assets/{jobs-lk1_Dq1U.js → jobs-SwYS2M-B.js} +1 -1
- package/client/dist/assets/nav-BHDiSIVu.js +1 -0
- package/client/dist/assets/nav-CMiYAq4i.js +1 -0
- package/client/dist/assets/nav-CqS7NrDW.js +1 -0
- package/client/dist/assets/nav-D-0M7xfv.js +1 -0
- package/client/dist/assets/nav-D65O6u9I.js +1 -0
- package/client/dist/assets/nav-D_LKYvBr.js +1 -0
- package/client/dist/assets/nav-FDTJmU79.js +1 -0
- package/client/dist/assets/nav-VaB-C4k5.js +1 -0
- package/client/dist/assets/provider-capabilities-BvoExILy.js +1 -0
- package/client/dist/assets/settings-BveOJoaI.js +1 -0
- package/client/dist/assets/settings-Cj1C6F5k.js +1 -0
- package/client/dist/assets/settings-CuVwOpe0.js +1 -0
- package/client/dist/assets/settings-D5X4DfRc.js +1 -0
- package/client/dist/assets/settings-DXwxe-BW.js +1 -0
- package/client/dist/assets/settings-Di81Ba_V.js +1 -0
- package/client/dist/assets/settings-jbLjCYC_.js +1 -0
- package/client/dist/assets/settings-wIZNPFnn.js +1 -0
- package/client/dist/assets/setup-BBiHEO7h.js +1 -0
- package/client/dist/assets/setup-BlTWhNIQ.js +1 -0
- package/client/dist/assets/setup-Bp2L_oiP.js +1 -0
- package/client/dist/assets/setup-CInYXp1-.js +1 -0
- package/client/dist/assets/{setup-CITtPjed.js → setup-CYH3bP6K.js} +1 -1
- package/client/dist/assets/setup-DwcQ921P.js +1 -0
- package/client/dist/assets/setup-PBLjuDSs.js +1 -0
- package/client/dist/assets/setup-bx6FtoBp.js +1 -0
- package/client/dist/assets/{spending-DMnCXy84.js → spending-BCyUxoOI.js} +1 -1
- package/client/dist/assets/{tickets-B8BuAaj5.js → tickets-BL9r1G0e.js} +1 -1
- package/client/dist/assets/{tickets-DpmdE8tv.js → tickets-B_NyHJ8f.js} +1 -1
- package/client/dist/assets/{tickets-B_yh4qQk.js → tickets-BarF1wuG.js} +1 -1
- package/client/dist/assets/{tickets-pAUJkDTu.js → tickets-CclVhpQA.js} +1 -1
- package/client/dist/assets/{tickets-D_yDquEw.js → tickets-CqqOHbli.js} +1 -1
- package/client/dist/assets/{tickets-CFk2n2Or.js → tickets-DCkFSiqt.js} +1 -1
- package/client/dist/assets/{tickets-DQp7JS2R.js → tickets-Djn3ccfT.js} +1 -1
- package/client/dist/assets/{tickets-Bs37scwY.js → tickets-MiHBPsy_.js} +1 -1
- package/client/dist/assets/{useBrowserCaptureSession-CkBJShUw.js → useBrowserCaptureSession-CkVCDzer.js} +1 -1
- package/client/dist/assets/{useDesktop-B4pSBIDd.js → useDesktop-DCP_MHp9.js} +1 -1
- package/client/dist/index.html +25 -24
- package/docs/README.md +7 -3
- package/docs/cli.md +3 -3
- package/docs/codex.md +51 -64
- package/docs/creating-specs.md +36 -4
- package/docs/customizing.md +5 -5
- package/docs/gemini.md +35 -47
- package/docs/getting-started.md +15 -9
- package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/de/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/de/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/de/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/de/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/de/integrations/1-ai-providers.md +29 -18
- package/docs/guide/de/integrations/2-plugins.md +3 -3
- package/docs/guide/de/integrations/6-agent-chat.md +8 -1
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +13 -13
- package/docs/guide/de/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/de/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +7 -1
- package/docs/guide/en/agents/4-custom-agents-catalog.md +17 -4
- package/docs/guide/en/getting-started/1-what-is-specrails.md +2 -1
- package/docs/guide/en/getting-started/2-installing-and-first-run.md +2 -1
- package/docs/guide/en/getting-started/4-the-dashboard-tour.md +5 -5
- package/docs/guide/en/insights/3-code-explorer.md +5 -0
- package/docs/guide/en/integrations/1-ai-providers.md +63 -22
- package/docs/guide/en/integrations/2-plugins.md +3 -3
- package/docs/guide/en/integrations/6-agent-chat.md +14 -2
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +19 -7
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +25 -11
- package/docs/guide/en/pipeline/5-the-loop-builder.md +7 -2
- package/docs/guide/en/specs/2-add-spec-quick-mode.md +10 -1
- package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/es/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/es/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/es/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/es/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/es/integrations/1-ai-providers.md +30 -18
- package/docs/guide/es/integrations/2-plugins.md +3 -3
- package/docs/guide/es/integrations/6-agent-chat.md +8 -1
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +15 -13
- package/docs/guide/es/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/es/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/fr/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/fr/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/fr/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/fr/integrations/1-ai-providers.md +29 -18
- package/docs/guide/fr/integrations/2-plugins.md +3 -3
- package/docs/guide/fr/integrations/6-agent-chat.md +8 -1
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +15 -13
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/fr/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/it/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/it/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/it/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/it/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/it/integrations/1-ai-providers.md +28 -18
- package/docs/guide/it/integrations/2-plugins.md +3 -3
- package/docs/guide/it/integrations/6-agent-chat.md +8 -1
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +13 -13
- package/docs/guide/it/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/it/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +6 -1
- package/docs/guide/ja/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/ja/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/ja/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/ja/integrations/1-ai-providers.md +28 -18
- package/docs/guide/ja/integrations/2-plugins.md +3 -3
- package/docs/guide/ja/integrations/6-agent-chat.md +7 -1
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +14 -13
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/ja/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/pt/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/pt/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/pt/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/pt/integrations/1-ai-providers.md +28 -18
- package/docs/guide/pt/integrations/2-plugins.md +3 -3
- package/docs/guide/pt/integrations/6-agent-chat.md +7 -1
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +2 -1
- package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +14 -13
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/pt/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +4 -1
- package/docs/guide/zh/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/zh/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/zh/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/zh/integrations/1-ai-providers.md +26 -18
- package/docs/guide/zh/integrations/2-plugins.md +3 -3
- package/docs/guide/zh/integrations/6-agent-chat.md +6 -1
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +2 -1
- package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +12 -12
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/zh/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/internals/README.md +4 -1
- package/docs/internals/adding-a-provider.md +32 -28
- package/docs/internals/api-reference.md +37 -13
- package/docs/internals/architecture.md +5 -5
- package/docs/internals/bundled-framework-build-plan.md +3 -3
- package/docs/internals/code-explorer-story.md +4 -0
- package/docs/internals/configuration.md +17 -5
- package/docs/internals/global-artifacts-alignment-contract.md +1 -1
- package/docs/internals/operations-runbook.md +1 -1
- package/docs/internals/profiles.md +37 -45
- package/docs/internals/project-builder.md +16 -0
- package/docs/kimi.md +291 -0
- package/docs/mcp.md +9 -2
- package/docs/platforms/macos.md +17 -7
- package/docs/platforms/windows.md +9 -4
- package/docs/running-pipelines.md +46 -12
- package/docs/terminal.md +1 -1
- package/docs/tracking-cost.md +25 -6
- package/package.json +4 -2
- package/server/dist/agent-chat-manager.js +74 -32
- package/server/dist/agent-chat-router.js +40 -17
- package/server/dist/agent-cwd-manager.js +14 -7
- package/server/dist/agent-generator.js +69 -85
- package/server/dist/agent-mcp-config.js +17 -11
- package/server/dist/agent-operator-prompt.js +29 -20
- package/server/dist/agent-refine-manager.js +58 -38
- package/server/dist/ai-invocations.js +65 -20
- package/server/dist/attachment-manager.js +49 -15
- package/server/dist/blueprint-chat-manager.js +41 -16
- package/server/dist/blueprint-router.js +40 -6
- package/server/dist/chat-manager.js +165 -89
- package/server/dist/code-explorer-router.js +26 -7
- package/server/dist/contract-refine-runner.js +146 -113
- package/server/dist/core-compat.js +57 -2
- package/server/dist/core-package.js +3 -3
- package/server/dist/core-update-manager.js +17 -1
- package/server/dist/db.js +60 -5
- package/server/dist/desktop-analytics.js +44 -6
- package/server/dist/desktop-router.js +127 -11
- package/server/dist/explore-cwd-manager.js +19 -3
- package/server/dist/feature-flags.js +1 -1
- package/server/dist/file-story-manager.js +3 -3
- package/server/dist/file-summary-generator.js +21 -15
- package/server/dist/file-summary-manager.js +6 -6
- package/server/dist/framework-manager.js +19 -2
- package/server/dist/framework-migration.js +27 -12
- package/server/dist/install-config-path.js +1 -1
- package/server/dist/interactive-job-session.js +8 -6
- package/server/dist/loop-command-catalog.js +27 -10
- package/server/dist/loop-executors.js +59 -27
- package/server/dist/loop-factory.js +2 -2
- package/server/dist/loop-run-manager.js +100 -39
- package/server/dist/loops-router.js +1 -1
- package/server/dist/mcp/guide.js +23 -16
- package/server/dist/mcp/tools/agents.js +47 -41
- package/server/dist/mcp/tools/analytics.js +2 -2
- package/server/dist/mcp/tools/loops.js +2 -2
- package/server/dist/mcp/tools/plugins.js +14 -9
- package/server/dist/mcp/tools/rails.js +12 -13
- package/server/dist/mcp/tools/support.js +1 -1
- package/server/dist/mobile/mobile-router.js +5 -1
- package/server/dist/offline-assemble.js +13 -1
- package/server/dist/path-resolver.js +2 -2
- package/server/dist/plugin-manager.js +391 -106
- package/server/dist/plugins/claude-md-mutation.js +5 -4
- package/server/dist/plugins/drift.js +32 -29
- package/server/dist/plugins/rail-integration.js +13 -2
- package/server/dist/plugins/serena/install.js +25 -17
- package/server/dist/plugins/serena/manifest.js +22 -5
- package/server/dist/plugins/serena/verify.js +46 -16
- package/server/dist/plugins-router.js +59 -8
- package/server/dist/profile-manager.js +148 -36
- package/server/dist/profiles-router.js +311 -82
- package/server/dist/project-registry.js +2 -2
- package/server/dist/project-router-chat.js +17 -8
- package/server/dist/project-router-helpers.js +93 -5
- package/server/dist/project-router-jobs.js +76 -12
- package/server/dist/project-router-loop-runs.js +42 -14
- package/server/dist/project-router-settings.js +31 -10
- package/server/dist/project-router-setup.js +4 -5
- package/server/dist/project-router-spending.js +15 -3
- package/server/dist/project-router-tickets.js +187 -181
- package/server/dist/project-router.js +1 -0
- package/server/dist/proposal-manager.js +125 -85
- package/server/dist/providers/claude-adapter.js +14 -0
- package/server/dist/providers/codex-adapter.js +20 -0
- package/server/dist/providers/gemini-adapter.js +14 -0
- package/server/dist/providers/index.js +18 -1
- package/server/dist/providers/kimi-adapter.js +361 -0
- package/server/dist/providers/kimi-skill-prompt.js +332 -0
- package/server/dist/providers/runtime.js +137 -0
- package/server/dist/queue-manager.js +103 -78
- package/server/dist/rail-isolated-launch.js +1 -1
- package/server/dist/rails-router.js +51 -38
- package/server/dist/result-event.js +5 -0
- package/server/dist/schemas/profile.v1.json +3 -4
- package/server/dist/setup-manager.js +222 -101
- package/server/dist/setup-prerequisites.js +7 -1
- package/server/dist/smash-runner.js +104 -57
- package/server/dist/spawn-lifecycle.js +18 -14
- package/server/dist/spec-launcher-manager.js +66 -54
- package/server/dist/spec-models.js +15 -6
- package/server/dist/spending.js +110 -17
- package/server/dist/util/cli-prompt.js +142 -5
- package/server/dist/util/stream-display.js +46 -0
- package/server/dist/workspace-manager.js +100 -37
- package/server/dist/worktree-manager.js +4 -0
- package/server/dist/worktree-overlay.js +12 -3
- package/client/dist/assets/AgentModeCodePane-BAYyFRmY.js +0 -2
- package/client/dist/assets/AgentsPage-DuyJuaFs.js +0 -86
- package/client/dist/assets/AnalyticsPage-BY7gtCx6.js +0 -1
- package/client/dist/assets/CodePage-vyQr40Vl.js +0 -2
- package/client/dist/assets/JobDetailPage-CU4FEnUC.js +0 -1
- package/client/dist/assets/LoopsPage-SqglnXlp.js +0 -1
- package/client/dist/assets/PluginsPage-B89S8o1a.js +0 -2
- package/client/dist/assets/TicketDetailModal-CI47Dq9E.js +0 -14
- package/client/dist/assets/addspec-BXjvkh5q.js +0 -1
- package/client/dist/assets/addspec-C4bwrmpi.js +0 -1
- package/client/dist/assets/addspec-CArHXUDM.js +0 -1
- package/client/dist/assets/addspec-DTKJcXiR.js +0 -1
- package/client/dist/assets/addspec-DbNBbrha.js +0 -1
- package/client/dist/assets/addspec-Rw8uVBC1.js +0 -1
- package/client/dist/assets/addspec-htHGZHOe.js +0 -1
- package/client/dist/assets/addspec-lr_LP6jk.js +0 -1
- package/client/dist/assets/agent-BABjRt0P.js +0 -1
- package/client/dist/assets/agent-BuUpgqVx.js +0 -1
- package/client/dist/assets/agent-CCDZesgU.js +0 -1
- package/client/dist/assets/agent-CVn64zWH.js +0 -1
- package/client/dist/assets/agent-CWeOny0E.js +0 -1
- package/client/dist/assets/agent-DOysmad_.js +0 -1
- package/client/dist/assets/agent-DSsjTBhn.js +0 -1
- package/client/dist/assets/agent-DlZMxfM9.js +0 -1
- package/client/dist/assets/agents-6ZOJXWVk.js +0 -1
- package/client/dist/assets/agents-BZ0XQKjg.js +0 -1
- package/client/dist/assets/agents-BqWklnHR.js +0 -1
- package/client/dist/assets/agents-Bz-thHkB.js +0 -1
- package/client/dist/assets/agents-CJclElx6.js +0 -1
- package/client/dist/assets/agents-D_l9L0VA.js +0 -1
- package/client/dist/assets/agents-DhG7mg6-.js +0 -1
- package/client/dist/assets/agents-u_C0jPB5.js +0 -1
- package/client/dist/assets/agentstudio-BF19hbXj.js +0 -1
- package/client/dist/assets/agentstudio-BIUe1Im2.js +0 -1
- package/client/dist/assets/agentstudio-BLGvxWBx.js +0 -1
- package/client/dist/assets/agentstudio-D9UKiWTj.js +0 -1
- package/client/dist/assets/agentstudio-Drs37nIM.js +0 -1
- package/client/dist/assets/agentstudio-nEnKkcXM.js +0 -1
- package/client/dist/assets/agentstudio-pyFcSBKy.js +0 -1
- package/client/dist/assets/agentstudio-zfTzQh3U.js +0 -1
- package/client/dist/assets/analytics-BC7mwR4s.js +0 -1
- package/client/dist/assets/analytics-BiNg8edP.js +0 -1
- package/client/dist/assets/analytics-C0s_T2GN.js +0 -1
- package/client/dist/assets/analytics-CdsAPAwZ.js +0 -1
- package/client/dist/assets/analytics-Cr1_q-lC.js +0 -1
- package/client/dist/assets/analytics-DZ8Aoq3I.js +0 -1
- package/client/dist/assets/analytics-IEsKZvpW.js +0 -1
- package/client/dist/assets/analytics-SRPf84OT.js +0 -1
- package/client/dist/assets/builder-BH8Fua-g.js +0 -1
- package/client/dist/assets/builder-BoV7DgKd.js +0 -1
- package/client/dist/assets/builder-Cc_Lf79C.js +0 -1
- package/client/dist/assets/builder-CwZ7Bme2.js +0 -1
- package/client/dist/assets/builder-CxW-JFsR.js +0 -1
- package/client/dist/assets/builder-D94lrV7x.js +0 -1
- package/client/dist/assets/builder-cqHpCyGa.js +0 -1
- package/client/dist/assets/code-76gS0IdL.js +0 -1
- package/client/dist/assets/code-BDXEM4ml.js +0 -1
- package/client/dist/assets/code-C1VwxlML.js +0 -1
- package/client/dist/assets/code-CnMZyJpr.js +0 -1
- package/client/dist/assets/code-D20ef9Cf.js +0 -1
- package/client/dist/assets/code-DdOXaWRY.js +0 -1
- package/client/dist/assets/code-Duohuf9l.js +0 -1
- package/client/dist/assets/code-geHmknB9.js +0 -1
- package/client/dist/assets/format-command-Dfa1o2d-.js +0 -1
- package/client/dist/assets/index-Dh9i4CnN.css +0 -2
- package/client/dist/assets/index-F9r8mWP_.js +0 -76
- package/client/dist/assets/jobs-BCtGfgUA.js +0 -1
- package/client/dist/assets/jobs-BHR_EnDI.js +0 -1
- package/client/dist/assets/jobs-BMbZJjJJ.js +0 -1
- package/client/dist/assets/jobs-DzSIR8V6.js +0 -1
- package/client/dist/assets/nav-BRJBxaGt.js +0 -1
- package/client/dist/assets/nav-CAGKv3_F.js +0 -1
- package/client/dist/assets/nav-CNskRJKQ.js +0 -1
- package/client/dist/assets/nav-DY93irBL.js +0 -1
- package/client/dist/assets/nav-DhjvYgqt.js +0 -1
- package/client/dist/assets/nav-Gb-r4wop.js +0 -1
- package/client/dist/assets/nav-TvumAFD2.js +0 -1
- package/client/dist/assets/nav-myFaXRHD.js +0 -1
- package/client/dist/assets/settings-B0ypw9uw.js +0 -1
- package/client/dist/assets/settings-B9ilOSYO.js +0 -1
- package/client/dist/assets/settings-BqlWHRHA.js +0 -1
- package/client/dist/assets/settings-D8dlqt4d.js +0 -1
- package/client/dist/assets/settings-DAZqjX2a.js +0 -1
- package/client/dist/assets/settings-DRh39v1S.js +0 -1
- package/client/dist/assets/settings-Mj1OJbfd.js +0 -1
- package/client/dist/assets/settings-lRReaSYm.js +0 -1
- package/client/dist/assets/setup-3832BJCo.js +0 -1
- package/client/dist/assets/setup-B8J-gECe.js +0 -1
- package/client/dist/assets/setup-BCIOxVsc.js +0 -1
- package/client/dist/assets/setup-BciLj_tU.js +0 -1
- package/client/dist/assets/setup-CTkE3cfh.js +0 -1
- package/client/dist/assets/setup-ajB4rYm-.js +0 -1
- package/client/dist/assets/setup-xFO1wNl0.js +0 -1
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.providerSupportsAgentStudioAutomation = providerSupportsAgentStudioAutomation;
|
|
6
7
|
exports.createProfilesRouter = createProfilesRouter;
|
|
7
8
|
const fs_1 = __importDefault(require("fs"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
@@ -11,27 +12,85 @@ const agent_generator_1 = require("./agent-generator");
|
|
|
11
12
|
const agent_refine_db_1 = require("./agent-refine-db");
|
|
12
13
|
const agent_refine_manager_1 = require("./agent-refine-manager");
|
|
13
14
|
const providers_1 = require("./providers");
|
|
15
|
+
const kimi_skill_prompt_1 = require("./providers/kimi-skill-prompt");
|
|
14
16
|
const profile_manager_1 = require("./profile-manager");
|
|
15
17
|
const workspace_resolution_1 = require("./workspace-resolution");
|
|
18
|
+
const project_router_helpers_1 = require("./project-router-helpers");
|
|
16
19
|
/**
|
|
17
20
|
* Relocate-artifacts gate: the dir whose `.specrails/{profiles,specrails-version}`
|
|
18
|
-
*
|
|
19
|
-
* Relocated ⇒ the workspace dir (where
|
|
21
|
+
* and provider-native roles catalog the profiles surface reads/writes.
|
|
22
|
+
* Relocated ⇒ the workspace dir (where Core assembled the roles catalog +
|
|
20
23
|
* `.specrails/profiles`); legacy ⇒ project.path (byte-identical to today).
|
|
21
24
|
*
|
|
22
|
-
* The
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* The roles catalog MUST follow this same root. Depending on the selected
|
|
26
|
+
* adapter, Core materializes file-based roles such as
|
|
27
|
+
* `<workspace>/.claude/agents/<id>.md`, or skill-based roles such as
|
|
28
|
+
* `<workspace>/.kimi-code/skills/<id>/SKILL.md`. Reading/writing beneath
|
|
29
|
+
* `project.path` would miss the execution catalog and violate repo immutability.
|
|
27
30
|
*/
|
|
28
31
|
function specRoot(project) {
|
|
29
32
|
const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
|
|
30
33
|
return exec.relocated && exec.workspaceDir ? exec.workspaceDir : project.path;
|
|
31
34
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
function installedProviders(project) {
|
|
36
|
+
const primary = project.provider ?? 'claude';
|
|
37
|
+
const configured = project.providers?.filter((provider) => typeof provider === 'string');
|
|
38
|
+
return configured?.length ? [...new Set([primary, ...configured])] : [primary];
|
|
39
|
+
}
|
|
40
|
+
function requestedProvider(req, project, bodyProvider) {
|
|
41
|
+
const queryProvider = typeof req.query.provider === 'string' ? req.query.provider : undefined;
|
|
42
|
+
const explicitBodyProvider = typeof bodyProvider === 'string' && bodyProvider.length > 0
|
|
43
|
+
? bodyProvider
|
|
44
|
+
: undefined;
|
|
45
|
+
if (queryProvider && explicitBodyProvider && queryProvider !== explicitBodyProvider) {
|
|
46
|
+
throw new profile_manager_1.ProfileValidationError([
|
|
47
|
+
`body provider '${explicitBodyProvider}' does not match requested provider '${queryProvider}'`,
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
const provider = queryProvider ?? explicitBodyProvider ?? project.provider ?? 'claude';
|
|
51
|
+
if (!(0, providers_1.hasAdapter)(provider) || !installedProviders(project).includes(provider)) {
|
|
52
|
+
throw new profile_manager_1.ProfileValidationError([
|
|
53
|
+
`provider '${provider}' is not installed for this project`,
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
return provider;
|
|
57
|
+
}
|
|
58
|
+
function projectAdapter(project, provider) {
|
|
59
|
+
return (0, providers_1.getAdapter)(provider ?? project.provider ?? 'claude');
|
|
60
|
+
}
|
|
61
|
+
function agentFile(project, agentId, provider) {
|
|
62
|
+
const root = specRoot(project);
|
|
63
|
+
const adapter = projectAdapter(project, provider);
|
|
64
|
+
return adapter.customRolePath?.(root, agentId)
|
|
65
|
+
?? path_1.default.join(root, adapter.projectDirName, 'agents', `${agentId}.md`);
|
|
66
|
+
}
|
|
67
|
+
/** The provider-native roles catalog directory. */
|
|
68
|
+
function agentsCatalogDir(project, provider) {
|
|
69
|
+
const probe = agentFile(project, '__catalog_probe__', provider);
|
|
70
|
+
// File-based roles use `<catalog>/<id>.md`; skill-based roles (Kimi) use
|
|
71
|
+
// `<catalog>/<id>/SKILL.md`.
|
|
72
|
+
return path_1.default.basename(probe) === 'SKILL.md'
|
|
73
|
+
? path_1.default.dirname(path_1.default.dirname(probe))
|
|
74
|
+
: path_1.default.dirname(probe);
|
|
75
|
+
}
|
|
76
|
+
function listAgentFiles(project, provider) {
|
|
77
|
+
const dir = agentsCatalogDir(project, provider);
|
|
78
|
+
if (!fs_1.default.existsSync(dir))
|
|
79
|
+
return [];
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
82
|
+
const id = entry.isDirectory()
|
|
83
|
+
? entry.name
|
|
84
|
+
: entry.isFile() && entry.name.endsWith('.md')
|
|
85
|
+
? entry.name.slice(0, -3)
|
|
86
|
+
: null;
|
|
87
|
+
if (!id)
|
|
88
|
+
continue;
|
|
89
|
+
const file = agentFile(project, id, provider);
|
|
90
|
+
if (fs_1.default.existsSync(file))
|
|
91
|
+
out.push({ id, file });
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
35
94
|
}
|
|
36
95
|
const AGENTS_SECTION_ENABLED = process.env.SPECRAILS_AGENTS_SECTION !== 'false';
|
|
37
96
|
function handleError(res, err) {
|
|
@@ -50,6 +109,35 @@ function handleError(res, err) {
|
|
|
50
109
|
const message = err instanceof Error ? err.message : 'unknown error';
|
|
51
110
|
res.status(500).json({ error: message });
|
|
52
111
|
}
|
|
112
|
+
/** Studio automation is safe when the CLI can enforce either a no-tools or a
|
|
113
|
+
* read-only boundary. Codex/Gemini use their verified native read-only modes;
|
|
114
|
+
* Kimi prompt mode exposes neither and therefore fails closed. */
|
|
115
|
+
function providerSupportsAgentStudioAutomation(provider) {
|
|
116
|
+
const adapter = (0, providers_1.getAdapter)(provider);
|
|
117
|
+
const policies = adapter.capabilities.toolPolicies ?? [];
|
|
118
|
+
return policies.includes('none') || policies.includes('read-only');
|
|
119
|
+
}
|
|
120
|
+
function requireSafeStudioPolicy(res, provider) {
|
|
121
|
+
if (providerSupportsAgentStudioAutomation(provider))
|
|
122
|
+
return true;
|
|
123
|
+
res.status(409).json({
|
|
124
|
+
error: 'provider_tool_policy_unsupported',
|
|
125
|
+
provider,
|
|
126
|
+
requiredPolicies: ['none', 'read-only'],
|
|
127
|
+
});
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
/** Kimi custom roles are provider-native Skills, so a non-empty markdown file
|
|
131
|
+
* is not enough: discovery requires valid identifying frontmatter. Reuse the
|
|
132
|
+
* execution parser directly so Profiles can never persist regex-accepted YAML
|
|
133
|
+
* that the Kimi headless path rejects later. The parser module is dependency
|
|
134
|
+
* leaf (fs/path/js-yaml only), so this direct import does not create a router /
|
|
135
|
+
* adapter cycle. */
|
|
136
|
+
function validateCustomRoleBody(provider, agentId, body) {
|
|
137
|
+
if (provider !== 'kimi')
|
|
138
|
+
return [];
|
|
139
|
+
return (0, kimi_skill_prompt_1.validateKimiRoleDocument)(body, agentId, `${agentId}/SKILL.md`);
|
|
140
|
+
}
|
|
53
141
|
function createProfilesRouter() {
|
|
54
142
|
const router = (0, express_1.Router)({ mergeParams: true });
|
|
55
143
|
function ctx(req) {
|
|
@@ -63,41 +151,75 @@ function createProfilesRouter() {
|
|
|
63
151
|
}
|
|
64
152
|
next();
|
|
65
153
|
});
|
|
154
|
+
// GET /api/projects/:projectId/profiles/context
|
|
155
|
+
// Provider-aware catalogs drive both Profiles and Agent Studio without
|
|
156
|
+
// hard-coding a Claude fallback into either client.
|
|
157
|
+
router.get('/context', (req, res) => {
|
|
158
|
+
try {
|
|
159
|
+
const { project } = ctx(req);
|
|
160
|
+
const providers = installedProviders(project);
|
|
161
|
+
const catalogs = Object.fromEntries(providers.map((provider) => {
|
|
162
|
+
const adapter = (0, providers_1.getAdapter)(provider);
|
|
163
|
+
return [provider, {
|
|
164
|
+
models: adapter.modelCatalog().map(({ value, label }) => ({ value, label })),
|
|
165
|
+
defaultModel: adapter.defaultModel(),
|
|
166
|
+
baselineAgents: [...adapter.baselineAgents()],
|
|
167
|
+
customModelAliases: adapter.capabilities.customModelAliases === true,
|
|
168
|
+
}];
|
|
169
|
+
}));
|
|
170
|
+
res.json({
|
|
171
|
+
primaryProvider: project.provider ?? providers[0] ?? 'claude',
|
|
172
|
+
providers,
|
|
173
|
+
catalogs,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
handleError(res, err);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
66
180
|
// POST /api/projects/:projectId/profiles/migrate-from-settings
|
|
67
181
|
// Seed a `default` profile from the agent frontmatter + legacy routing.
|
|
68
182
|
// Intended for first-time onboarding of existing projects.
|
|
69
183
|
router.post('/migrate-from-settings', (req, res) => {
|
|
70
184
|
try {
|
|
71
185
|
const { project, broadcast } = ctx(req);
|
|
72
|
-
const
|
|
186
|
+
const provider = requestedProvider(req, project);
|
|
187
|
+
const adapter = projectAdapter(project, provider);
|
|
188
|
+
const configuredKimiModels = provider === 'kimi'
|
|
189
|
+
? (0, project_router_helpers_1.readAgentModelSelection)({ ...project, provider })
|
|
190
|
+
: null;
|
|
191
|
+
const projectedKimiModels = configuredKimiModels
|
|
192
|
+
? new Map((0, project_router_helpers_1.readAgentModels)({ ...project, provider })
|
|
193
|
+
.map((entry) => [entry.name, entry.model]))
|
|
194
|
+
: null;
|
|
195
|
+
const agentsDir = agentsCatalogDir(project, provider);
|
|
73
196
|
if (!fs_1.default.existsSync(agentsDir)) {
|
|
74
|
-
res.status(400).json({ error:
|
|
197
|
+
res.status(400).json({ error: `no ${adapter.projectDirName} roles catalog found` });
|
|
75
198
|
return;
|
|
76
199
|
}
|
|
77
|
-
// Gather installed sr-*.md with their declared models.
|
|
78
200
|
const agents = [];
|
|
79
|
-
for (const entry of
|
|
80
|
-
if (!entry.
|
|
201
|
+
for (const entry of listAgentFiles(project, provider)) {
|
|
202
|
+
if (!entry.id.startsWith('sr-'))
|
|
81
203
|
continue;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
204
|
+
const id = entry.id;
|
|
205
|
+
let model = projectedKimiModels?.get(id) ?? adapter.defaultModel();
|
|
206
|
+
if (!projectedKimiModels) {
|
|
207
|
+
try {
|
|
208
|
+
const content = fs_1.default.readFileSync(entry.file, 'utf8');
|
|
209
|
+
const fm = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
210
|
+
if (fm) {
|
|
211
|
+
const m = fm[1].match(/^model:\s*(\S+)/m);
|
|
212
|
+
if (m && (0, providers_1.isModelAvailableForAdapter)(adapter, m[1]))
|
|
213
|
+
model = m[1];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// skip unreadable files
|
|
93
218
|
}
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// skip unreadable files
|
|
97
219
|
}
|
|
98
220
|
agents.push({ id, model });
|
|
99
221
|
}
|
|
100
|
-
const baseline = [
|
|
222
|
+
const baseline = [...adapter.baselineAgents()];
|
|
101
223
|
const missing = baseline.filter((id) => !agents.some((a) => a.id === id));
|
|
102
224
|
if (missing.length > 0) {
|
|
103
225
|
res.status(400).json({
|
|
@@ -120,23 +242,23 @@ function createProfilesRouter() {
|
|
|
120
242
|
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
121
243
|
...agents.filter((a) => pinnedLast.has(a.id)),
|
|
122
244
|
];
|
|
123
|
-
// Build the default profile mirroring legacy routing.
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
// against the right catalog on every future read.
|
|
128
|
-
const provider = project.provider ?? 'claude';
|
|
245
|
+
// Build the default profile mirroring legacy routing. Claude keeps its
|
|
246
|
+
// role-frontmatter projection. Kimi has no compatible `model:` field in
|
|
247
|
+
// SKILL.md, so its exact validated default/overrides come from the
|
|
248
|
+
// provider install config projected above.
|
|
129
249
|
const isClaude = provider === 'claude';
|
|
130
|
-
const fallbackModel =
|
|
250
|
+
const fallbackModel = adapter.defaultModel();
|
|
131
251
|
const profile = {
|
|
132
252
|
schemaVersion: 1,
|
|
133
253
|
name: 'default',
|
|
134
254
|
description: 'Baseline profile migrated from your current agent frontmatters.',
|
|
135
|
-
...(isClaude ? {} : {
|
|
136
|
-
orchestrator: {
|
|
255
|
+
...(!isClaude || installedProviders(project).length > 1 ? { provider } : {}),
|
|
256
|
+
orchestrator: {
|
|
257
|
+
model: configuredKimiModels?.defaultModel ?? fallbackModel,
|
|
258
|
+
},
|
|
137
259
|
agents: orderedAgents.map((a) => ({
|
|
138
260
|
id: a.id,
|
|
139
|
-
model:
|
|
261
|
+
model: a.model,
|
|
140
262
|
required: baseline.includes(a.id),
|
|
141
263
|
})),
|
|
142
264
|
routing: [
|
|
@@ -170,7 +292,8 @@ function createProfilesRouter() {
|
|
|
170
292
|
// Per-profile aggregated metrics over the requested time window.
|
|
171
293
|
router.get('/analytics', (req, res) => {
|
|
172
294
|
try {
|
|
173
|
-
const { db } = ctx(req);
|
|
295
|
+
const { db, project } = ctx(req);
|
|
296
|
+
const provider = requestedProvider(req, project);
|
|
174
297
|
const windowDays = Math.max(1, Math.min(365, parseInt((req.query.windowDays ?? '30'), 10) || 30));
|
|
175
298
|
const since = Date.now() - windowDays * 24 * 60 * 60 * 1000;
|
|
176
299
|
const rows = db
|
|
@@ -179,16 +302,30 @@ function createProfilesRouter() {
|
|
|
179
302
|
COUNT(*) AS jobs,
|
|
180
303
|
SUM(CASE WHEN j.status = 'completed' THEN 1 ELSE 0 END) AS succeeded,
|
|
181
304
|
AVG(j.duration_ms) AS avgDurationMs,
|
|
182
|
-
AVG(
|
|
183
|
-
|
|
184
|
-
|
|
305
|
+
AVG(CASE
|
|
306
|
+
WHEN j.tokens_in IS NOT NULL OR j.tokens_out IS NOT NULL
|
|
307
|
+
OR j.tokens_cache_read IS NOT NULL OR j.tokens_cache_create IS NOT NULL
|
|
308
|
+
THEN COALESCE(j.tokens_in, 0) + COALESCE(j.tokens_out, 0)
|
|
309
|
+
+ COALESCE(j.tokens_cache_read, 0) + COALESCE(j.tokens_cache_create, 0)
|
|
310
|
+
END) AS avgTokens,
|
|
311
|
+
AVG(j.total_cost_usd) AS avgCostUsd,
|
|
312
|
+
SUM(CASE WHEN j.tokens_in IS NOT NULL OR j.tokens_out IS NOT NULL
|
|
313
|
+
OR j.tokens_cache_read IS NOT NULL OR j.tokens_cache_create IS NOT NULL
|
|
314
|
+
THEN 1 ELSE 0 END) AS usageReportedJobs,
|
|
315
|
+
SUM(CASE WHEN j.tokens_in IS NULL AND j.tokens_out IS NULL
|
|
316
|
+
AND j.tokens_cache_read IS NULL AND j.tokens_cache_create IS NULL
|
|
317
|
+
THEN 1 ELSE 0 END) AS usageUnavailableJobs,
|
|
318
|
+
COUNT(j.total_cost_usd) AS pricedJobs,
|
|
319
|
+
SUM(CASE WHEN j.total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpricedJobs
|
|
185
320
|
FROM job_profiles jp
|
|
186
321
|
JOIN jobs j ON j.id = jp.job_id
|
|
187
322
|
WHERE jp.created_at >= ?
|
|
323
|
+
AND COALESCE(j.provider, 'claude') = ?
|
|
188
324
|
GROUP BY jp.profile_name
|
|
189
325
|
ORDER BY jobs DESC`)
|
|
190
|
-
.all(since);
|
|
326
|
+
.all(since, provider);
|
|
191
327
|
res.json({
|
|
328
|
+
provider,
|
|
192
329
|
windowDays,
|
|
193
330
|
rows: rows.map((r) => ({
|
|
194
331
|
profileName: r.profileName,
|
|
@@ -198,6 +335,10 @@ function createProfilesRouter() {
|
|
|
198
335
|
avgDurationMs: r.avgDurationMs,
|
|
199
336
|
avgTokens: r.avgTokens,
|
|
200
337
|
avgCostUsd: r.avgCostUsd,
|
|
338
|
+
usageReportedJobs: r.usageReportedJobs,
|
|
339
|
+
usageUnavailableJobs: r.usageUnavailableJobs,
|
|
340
|
+
pricedJobs: r.pricedJobs,
|
|
341
|
+
unpricedJobs: r.unpricedJobs,
|
|
201
342
|
})),
|
|
202
343
|
});
|
|
203
344
|
}
|
|
@@ -248,20 +389,19 @@ function createProfilesRouter() {
|
|
|
248
389
|
}
|
|
249
390
|
});
|
|
250
391
|
// GET /api/projects/:projectId/profiles/catalog
|
|
251
|
-
// List all
|
|
392
|
+
// List all roles in the selected provider-native catalog (sr-* + custom-*).
|
|
252
393
|
router.get('/catalog', (req, res) => {
|
|
253
394
|
try {
|
|
254
395
|
const { project } = ctx(req);
|
|
255
|
-
const
|
|
256
|
-
|
|
396
|
+
const provider = requestedProvider(req, project);
|
|
397
|
+
const files = listAgentFiles(project, provider);
|
|
398
|
+
if (files.length === 0) {
|
|
257
399
|
res.json({ agents: [] });
|
|
258
400
|
return;
|
|
259
401
|
}
|
|
260
402
|
const agents = [];
|
|
261
|
-
for (const
|
|
262
|
-
|
|
263
|
-
continue;
|
|
264
|
-
const id = file.slice(0, -'.md'.length);
|
|
403
|
+
for (const entry of files) {
|
|
404
|
+
const id = entry.id;
|
|
265
405
|
const kind = id.startsWith('sr-')
|
|
266
406
|
? 'upstream'
|
|
267
407
|
: id.startsWith('custom-')
|
|
@@ -272,9 +412,16 @@ function createProfilesRouter() {
|
|
|
272
412
|
let description;
|
|
273
413
|
let model;
|
|
274
414
|
try {
|
|
275
|
-
const body = fs_1.default.readFileSync(
|
|
276
|
-
|
|
277
|
-
|
|
415
|
+
const body = fs_1.default.readFileSync(entry.file, 'utf8');
|
|
416
|
+
if (provider === 'kimi') {
|
|
417
|
+
// Use the same js-yaml metadata parser as validation/execution so
|
|
418
|
+
// folded/literal descriptions and quoted scalars render correctly.
|
|
419
|
+
description = (0, kimi_skill_prompt_1.parseKimiSkillDocument)(body, entry.file).description;
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
const fm = body.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
423
|
+
if (!fm)
|
|
424
|
+
throw new Error(`Missing frontmatter in ${entry.file}`);
|
|
278
425
|
// description can be a long JSON-escaped string spanning multiple lines.
|
|
279
426
|
// Match from `description:` up to the next top-level YAML key or the end
|
|
280
427
|
// of the frontmatter block. Then unescape \n, \t, \" and strip surrounding
|
|
@@ -304,6 +451,9 @@ function createProfilesRouter() {
|
|
|
304
451
|
if (modelMatch)
|
|
305
452
|
model = modelMatch[1];
|
|
306
453
|
}
|
|
454
|
+
if (description && description.length > 280) {
|
|
455
|
+
description = description.slice(0, 277) + '…';
|
|
456
|
+
}
|
|
307
457
|
}
|
|
308
458
|
catch {
|
|
309
459
|
// ignore unreadable files
|
|
@@ -322,12 +472,13 @@ function createProfilesRouter() {
|
|
|
322
472
|
router.get('/catalog/:agentId', (req, res) => {
|
|
323
473
|
try {
|
|
324
474
|
const { project } = ctx(req);
|
|
475
|
+
const provider = requestedProvider(req, project);
|
|
325
476
|
const agentId = req.params.agentId;
|
|
326
477
|
if (!/^(sr|custom)-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
|
|
327
478
|
res.status(400).json({ error: 'invalid agent id' });
|
|
328
479
|
return;
|
|
329
480
|
}
|
|
330
|
-
const file =
|
|
481
|
+
const file = agentFile(project, agentId, provider);
|
|
331
482
|
if (!fs_1.default.existsSync(file)) {
|
|
332
483
|
res.status(404).json({ error: 'agent not found' });
|
|
333
484
|
return;
|
|
@@ -345,6 +496,7 @@ function createProfilesRouter() {
|
|
|
345
496
|
router.post('/catalog', (req, res) => {
|
|
346
497
|
try {
|
|
347
498
|
const { project, db, broadcast } = ctx(req);
|
|
499
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
348
500
|
const id = (req.body?.id ?? '').toString().trim();
|
|
349
501
|
const body = (req.body?.body ?? '').toString();
|
|
350
502
|
if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(id)) {
|
|
@@ -355,9 +507,13 @@ function createProfilesRouter() {
|
|
|
355
507
|
res.status(400).json({ error: 'body is required' });
|
|
356
508
|
return;
|
|
357
509
|
}
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
510
|
+
const roleErrors = validateCustomRoleBody(provider, id, body);
|
|
511
|
+
if (roleErrors.length > 0) {
|
|
512
|
+
res.status(400).json({ error: 'invalid_kimi_skill', details: roleErrors });
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const file = agentFile(project, id, provider);
|
|
516
|
+
fs_1.default.mkdirSync(path_1.default.dirname(file), { recursive: true });
|
|
361
517
|
if (fs_1.default.existsSync(file)) {
|
|
362
518
|
res.status(409).json({ error: `agent '${id}' already exists` });
|
|
363
519
|
return;
|
|
@@ -365,7 +521,8 @@ function createProfilesRouter() {
|
|
|
365
521
|
fs_1.default.writeFileSync(file, body, 'utf8');
|
|
366
522
|
// Record initial version
|
|
367
523
|
const nextVersion = 1;
|
|
368
|
-
db.prepare(`INSERT INTO agent_versions (agent_name, version, body, created_at)
|
|
524
|
+
db.prepare(`INSERT INTO agent_versions (provider, agent_name, version, body, created_at)
|
|
525
|
+
VALUES (?, ?, ?, ?, ?)`).run(provider, id, nextVersion, body, Date.now());
|
|
369
526
|
broadcast({ type: 'agent.changed', projectId: project.id, id });
|
|
370
527
|
res.status(201).json({ id, body, version: nextVersion });
|
|
371
528
|
}
|
|
@@ -378,6 +535,7 @@ function createProfilesRouter() {
|
|
|
378
535
|
router.patch('/catalog/:agentId', (req, res) => {
|
|
379
536
|
try {
|
|
380
537
|
const { project, db, broadcast } = ctx(req);
|
|
538
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
381
539
|
const agentId = req.params.agentId;
|
|
382
540
|
if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
|
|
383
541
|
res.status(403).json({ error: 'only custom-* agents can be edited from the app' });
|
|
@@ -388,17 +546,24 @@ function createProfilesRouter() {
|
|
|
388
546
|
res.status(400).json({ error: 'body is required' });
|
|
389
547
|
return;
|
|
390
548
|
}
|
|
391
|
-
const
|
|
549
|
+
const roleErrors = validateCustomRoleBody(provider, agentId, body);
|
|
550
|
+
if (roleErrors.length > 0) {
|
|
551
|
+
res.status(400).json({ error: 'invalid_kimi_skill', details: roleErrors });
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const file = agentFile(project, agentId, provider);
|
|
392
555
|
if (!fs_1.default.existsSync(file)) {
|
|
393
556
|
res.status(404).json({ error: 'agent not found' });
|
|
394
557
|
return;
|
|
395
558
|
}
|
|
396
559
|
fs_1.default.writeFileSync(file, body, 'utf8');
|
|
397
560
|
const maxVersion = db
|
|
398
|
-
.prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM agent_versions
|
|
399
|
-
|
|
561
|
+
.prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM agent_versions
|
|
562
|
+
WHERE provider = ? AND agent_name = ?`)
|
|
563
|
+
.get(provider, agentId).v;
|
|
400
564
|
const nextVersion = maxVersion + 1;
|
|
401
|
-
db.prepare(`INSERT INTO agent_versions (agent_name, version, body, created_at)
|
|
565
|
+
db.prepare(`INSERT INTO agent_versions (provider, agent_name, version, body, created_at)
|
|
566
|
+
VALUES (?, ?, ?, ?, ?)`).run(provider, agentId, nextVersion, body, Date.now());
|
|
402
567
|
broadcast({ type: 'agent.changed', projectId: project.id, id: agentId });
|
|
403
568
|
res.json({ id: agentId, body, version: nextVersion });
|
|
404
569
|
}
|
|
@@ -411,17 +576,24 @@ function createProfilesRouter() {
|
|
|
411
576
|
router.delete('/catalog/:agentId', (req, res) => {
|
|
412
577
|
try {
|
|
413
578
|
const { project, broadcast } = ctx(req);
|
|
579
|
+
const provider = requestedProvider(req, project);
|
|
414
580
|
const agentId = req.params.agentId;
|
|
415
581
|
if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
|
|
416
582
|
res.status(403).json({ error: 'only custom-* agents can be deleted' });
|
|
417
583
|
return;
|
|
418
584
|
}
|
|
419
|
-
const file =
|
|
585
|
+
const file = agentFile(project, agentId, provider);
|
|
420
586
|
if (!fs_1.default.existsSync(file)) {
|
|
421
587
|
res.status(404).json({ error: 'agent not found' });
|
|
422
588
|
return;
|
|
423
589
|
}
|
|
424
590
|
fs_1.default.unlinkSync(file);
|
|
591
|
+
try {
|
|
592
|
+
const parent = path_1.default.dirname(file);
|
|
593
|
+
if (parent !== agentsCatalogDir(project, provider) && fs_1.default.readdirSync(parent).length === 0)
|
|
594
|
+
fs_1.default.rmdirSync(parent);
|
|
595
|
+
}
|
|
596
|
+
catch { /* best-effort */ }
|
|
425
597
|
broadcast({ type: 'agent.changed', projectId: project.id, id: agentId, deleted: true });
|
|
426
598
|
res.json({ ok: true });
|
|
427
599
|
}
|
|
@@ -436,6 +608,9 @@ function createProfilesRouter() {
|
|
|
436
608
|
router.post('/catalog/test', async (req, res) => {
|
|
437
609
|
try {
|
|
438
610
|
const { project, db, broadcast } = ctx(req);
|
|
611
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
612
|
+
if (!requireSafeStudioPolicy(res, provider))
|
|
613
|
+
return;
|
|
439
614
|
const agentId = (req.body?.agentId ?? '').toString().trim() || 'draft';
|
|
440
615
|
const draftBody = (req.body?.draftBody ?? '').toString();
|
|
441
616
|
const sampleTask = (req.body?.sampleTask ?? '').toString().trim();
|
|
@@ -450,10 +625,12 @@ function createProfilesRouter() {
|
|
|
450
625
|
const result = await (0, agent_generator_1.testCustomAgent)(project.path, {
|
|
451
626
|
draftBody,
|
|
452
627
|
sampleTask,
|
|
628
|
+
providerId: provider,
|
|
453
629
|
record: { db, projectId: project.id, surfaceRefId: agentId, broadcast },
|
|
454
630
|
});
|
|
455
|
-
db.prepare(`INSERT INTO agent_tests
|
|
456
|
-
|
|
631
|
+
db.prepare(`INSERT INTO agent_tests
|
|
632
|
+
(provider, agent_name, draft_hash, sample_task_id, tokens, duration_ms, output, created_at)
|
|
633
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(provider, agentId, result.draftHash, null, result.tokens, result.durationMs, result.output, Date.now());
|
|
457
634
|
res.json(result);
|
|
458
635
|
}
|
|
459
636
|
catch (err) {
|
|
@@ -467,6 +644,9 @@ function createProfilesRouter() {
|
|
|
467
644
|
router.post('/catalog/generate', async (req, res) => {
|
|
468
645
|
try {
|
|
469
646
|
const { project, db, broadcast } = ctx(req);
|
|
647
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
648
|
+
if (!requireSafeStudioPolicy(res, provider))
|
|
649
|
+
return;
|
|
470
650
|
const name = (req.body?.name ?? '').toString().trim();
|
|
471
651
|
const description = (req.body?.description ?? '').toString().trim();
|
|
472
652
|
if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(name)) {
|
|
@@ -480,6 +660,7 @@ function createProfilesRouter() {
|
|
|
480
660
|
const draft = await (0, agent_generator_1.generateCustomAgent)(project.path, {
|
|
481
661
|
name,
|
|
482
662
|
description,
|
|
663
|
+
providerId: provider,
|
|
483
664
|
record: { db, projectId: project.id, surfaceRefId: name, broadcast },
|
|
484
665
|
});
|
|
485
666
|
res.json({ draft });
|
|
@@ -493,7 +674,20 @@ function createProfilesRouter() {
|
|
|
493
674
|
router.post('/catalog/:agentId/refine', async (req, res) => {
|
|
494
675
|
try {
|
|
495
676
|
const ctxObj = ctx(req);
|
|
496
|
-
const { agentRefineManager } = ctxObj;
|
|
677
|
+
const { agentRefineManager, project } = ctxObj;
|
|
678
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
679
|
+
if (!requireSafeStudioPolicy(res, provider))
|
|
680
|
+
return;
|
|
681
|
+
// AgentRefineManager owns the primary provider's native session store.
|
|
682
|
+
// Cross-provider sessions need a provider column/migration before they
|
|
683
|
+
// can be resumed safely; reject instead of silently using the primary.
|
|
684
|
+
if (provider !== (project.provider ?? 'claude')) {
|
|
685
|
+
res.status(409).json({
|
|
686
|
+
error: 'provider_refine_manager_unavailable',
|
|
687
|
+
provider,
|
|
688
|
+
});
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
497
691
|
const agentId = req.params.agentId;
|
|
498
692
|
if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
|
|
499
693
|
res.status(400).json({ error: 'not_a_custom_agent' });
|
|
@@ -528,7 +722,17 @@ function createProfilesRouter() {
|
|
|
528
722
|
});
|
|
529
723
|
router.post('/catalog/:agentId/refine/:refineId/turn', async (req, res) => {
|
|
530
724
|
try {
|
|
531
|
-
const { agentRefineManager, db } = ctx(req);
|
|
725
|
+
const { agentRefineManager, db, project } = ctx(req);
|
|
726
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
727
|
+
if (!requireSafeStudioPolicy(res, provider))
|
|
728
|
+
return;
|
|
729
|
+
if (provider !== (project.provider ?? 'claude')) {
|
|
730
|
+
res.status(409).json({
|
|
731
|
+
error: 'provider_refine_manager_unavailable',
|
|
732
|
+
provider,
|
|
733
|
+
});
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
532
736
|
const refineId = req.params.refineId;
|
|
533
737
|
const instruction = (req.body?.instruction ?? '').toString().trim();
|
|
534
738
|
if (!instruction) {
|
|
@@ -661,12 +865,13 @@ function createProfilesRouter() {
|
|
|
661
865
|
// GET /api/projects/:projectId/profiles/catalog/:agentId/versions
|
|
662
866
|
router.get('/catalog/:agentId/versions', (req, res) => {
|
|
663
867
|
try {
|
|
664
|
-
const { db } = ctx(req);
|
|
868
|
+
const { db, project } = ctx(req);
|
|
869
|
+
const provider = requestedProvider(req, project);
|
|
665
870
|
const agentId = req.params.agentId;
|
|
666
871
|
const rows = db
|
|
667
872
|
.prepare(`SELECT version, body, created_at AS createdAt FROM agent_versions
|
|
668
|
-
WHERE agent_name = ? ORDER BY version DESC`)
|
|
669
|
-
.all(agentId);
|
|
873
|
+
WHERE provider = ? AND agent_name = ? ORDER BY version DESC`)
|
|
874
|
+
.all(provider, agentId);
|
|
670
875
|
res.json({ versions: rows });
|
|
671
876
|
}
|
|
672
877
|
catch (err) {
|
|
@@ -677,7 +882,12 @@ function createProfilesRouter() {
|
|
|
677
882
|
router.get('/', (req, res) => {
|
|
678
883
|
try {
|
|
679
884
|
const { project } = ctx(req);
|
|
680
|
-
|
|
885
|
+
const provider = requestedProvider(req, project);
|
|
886
|
+
const primary = project.provider ?? 'claude';
|
|
887
|
+
res.json({
|
|
888
|
+
profiles: (0, profile_manager_1.listProfiles)(specRoot(project), primary)
|
|
889
|
+
.filter((profile) => profile.provider === provider),
|
|
890
|
+
});
|
|
681
891
|
}
|
|
682
892
|
catch (err) {
|
|
683
893
|
handleError(res, err);
|
|
@@ -687,8 +897,9 @@ function createProfilesRouter() {
|
|
|
687
897
|
router.get('/resolve', (req, res) => {
|
|
688
898
|
try {
|
|
689
899
|
const { project } = ctx(req);
|
|
900
|
+
const provider = requestedProvider(req, project);
|
|
690
901
|
const explicit = typeof req.query.profile === 'string' ? req.query.profile : undefined;
|
|
691
|
-
const resolved = (0, profile_manager_1.resolveProfile)(specRoot(project), explicit,
|
|
902
|
+
const resolved = (0, profile_manager_1.resolveProfile)(specRoot(project), explicit, provider);
|
|
692
903
|
if (!resolved) {
|
|
693
904
|
res.json({ resolved: null });
|
|
694
905
|
return;
|
|
@@ -703,8 +914,15 @@ function createProfilesRouter() {
|
|
|
703
914
|
router.post('/', (req, res) => {
|
|
704
915
|
try {
|
|
705
916
|
const { project, broadcast } = ctx(req);
|
|
706
|
-
const
|
|
707
|
-
(
|
|
917
|
+
const input = req.body;
|
|
918
|
+
const provider = requestedProvider(req, project, input?.provider);
|
|
919
|
+
const body = {
|
|
920
|
+
...input,
|
|
921
|
+
...(input?.provider || provider !== 'claude' || installedProviders(project).length > 1
|
|
922
|
+
? { provider }
|
|
923
|
+
: {}),
|
|
924
|
+
};
|
|
925
|
+
(0, profile_manager_1.createProfile)(specRoot(project), body, provider);
|
|
708
926
|
broadcast({ type: 'profile.changed', projectId: project.id, name: body.name });
|
|
709
927
|
res.status(201).json({ profile: body });
|
|
710
928
|
}
|
|
@@ -716,12 +934,13 @@ function createProfilesRouter() {
|
|
|
716
934
|
router.post('/:name/duplicate', (req, res) => {
|
|
717
935
|
try {
|
|
718
936
|
const { project, broadcast } = ctx(req);
|
|
937
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
719
938
|
const newName = (req.body?.name ?? '').toString();
|
|
720
939
|
if (!newName) {
|
|
721
940
|
res.status(400).json({ error: "body field 'name' is required" });
|
|
722
941
|
return;
|
|
723
942
|
}
|
|
724
|
-
const copy = (0, profile_manager_1.duplicateProfile)(specRoot(project), req.params.name, newName,
|
|
943
|
+
const copy = (0, profile_manager_1.duplicateProfile)(specRoot(project), req.params.name, newName, provider);
|
|
725
944
|
broadcast({ type: 'profile.changed', projectId: project.id, name: newName });
|
|
726
945
|
res.status(201).json({ profile: copy });
|
|
727
946
|
}
|
|
@@ -733,12 +952,13 @@ function createProfilesRouter() {
|
|
|
733
952
|
router.post('/:name/rename', (req, res) => {
|
|
734
953
|
try {
|
|
735
954
|
const { project, broadcast } = ctx(req);
|
|
955
|
+
const provider = requestedProvider(req, project, req.body?.provider);
|
|
736
956
|
const newName = (req.body?.name ?? '').toString();
|
|
737
957
|
if (!newName) {
|
|
738
958
|
res.status(400).json({ error: "body field 'name' is required" });
|
|
739
959
|
return;
|
|
740
960
|
}
|
|
741
|
-
const renamed = (0, profile_manager_1.renameProfile)(specRoot(project), req.params.name, newName,
|
|
961
|
+
const renamed = (0, profile_manager_1.renameProfile)(specRoot(project), req.params.name, newName, provider);
|
|
742
962
|
broadcast({ type: 'profile.changed', projectId: project.id, name: newName });
|
|
743
963
|
res.json({ profile: renamed });
|
|
744
964
|
}
|
|
@@ -750,10 +970,11 @@ function createProfilesRouter() {
|
|
|
750
970
|
router.get('/:name', (req, res) => {
|
|
751
971
|
try {
|
|
752
972
|
const { project } = ctx(req);
|
|
973
|
+
const provider = requestedProvider(req, project);
|
|
753
974
|
// Non-validating read so a profile that drifted invalid against the current
|
|
754
975
|
// catalog can still be opened + repaired in the editor (getProfile threw and
|
|
755
976
|
// locked it out). The body + validation errors are both returned.
|
|
756
|
-
const { profile, valid, errors } = (0, profile_manager_1.getProfileRaw)(specRoot(project), req.params.name,
|
|
977
|
+
const { profile, valid, errors } = (0, profile_manager_1.getProfileRaw)(specRoot(project), req.params.name, provider);
|
|
757
978
|
res.json({ profile, valid, validationErrors: errors });
|
|
758
979
|
}
|
|
759
980
|
catch (err) {
|
|
@@ -764,12 +985,19 @@ function createProfilesRouter() {
|
|
|
764
985
|
router.patch('/:name', (req, res) => {
|
|
765
986
|
try {
|
|
766
987
|
const { project, broadcast } = ctx(req);
|
|
767
|
-
const
|
|
988
|
+
const input = req.body;
|
|
989
|
+
const provider = requestedProvider(req, project, input?.provider);
|
|
990
|
+
const body = {
|
|
991
|
+
...input,
|
|
992
|
+
...(input?.provider || provider !== 'claude' || installedProviders(project).length > 1
|
|
993
|
+
? { provider }
|
|
994
|
+
: {}),
|
|
995
|
+
};
|
|
768
996
|
if (body.name !== req.params.name) {
|
|
769
997
|
res.status(400).json({ error: "body.name must match path parameter (use /rename to change name)" });
|
|
770
998
|
return;
|
|
771
999
|
}
|
|
772
|
-
(0, profile_manager_1.updateProfile)(specRoot(project), body,
|
|
1000
|
+
(0, profile_manager_1.updateProfile)(specRoot(project), body, provider);
|
|
773
1001
|
broadcast({ type: 'profile.changed', projectId: project.id, name: body.name });
|
|
774
1002
|
res.json({ profile: body });
|
|
775
1003
|
}
|
|
@@ -781,7 +1009,8 @@ function createProfilesRouter() {
|
|
|
781
1009
|
router.delete('/:name', (req, res) => {
|
|
782
1010
|
try {
|
|
783
1011
|
const { project, broadcast } = ctx(req);
|
|
784
|
-
(
|
|
1012
|
+
const provider = requestedProvider(req, project);
|
|
1013
|
+
(0, profile_manager_1.deleteProfile)(specRoot(project), req.params.name, provider);
|
|
785
1014
|
broadcast({ type: 'profile.changed', projectId: project.id, name: req.params.name, deleted: true });
|
|
786
1015
|
res.json({ ok: true });
|
|
787
1016
|
}
|