circuschief 0.1.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/package.json +33 -0
- package/packages/server/bin/cli.js +4 -0
- package/packages/server/src/agents/AgentGateway.js +64 -0
- package/packages/server/src/agents/BaseAgent.js +41 -0
- package/packages/server/src/agents/LoggingAgentWrapper.js +73 -0
- package/packages/server/src/agents/adapters/ClaudeCodeAdapter.js +33 -0
- package/packages/server/src/agents/adapters/CodexAdapter.js +26 -0
- package/packages/server/src/agents/types.js +43 -0
- package/packages/server/src/agents/vcr/CassetteStore.js +111 -0
- package/packages/server/src/agents/vcr/VCRAgentAdapter.js +126 -0
- package/packages/server/src/agents/vcr/VCRSummaryWrapper.js +71 -0
- package/packages/server/src/api/canvas-helpers.js +249 -0
- package/packages/server/src/api/canvas-trash-routes.js +205 -0
- package/packages/server/src/api/canvas.js +331 -0
- package/packages/server/src/api/commandButtons.js +312 -0
- package/packages/server/src/api/commands.js +169 -0
- package/packages/server/src/api/filesystem.js +62 -0
- package/packages/server/src/api/git.js +85 -0
- package/packages/server/src/api/index.js +44 -0
- package/packages/server/src/api/kanban.js +342 -0
- package/packages/server/src/api/metrics.js +194 -0
- package/packages/server/src/api/projects-helpers.js +43 -0
- package/packages/server/src/api/projects-session-helpers.js +295 -0
- package/packages/server/src/api/projects.js +384 -0
- package/packages/server/src/api/providers.js +249 -0
- package/packages/server/src/api/quickResponses.js +129 -0
- package/packages/server/src/api/sessions-archive.js +69 -0
- package/packages/server/src/api/sessions-commands.js +220 -0
- package/packages/server/src/api/sessions-conversations.js +168 -0
- package/packages/server/src/api/sessions-draft.js +72 -0
- package/packages/server/src/api/sessions-lifecycle.js +190 -0
- package/packages/server/src/api/sessions-messages.js +141 -0
- package/packages/server/src/api/sessions-notes.js +51 -0
- package/packages/server/src/api/sessions-patch.js +252 -0
- package/packages/server/src/api/sessions-streaming.js +86 -0
- package/packages/server/src/api/sessions.js +269 -0
- package/packages/server/src/api/settings.js +194 -0
- package/packages/server/src/api/templates.js +63 -0
- package/packages/server/src/app.js +51 -0
- package/packages/server/src/database.js +58 -0
- package/packages/server/src/db/AgentCallLogRepository.js +322 -0
- package/packages/server/src/db/AttachmentRepository.js +191 -0
- package/packages/server/src/db/BaseRepository.js +39 -0
- package/packages/server/src/db/CanvasItemRepository.js +315 -0
- package/packages/server/src/db/CommandButtonRepository.js +75 -0
- package/packages/server/src/db/CommandRunRepository.js +219 -0
- package/packages/server/src/db/ConversationRepository.js +379 -0
- package/packages/server/src/db/DatabaseManager.js +91 -0
- package/packages/server/src/db/KanbanBoardRepository.js +92 -0
- package/packages/server/src/db/KanbanCardRepository.js +286 -0
- package/packages/server/src/db/KanbanLaneRepository.js +279 -0
- package/packages/server/src/db/MessageRepository.js +156 -0
- package/packages/server/src/db/ProjectDefaultsRepository.js +173 -0
- package/packages/server/src/db/ProjectRepository.js +110 -0
- package/packages/server/src/db/ProviderRepository.js +307 -0
- package/packages/server/src/db/QuickResponseRepository.js +186 -0
- package/packages/server/src/db/SessionNoteRepository.js +60 -0
- package/packages/server/src/db/SessionRepository.js +314 -0
- package/packages/server/src/db/SessionSummaryRepository.js +200 -0
- package/packages/server/src/db/SessionTemplateRepository.js +171 -0
- package/packages/server/src/db/SettingsRepository.js +211 -0
- package/packages/server/src/db/TodoRepository.js +132 -0
- package/packages/server/src/db/WorkLogRepository.js +122 -0
- package/packages/server/src/db/conversation-helpers.js +119 -0
- package/packages/server/src/db/index.js +100 -0
- package/packages/server/src/db/migrations/canvasItemsMigrations.js +109 -0
- package/packages/server/src/db/migrations/conversationsMigrations.js +183 -0
- package/packages/server/src/db/migrations/index.js +199 -0
- package/packages/server/src/db/migrations/kanbanMigrations.js +99 -0
- package/packages/server/src/db/migrations/migrationUtils.js +55 -0
- package/packages/server/src/db/migrations/miscMigrations.js +242 -0
- package/packages/server/src/db/migrations/projectsMigrations.js +95 -0
- package/packages/server/src/db/migrations/sessionsMigrations.js +282 -0
- package/packages/server/src/db/session-helpers.js +150 -0
- package/packages/server/src/index.js +106 -0
- package/packages/server/src/logger.js +22 -0
- package/packages/server/src/middleware/sessionLookup.js +57 -0
- package/packages/server/src/middleware/upload.js +94 -0
- package/packages/server/src/schema.sql +363 -0
- package/packages/server/src/services/agentCallLogger.js +116 -0
- package/packages/server/src/services/canvasStore.js +56 -0
- package/packages/server/src/services/childSessionContext.js +61 -0
- package/packages/server/src/services/commandRunner.js +422 -0
- package/packages/server/src/services/conversationContext.js +72 -0
- package/packages/server/src/services/diffService.js +172 -0
- package/packages/server/src/services/draftSessionService.js +181 -0
- package/packages/server/src/services/encryption.js +134 -0
- package/packages/server/src/services/ghService.js +169 -0
- package/packages/server/src/services/gitService.js +520 -0
- package/packages/server/src/services/gitSessionSetup.js +48 -0
- package/packages/server/src/services/hookService.js +60 -0
- package/packages/server/src/services/kanbanService.js +262 -0
- package/packages/server/src/services/kanbanTriggers.js +273 -0
- package/packages/server/src/services/nodeSpawnHelper.js +63 -0
- package/packages/server/src/services/prStatusService.js +204 -0
- package/packages/server/src/services/prUrlService.js +224 -0
- package/packages/server/src/services/providerTestService.js +81 -0
- package/packages/server/src/services/scheduleService.js +110 -0
- package/packages/server/src/services/schedulerService.js +281 -0
- package/packages/server/src/services/sessionDuplicator.js +63 -0
- package/packages/server/src/services/sessionErrors.js +173 -0
- package/packages/server/src/services/sessionExecution.js +378 -0
- package/packages/server/src/services/sessionManager.js +356 -0
- package/packages/server/src/services/sessionPrompts.js +427 -0
- package/packages/server/src/services/sessionProvider.js +107 -0
- package/packages/server/src/services/slashCommandDiscovery.js +258 -0
- package/packages/server/src/services/slashCommandPluginDiscovery.js +216 -0
- package/packages/server/src/services/slashCommandService.js +306 -0
- package/packages/server/src/services/streamEventCallbacks.js +170 -0
- package/packages/server/src/services/streamEventHandler.js +488 -0
- package/packages/server/src/services/streamUsageHandler.js +228 -0
- package/packages/server/src/services/summaryBroadcast.js +61 -0
- package/packages/server/src/services/summaryClaudeClient.js +180 -0
- package/packages/server/src/services/summaryPrompts.js +169 -0
- package/packages/server/src/services/summaryService.js +552 -0
- package/packages/server/src/services/summaryStaleCheck.js +35 -0
- package/packages/server/src/services/systemMonitor.js +281 -0
- package/packages/server/src/services/templateTriggerService.js +197 -0
- package/packages/server/src/services/terminalOutput.js +160 -0
- package/packages/server/src/services/todoStore.js +58 -0
- package/packages/server/src/services/usageTracker.js +69 -0
- package/packages/server/src/services/withConcurrencyGuard.js +110 -0
- package/packages/server/src/websocket.js +10 -0
- package/packages/server/src/ws/WebSocketManager.js +240 -0
- package/packages/server/src/ws/index.js +50 -0
- package/packages/shared/package.json +27 -0
- package/packages/shared/src/constants.js +44 -0
- package/packages/shared/src/contracts/canvas.js +25 -0
- package/packages/shared/src/contracts/commandButtons.js +36 -0
- package/packages/shared/src/contracts/kanban.js +142 -0
- package/packages/shared/src/contracts/projects.js +63 -0
- package/packages/shared/src/contracts/providers.js +81 -0
- package/packages/shared/src/contracts/quickResponses.js +44 -0
- package/packages/shared/src/contracts/sessions.js +112 -0
- package/packages/shared/src/contracts/templates.js +51 -0
- package/packages/shared/src/index.js +5 -0
- package/packages/shared/src/protocol.js +76 -0
- package/packages/shared/src/routeParams.js +36 -0
- package/packages/shared/src/types.js +167 -0
- package/packages/shared/src/utils.js +101 -0
- package/packages/web/dist/assets/ActiveSessionsView-BQc76Jc8.js +1 -0
- package/packages/web/dist/assets/ActiveSessionsView-ofSvx-K1.css +1 -0
- package/packages/web/dist/assets/AgentLogsView-CTCjHjsu.js +2 -0
- package/packages/web/dist/assets/AgentLogsView-D90PnQVk.css +1 -0
- package/packages/web/dist/assets/ApiClient-Dbs1H78V.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-CCxSZ52u.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-CQZeuYBz.css +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-CF_-LXpU.js +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-DBm3rzhw.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-BQaQmU2d.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-DPofLvm-.js +1 -0
- package/packages/web/dist/assets/GeneralSettingsView-BCf53fpC.css +1 -0
- package/packages/web/dist/assets/GeneralSettingsView-BY1G-Kv8.js +1 -0
- package/packages/web/dist/assets/InterpolationHelp-CgdbNcJB.js +1 -0
- package/packages/web/dist/assets/InterpolationHelp-iNxTxmhs.css +1 -0
- package/packages/web/dist/assets/MarkdownEditor-CqT1U8lo.js +2 -0
- package/packages/web/dist/assets/MarkdownEditor-enuH2yvP.css +1 -0
- package/packages/web/dist/assets/ModelSelector-BBn_Ve0D.js +1 -0
- package/packages/web/dist/assets/ModelSelector-DPPD-92R.css +1 -0
- package/packages/web/dist/assets/NewSessionView-Bo5l49nu.js +3 -0
- package/packages/web/dist/assets/NewSessionView-Byoi1XdQ.css +1 -0
- package/packages/web/dist/assets/PathChooser-BoMGzeg2.css +1 -0
- package/packages/web/dist/assets/PathChooser-Cx9gQ-Qt.js +1 -0
- package/packages/web/dist/assets/ProjectEditView-BFuscj-V.js +1 -0
- package/packages/web/dist/assets/ProjectEditView-DNwBUNRk.css +1 -0
- package/packages/web/dist/assets/ProjectListView-C55H1JHQ.css +1 -0
- package/packages/web/dist/assets/ProjectListView-Dj0jBZ46.js +1 -0
- package/packages/web/dist/assets/ProjectNewView-Brdp-xUu.js +1 -0
- package/packages/web/dist/assets/ProjectNewView-CpgE4R-l.css +1 -0
- package/packages/web/dist/assets/ProvidersView-B_QQF3RM.css +1 -0
- package/packages/web/dist/assets/ProvidersView-Cxc-1skq.js +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-B2eVAtHW.js +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-B8188A1D.css +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-DIBQFj0W.css +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-lU8pW2B0.js +1 -0
- package/packages/web/dist/assets/ResizableTextarea-B5nAA0RV.css +1 -0
- package/packages/web/dist/assets/ResizableTextarea-DSy1mWGY.js +1 -0
- package/packages/web/dist/assets/SessionCard-BvjLwVYg.js +1 -0
- package/packages/web/dist/assets/SessionCard-D20G3bX8.css +1 -0
- package/packages/web/dist/assets/SessionDetailView-BQbPg-RJ.js +36 -0
- package/packages/web/dist/assets/SessionDetailView-BrMG4p2-.css +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BgqFR-5f.js +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BuLlDF-7.css +1 -0
- package/packages/web/dist/assets/SessionListView-BAIBtJF7.css +1 -0
- package/packages/web/dist/assets/SessionListView-CYIHI8qF.js +1 -0
- package/packages/web/dist/assets/SessionLogStream-B-FwUMJQ.js +18 -0
- package/packages/web/dist/assets/SessionLogStream-zPUTiGbe.css +1 -0
- package/packages/web/dist/assets/SettingsView-DC8-hTQ-.css +1 -0
- package/packages/web/dist/assets/SettingsView-fZxpiGp7.js +1 -0
- package/packages/web/dist/assets/SlashCommandWizard-BB30cSvo.css +1 -0
- package/packages/web/dist/assets/SlashCommandWizard-BgaOw9W3.js +1 -0
- package/packages/web/dist/assets/SummarySettingsView-DcsmSVJI.css +1 -0
- package/packages/web/dist/assets/SummarySettingsView-eeu1Xq86.js +1 -0
- package/packages/web/dist/assets/TemplateDetailView-DEPKSwDo.js +1 -0
- package/packages/web/dist/assets/TemplateDetailView-DT2m06W7.css +1 -0
- package/packages/web/dist/assets/apl-B4CMkyY2.js +1 -0
- package/packages/web/dist/assets/asciiarmor-Df11BRmG.js +1 -0
- package/packages/web/dist/assets/asn1-EdZsLKOL.js +1 -0
- package/packages/web/dist/assets/asterisk-B-8jnY81.js +1 -0
- package/packages/web/dist/assets/brainfuck-C4LP7Hcl.js +1 -0
- package/packages/web/dist/assets/clike-B9uivgTg.js +1 -0
- package/packages/web/dist/assets/clojure-BMjYHr_A.js +1 -0
- package/packages/web/dist/assets/cmake-BQqOBYOt.js +1 -0
- package/packages/web/dist/assets/cobol-CWcv1MsR.js +1 -0
- package/packages/web/dist/assets/coffeescript-S37ZYGWr.js +1 -0
- package/packages/web/dist/assets/commandButtons-DNSHH8IA.js +4 -0
- package/packages/web/dist/assets/commonlisp-DBKNyK5s.js +1 -0
- package/packages/web/dist/assets/crystal-SjHAIU92.js +1 -0
- package/packages/web/dist/assets/css-BnMrqG3P.js +1 -0
- package/packages/web/dist/assets/cypher-C_CwsFkJ.js +1 -0
- package/packages/web/dist/assets/d-pRatUO7H.js +1 -0
- package/packages/web/dist/assets/diff-DbItnlRl.js +1 -0
- package/packages/web/dist/assets/dockerfile-BKs6k2Af.js +1 -0
- package/packages/web/dist/assets/dtd-DF_7sFjM.js +1 -0
- package/packages/web/dist/assets/dylan-DwRh75JA.js +1 -0
- package/packages/web/dist/assets/ebnf-CDyGwa7X.js +1 -0
- package/packages/web/dist/assets/ecl-Cabwm37j.js +1 -0
- package/packages/web/dist/assets/eiffel-CnydiIhH.js +1 -0
- package/packages/web/dist/assets/elm-vLlmbW-K.js +1 -0
- package/packages/web/dist/assets/erlang-BNw1qcRV.js +1 -0
- package/packages/web/dist/assets/factor-kuTfRLto.js +1 -0
- package/packages/web/dist/assets/fcl-Kvtd6kyn.js +1 -0
- package/packages/web/dist/assets/forth-Ffai-XNe.js +1 -0
- package/packages/web/dist/assets/fortran-DYz_wnZ1.js +1 -0
- package/packages/web/dist/assets/gas-Bneqetm1.js +1 -0
- package/packages/web/dist/assets/gherkin-heZmZLOM.js +1 -0
- package/packages/web/dist/assets/groovy-D9Dt4D0W.js +1 -0
- package/packages/web/dist/assets/haskell-BWDZoCOh.js +1 -0
- package/packages/web/dist/assets/haxe-H-WmDvRZ.js +1 -0
- package/packages/web/dist/assets/http-DBlCnlav.js +1 -0
- package/packages/web/dist/assets/idl-BEugSyMb.js +1 -0
- package/packages/web/dist/assets/index-BZlHgDSz.js +1 -0
- package/packages/web/dist/assets/index-BhWX8AfE.js +2 -0
- package/packages/web/dist/assets/index-Bi3XvF_f.js +1 -0
- package/packages/web/dist/assets/index-BqXoPf_D.js +1 -0
- package/packages/web/dist/assets/index-CAuTOZSD.js +1 -0
- package/packages/web/dist/assets/index-CKYk-fkb.js +1 -0
- package/packages/web/dist/assets/index-CTumW_tV.js +318 -0
- package/packages/web/dist/assets/index-CVOJVSsC.js +82 -0
- package/packages/web/dist/assets/index-CXK2Z3_z.js +1 -0
- package/packages/web/dist/assets/index-CYllQ3Vd.js +1 -0
- package/packages/web/dist/assets/index-CpsfI08O.js +1 -0
- package/packages/web/dist/assets/index-DQkhDeTA.js +3 -0
- package/packages/web/dist/assets/index-DWP8iCBp.js +1 -0
- package/packages/web/dist/assets/index-DkVb9W_J.js +1 -0
- package/packages/web/dist/assets/index-DmKHPbIa.js +1 -0
- package/packages/web/dist/assets/index-DrlQi03X.js +1 -0
- package/packages/web/dist/assets/index-gmCCsCQ1.css +1 -0
- package/packages/web/dist/assets/index-prTEzzgO.js +1 -0
- package/packages/web/dist/assets/index-wqgejMCM.js +1 -0
- package/packages/web/dist/assets/index-yh0ZHIWw.js +7 -0
- package/packages/web/dist/assets/javascript-qCveANmP.js +1 -0
- package/packages/web/dist/assets/julia-DuME0IfC.js +1 -0
- package/packages/web/dist/assets/livescript-BwQOo05w.js +1 -0
- package/packages/web/dist/assets/lua-BgMRiT3U.js +1 -0
- package/packages/web/dist/assets/mathematica-DTrFuWx2.js +1 -0
- package/packages/web/dist/assets/mbox-CNhZ1qSd.js +1 -0
- package/packages/web/dist/assets/mirc-CjQqDB4T.js +1 -0
- package/packages/web/dist/assets/mllike-CXdrOF99.js +1 -0
- package/packages/web/dist/assets/modelica-Dc1JOy9r.js +1 -0
- package/packages/web/dist/assets/mscgen-BA5vi2Kp.js +1 -0
- package/packages/web/dist/assets/mumps-BT43cFF4.js +1 -0
- package/packages/web/dist/assets/nginx-DdIZxoE0.js +1 -0
- package/packages/web/dist/assets/nsis-LdVXkNf5.js +1 -0
- package/packages/web/dist/assets/ntriples-BfvgReVJ.js +1 -0
- package/packages/web/dist/assets/octave-Ck1zUtKM.js +1 -0
- package/packages/web/dist/assets/oz-BzwKVEFT.js +1 -0
- package/packages/web/dist/assets/pascal--L3eBynH.js +1 -0
- package/packages/web/dist/assets/perl-CdXCOZ3F.js +1 -0
- package/packages/web/dist/assets/pig-CevX1Tat.js +1 -0
- package/packages/web/dist/assets/powershell-CFHJl5sT.js +1 -0
- package/packages/web/dist/assets/projects-DbBQQH-V.js +1 -0
- package/packages/web/dist/assets/properties-C78fOPTZ.js +1 -0
- package/packages/web/dist/assets/protobuf-ChK-085T.js +1 -0
- package/packages/web/dist/assets/providers-ceCc4xRU.js +1 -0
- package/packages/web/dist/assets/pug-DukmZTjD.js +1 -0
- package/packages/web/dist/assets/puppet-DMA9R1ak.js +1 -0
- package/packages/web/dist/assets/python-BuPzkPfP.js +1 -0
- package/packages/web/dist/assets/q-pXgVlZs6.js +1 -0
- package/packages/web/dist/assets/r-DUYO_cvP.js +1 -0
- package/packages/web/dist/assets/rpm-CTu-6PCP.js +1 -0
- package/packages/web/dist/assets/ruby-B2Rjki9n.js +1 -0
- package/packages/web/dist/assets/sas-B4kiWyti.js +1 -0
- package/packages/web/dist/assets/scheme-C41bIUwD.js +1 -0
- package/packages/web/dist/assets/sessions-D681M81k.js +1 -0
- package/packages/web/dist/assets/settings-D0evez2V.js +1 -0
- package/packages/web/dist/assets/shell-CjFT_Tl9.js +1 -0
- package/packages/web/dist/assets/sieve-C3Gn_uJK.js +1 -0
- package/packages/web/dist/assets/simple-mode-GW_nhZxv.js +1 -0
- package/packages/web/dist/assets/smalltalk-CnHTOXQT.js +1 -0
- package/packages/web/dist/assets/solr-DehyRSwq.js +1 -0
- package/packages/web/dist/assets/sparql-DkYu6x3z.js +1 -0
- package/packages/web/dist/assets/spreadsheet-BCZA_wO0.js +1 -0
- package/packages/web/dist/assets/sql-D0XecflT.js +1 -0
- package/packages/web/dist/assets/stex-C3f8Ysf7.js +1 -0
- package/packages/web/dist/assets/style-BTin-zR_.css +1 -0
- package/packages/web/dist/assets/stylus-B533Al4x.js +1 -0
- package/packages/web/dist/assets/swift-BzpIVaGY.js +1 -0
- package/packages/web/dist/assets/tcl-DVfN8rqt.js +1 -0
- package/packages/web/dist/assets/textile-CnDTJFAw.js +1 -0
- package/packages/web/dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
- package/packages/web/dist/assets/tiki-DGYXhP31.js +1 -0
- package/packages/web/dist/assets/toml-Bm5Em-hy.js +1 -0
- package/packages/web/dist/assets/troff-wAsdV37c.js +1 -0
- package/packages/web/dist/assets/ttcn-CfJYG6tj.js +1 -0
- package/packages/web/dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
- package/packages/web/dist/assets/turtle-B1tBg_DP.js +1 -0
- package/packages/web/dist/assets/vb-CmGdzxic.js +1 -0
- package/packages/web/dist/assets/vbscript-BuJXcnF6.js +1 -0
- package/packages/web/dist/assets/velocity-D8B20fx6.js +1 -0
- package/packages/web/dist/assets/verilog-C6RDOZhf.js +1 -0
- package/packages/web/dist/assets/vhdl-lSbBsy5d.js +1 -0
- package/packages/web/dist/assets/webidl-ZXfAyPTL.js +1 -0
- package/packages/web/dist/assets/xquery-CQfU5ijd.js +1 -0
- package/packages/web/dist/assets/yacas-BJ4BC0dw.js +1 -0
- package/packages/web/dist/assets/z80-Hz9HOZM7.js +1 -0
- package/packages/web/dist/favicon.png +0 -0
- package/packages/web/dist/index.html +17 -0
- package/packages/web/dist/logo.png +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { projects, quickResponses } from '../database.js';
|
|
3
|
+
import {
|
|
4
|
+
CreateQuickResponseRequest,
|
|
5
|
+
UpdateQuickResponseRequest,
|
|
6
|
+
ReorderQuickResponsesRequest,
|
|
7
|
+
} from '../../../shared/src/contracts/quickResponses.js';
|
|
8
|
+
|
|
9
|
+
const router = Router();
|
|
10
|
+
|
|
11
|
+
// GET /api/projects/:projectId/quick-responses - List quick responses for a project
|
|
12
|
+
router.get('/projects/:projectId/quick-responses', (req, res) => {
|
|
13
|
+
const { projectId } = req.params;
|
|
14
|
+
|
|
15
|
+
const project = projects.getById(projectId);
|
|
16
|
+
if (!project) {
|
|
17
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const responses = quickResponses.getAvailableForProject(projectId);
|
|
21
|
+
res.json(responses);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// POST /api/projects/:projectId/quick-responses - Create a quick response
|
|
25
|
+
router.post('/projects/:projectId/quick-responses', (req, res) => {
|
|
26
|
+
const { projectId } = req.params;
|
|
27
|
+
|
|
28
|
+
const project = projects.getById(projectId);
|
|
29
|
+
if (!project) {
|
|
30
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = CreateQuickResponseRequest.safeParse(req.body);
|
|
34
|
+
if (!result.success) {
|
|
35
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { label, content, autoSubmit, category, sortOrder, isGlobal } = result.data;
|
|
39
|
+
|
|
40
|
+
const response = quickResponses.create({
|
|
41
|
+
projectId: isGlobal ? null : projectId,
|
|
42
|
+
label: label.trim(),
|
|
43
|
+
content,
|
|
44
|
+
autoSubmit,
|
|
45
|
+
category,
|
|
46
|
+
sortOrder,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
res.status(201).json(response);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// GET /api/quick-responses/global - List global quick responses only
|
|
53
|
+
router.get('/quick-responses/global', (_req, res) => {
|
|
54
|
+
const responses = quickResponses.getGlobal();
|
|
55
|
+
res.json(responses);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// PATCH /api/quick-responses/:id - Update a quick response
|
|
59
|
+
router.patch('/quick-responses/:id', (req, res) => {
|
|
60
|
+
const { id } = req.params;
|
|
61
|
+
|
|
62
|
+
const existing = quickResponses.getById(id);
|
|
63
|
+
if (!existing) {
|
|
64
|
+
return res.status(404).json({ error: 'Quick response not found' });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const result = UpdateQuickResponseRequest.safeParse(req.body);
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const updates = { ...result.data };
|
|
73
|
+
if (updates.label) {
|
|
74
|
+
updates.label = updates.label.trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const updated = quickResponses.update(id, updates);
|
|
78
|
+
res.json(updated);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// DELETE /api/quick-responses/:id - Delete a quick response
|
|
82
|
+
router.delete('/quick-responses/:id', (req, res) => {
|
|
83
|
+
const { id } = req.params;
|
|
84
|
+
|
|
85
|
+
const existing = quickResponses.getById(id);
|
|
86
|
+
if (!existing) {
|
|
87
|
+
return res.status(404).json({ error: 'Quick response not found' });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
quickResponses.deleteById(id);
|
|
91
|
+
res.status(204).send();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// POST /api/projects/:projectId/quick-responses/reorder - Reorder quick responses
|
|
95
|
+
router.post('/projects/:projectId/quick-responses/reorder', (req, res) => {
|
|
96
|
+
const { projectId } = req.params;
|
|
97
|
+
|
|
98
|
+
const project = projects.getById(projectId);
|
|
99
|
+
if (!project) {
|
|
100
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const result = ReorderQuickResponsesRequest.safeParse(req.body);
|
|
104
|
+
if (!result.success) {
|
|
105
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
quickResponses.updateSortOrder(result.data);
|
|
109
|
+
|
|
110
|
+
// Return the updated responses
|
|
111
|
+
const responses = quickResponses.getAvailableForProject(projectId);
|
|
112
|
+
res.json(responses);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// POST /api/quick-responses/global/reorder - Reorder global quick responses
|
|
116
|
+
router.post('/quick-responses/global/reorder', (req, res) => {
|
|
117
|
+
const result = ReorderQuickResponsesRequest.safeParse(req.body);
|
|
118
|
+
if (!result.success) {
|
|
119
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
quickResponses.updateSortOrder(result.data);
|
|
123
|
+
|
|
124
|
+
// Return the updated global responses
|
|
125
|
+
const responses = quickResponses.getGlobal();
|
|
126
|
+
res.json(responses);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export default router;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { sessions } from '../database.js';
|
|
3
|
+
import { broadcastToProject } from '../websocket.js';
|
|
4
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
5
|
+
import { requireSession, requireSessionAndProject } from '../middleware/sessionLookup.js';
|
|
6
|
+
import { executeHookAsync } from '../services/hookService.js';
|
|
7
|
+
|
|
8
|
+
const router = Router();
|
|
9
|
+
|
|
10
|
+
// POST /api/sessions/:id/archive - Archive a session
|
|
11
|
+
router.post('/:id/archive', requireSessionAndProject, async (req, res) => {
|
|
12
|
+
// Only allow archiving stopped/waiting/error sessions (not active sessions like starting/running)
|
|
13
|
+
if (!['stopped', 'waiting', 'error'].includes(req.session_.status)) {
|
|
14
|
+
return res.status(400).json({ error: 'Can only archive stopped, waiting, or error sessions' });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { cleanup } = req.body || {};
|
|
18
|
+
|
|
19
|
+
const updated = sessions.update(req.params.id, { archived: true });
|
|
20
|
+
|
|
21
|
+
// Execute project cleanup command if cleanup requested and project has one configured
|
|
22
|
+
// Skip for child sessions - they share parent's resources and shouldn't trigger teardown
|
|
23
|
+
if (cleanup && req.project?.onSessionDeleted && !req.session_.parentSessionId) {
|
|
24
|
+
executeHookAsync(req.project.onSessionDeleted, req.workingDirectory, {
|
|
25
|
+
sessionId: req.session_.id,
|
|
26
|
+
projectId: req.project.id,
|
|
27
|
+
sessionName: req.session_.name,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Broadcast update to project subscribers
|
|
32
|
+
broadcastToProject(req.session_.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
|
|
33
|
+
projectId: req.session_.projectId,
|
|
34
|
+
sessionId: req.params.id,
|
|
35
|
+
session: updated,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
res.json(updated);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// POST /api/sessions/:id/unarchive - Unarchive a session
|
|
42
|
+
router.post('/:id/unarchive', requireSession, (req, res) => {
|
|
43
|
+
const updated = sessions.update(req.params.id, { archived: false });
|
|
44
|
+
|
|
45
|
+
// Broadcast update to project subscribers
|
|
46
|
+
broadcastToProject(req.session_.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
|
|
47
|
+
projectId: req.session_.projectId,
|
|
48
|
+
sessionId: req.params.id,
|
|
49
|
+
session: updated,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
res.json(updated);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// POST /api/sessions/:id/star - Toggle star status for a session
|
|
56
|
+
router.post('/:id/star', requireSession, (req, res) => {
|
|
57
|
+
const updated = sessions.update(req.params.id, { starred: !req.session_.starred });
|
|
58
|
+
|
|
59
|
+
// Broadcast update to project subscribers
|
|
60
|
+
broadcastToProject(req.session_.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
|
|
61
|
+
projectId: req.session_.projectId,
|
|
62
|
+
sessionId: req.params.id,
|
|
63
|
+
session: updated,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
res.json(updated);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default router;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { commandButtons, commandRuns } from '../database.js';
|
|
3
|
+
import { broadcastToSession, broadcastToProject } from '../websocket.js';
|
|
4
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
5
|
+
import { requireSession, requireSessionAndProject } from '../middleware/sessionLookup.js';
|
|
6
|
+
import { commandRunner } from '../services/commandRunner.js';
|
|
7
|
+
import { databaseManager } from '../db/DatabaseManager.js';
|
|
8
|
+
|
|
9
|
+
const router = Router();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Broadcast command output to session and project subscribers.
|
|
13
|
+
* @param {{ sessionId: string, projectId: string, runId: string, buttonId: string }} ctx - Context
|
|
14
|
+
* @param {string} output - Output text
|
|
15
|
+
*/
|
|
16
|
+
function broadcastCommandOutput(ctx, output) {
|
|
17
|
+
const { sessionId, projectId, runId, buttonId } = ctx;
|
|
18
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_OUTPUT, { sessionId, runId, buttonId, output });
|
|
19
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_OUTPUT, { projectId, sessionId, runId, buttonId, output });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Broadcast command completion to session and project subscribers.
|
|
24
|
+
* @param {{ sessionId: string, projectId: string, runId: string, buttonId: string }} ctx - Context
|
|
25
|
+
* @param {{ exitCode: number, output: string }} result - Completion result
|
|
26
|
+
*/
|
|
27
|
+
function broadcastCommandComplete(ctx, result) {
|
|
28
|
+
const { sessionId, projectId, runId, buttonId } = ctx;
|
|
29
|
+
const { exitCode, output } = result;
|
|
30
|
+
const status = exitCode === 0 ? 'success' : 'error';
|
|
31
|
+
console.log(`[RUN] Command completed for runId: ${runId}, exitCode: ${exitCode}, status: ${status}`);
|
|
32
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_COMPLETE, { sessionId, runId, buttonId, status, exitCode, output });
|
|
33
|
+
console.log(`[RUN] Broadcasting COMMAND_RUN_COMPLETE to project ${projectId}`);
|
|
34
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_COMPLETE, { projectId, sessionId, runId, buttonId, status, exitCode, output });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Broadcast command error to session and project subscribers.
|
|
39
|
+
* @param {{ sessionId: string, projectId: string, runId: string, buttonId: string }} ctx - Context
|
|
40
|
+
* @param {string} errorMessage - Error message
|
|
41
|
+
*/
|
|
42
|
+
function broadcastCommandError(ctx, errorMessage) {
|
|
43
|
+
const { sessionId, projectId, runId, buttonId } = ctx;
|
|
44
|
+
console.log(`[RUN] Error for runId: ${runId}: ${errorMessage}`);
|
|
45
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_ERROR, { sessionId, runId, buttonId, error: errorMessage });
|
|
46
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_ERROR, { projectId, sessionId, runId, buttonId, error: errorMessage });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// POST /api/sessions/:id/command-buttons/:buttonId/run - Execute button command
|
|
50
|
+
router.post('/:id/command-buttons/:buttonId/run', requireSessionAndProject, (req, res) => {
|
|
51
|
+
const sessionId = req.params.id;
|
|
52
|
+
const buttonId = req.params.buttonId;
|
|
53
|
+
|
|
54
|
+
console.log(`[RUN] Starting command for buttonId: ${buttonId}, sessionId: ${sessionId}`);
|
|
55
|
+
|
|
56
|
+
const button = commandButtons.getById(buttonId);
|
|
57
|
+
if (!button) {
|
|
58
|
+
return res.status(404).json({ error: 'Command button not found' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Generate run ID
|
|
62
|
+
const runId = databaseManager.generateId();
|
|
63
|
+
|
|
64
|
+
console.log(`[RUN] Generated runId: ${runId} for command: ${button.command}`);
|
|
65
|
+
|
|
66
|
+
// Return immediately with runId
|
|
67
|
+
res.json({ runId, buttonId, status: 'running', output: '' });
|
|
68
|
+
|
|
69
|
+
// Capture middleware values for use in async callbacks
|
|
70
|
+
const projectId = req.session_.projectId;
|
|
71
|
+
const workingDirectory = req.workingDirectory;
|
|
72
|
+
const ctx = { sessionId, projectId, runId, buttonId };
|
|
73
|
+
|
|
74
|
+
// Broadcast initial "running" status immediately so session list can show the running indicator
|
|
75
|
+
broadcastCommandOutput(ctx, '');
|
|
76
|
+
|
|
77
|
+
// Execute command asynchronously
|
|
78
|
+
(async () => {
|
|
79
|
+
try {
|
|
80
|
+
console.log(`[RUN] Starting async execution for runId: ${runId}`);
|
|
81
|
+
await commandRunner.run(
|
|
82
|
+
{ runId, command: button.command, workingDirectory },
|
|
83
|
+
{
|
|
84
|
+
onOutput: (text) => {
|
|
85
|
+
console.log(`[RUN] Output received for runId: ${runId}`);
|
|
86
|
+
broadcastCommandOutput(ctx, text);
|
|
87
|
+
},
|
|
88
|
+
onComplete: (exitCode, output) => broadcastCommandComplete(ctx, { exitCode, output }),
|
|
89
|
+
onError: (message) => broadcastCommandError(ctx, message),
|
|
90
|
+
},
|
|
91
|
+
{ sessionId, buttonId }
|
|
92
|
+
);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error(`Error running command button ${buttonId}:`, error);
|
|
95
|
+
broadcastCommandError(ctx, error.message);
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// GET /api/sessions/:id/command-buttons/runs - Get active runs for session
|
|
101
|
+
router.get('/:id/command-buttons/runs', requireSession, (req, res) => {
|
|
102
|
+
const sessionId = req.params.id;
|
|
103
|
+
|
|
104
|
+
const activeRuns = commandRunner.getRunsBySession(sessionId);
|
|
105
|
+
res.json(activeRuns);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// GET /api/sessions/:id/command-buttons/runs/:runId - Get single run by ID
|
|
109
|
+
router.get('/:id/command-buttons/runs/:runId', requireSession, (req, res) => {
|
|
110
|
+
const { id: sessionId, runId } = req.params;
|
|
111
|
+
|
|
112
|
+
// Check if run is currently running (in memory)
|
|
113
|
+
if (commandRunner.isRunning(runId)) {
|
|
114
|
+
const activeRuns = commandRunner.getRunsBySession(sessionId);
|
|
115
|
+
const run = activeRuns.find((r) => r.runId === runId);
|
|
116
|
+
if (run) {
|
|
117
|
+
return res.json(run);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Otherwise check database
|
|
122
|
+
const run = commandRuns.getById(runId);
|
|
123
|
+
if (!run || run.sessionId !== sessionId) {
|
|
124
|
+
return res.status(404).json({ error: 'Run not found' });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
res.json({
|
|
128
|
+
runId: run.id,
|
|
129
|
+
buttonId: run.buttonId,
|
|
130
|
+
status: run.status,
|
|
131
|
+
output: run.output,
|
|
132
|
+
exitCode: run.exitCode,
|
|
133
|
+
startedAt: run.startedAt,
|
|
134
|
+
completedAt: run.completedAt,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// DELETE /api/sessions/:id/command-buttons/runs/:runId - Delete a command run record
|
|
139
|
+
router.delete('/:id/command-buttons/runs/:runId', requireSessionAndProject, (req, res) => {
|
|
140
|
+
const sessionId = req.params.id;
|
|
141
|
+
const { runId } = req.params;
|
|
142
|
+
|
|
143
|
+
const run = commandRuns.getById(runId);
|
|
144
|
+
if (!run || run.sessionId !== sessionId) {
|
|
145
|
+
return res.status(404).json({ error: 'Run not found' });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (commandRunner.isRunning(runId)) {
|
|
149
|
+
return res.status(409).json({ error: 'Cannot delete a running command. Kill it first.' });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
commandRuns.deleteById(runId);
|
|
153
|
+
|
|
154
|
+
const projectId = req.session_.projectId;
|
|
155
|
+
|
|
156
|
+
// Broadcast deletion to session and project subscribers
|
|
157
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
|
|
158
|
+
runId,
|
|
159
|
+
buttonId: run.buttonId,
|
|
160
|
+
sessionId,
|
|
161
|
+
});
|
|
162
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
|
|
163
|
+
runId,
|
|
164
|
+
buttonId: run.buttonId,
|
|
165
|
+
sessionId,
|
|
166
|
+
projectId,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
res.status(204).send();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// DELETE /api/sessions/:id/command-buttons/:buttonId/runs/all - Delete all runs for a button in a session
|
|
173
|
+
router.delete('/:id/command-buttons/:buttonId/runs/all', requireSessionAndProject, (req, res) => {
|
|
174
|
+
const sessionId = req.params.id;
|
|
175
|
+
const { buttonId } = req.params;
|
|
176
|
+
|
|
177
|
+
const button = commandButtons.getById(buttonId);
|
|
178
|
+
if (!button) {
|
|
179
|
+
return res.status(404).json({ error: 'Command button not found' });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { deletedRuns } = commandRuns.deleteByButtonAndSession(buttonId, sessionId);
|
|
183
|
+
|
|
184
|
+
const projectId = req.session_.projectId;
|
|
185
|
+
|
|
186
|
+
// Broadcast individual COMMAND_RUN_DELETED events for each deleted run
|
|
187
|
+
for (const run of deletedRuns) {
|
|
188
|
+
broadcastToSession(sessionId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
|
|
189
|
+
runId: run.id,
|
|
190
|
+
buttonId: run.buttonId,
|
|
191
|
+
sessionId,
|
|
192
|
+
});
|
|
193
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.COMMAND_RUN_DELETED, {
|
|
194
|
+
runId: run.id,
|
|
195
|
+
buttonId: run.buttonId,
|
|
196
|
+
sessionId,
|
|
197
|
+
projectId,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
res.status(204).send();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// POST /api/sessions/:id/command-buttons/runs/:runId/kill - Kill running command
|
|
205
|
+
router.post('/:id/command-buttons/runs/:runId/kill', requireSession, (req, res) => {
|
|
206
|
+
const sessionId = req.params.id;
|
|
207
|
+
const runId = req.params.runId;
|
|
208
|
+
|
|
209
|
+
console.log(`[KILL] Kill request for runId: ${runId}, sessionId: ${sessionId}`);
|
|
210
|
+
|
|
211
|
+
const killed = commandRunner.kill(runId);
|
|
212
|
+
console.log(`[KILL] Kill result: ${killed} for runId: ${runId}`);
|
|
213
|
+
if (!killed) {
|
|
214
|
+
return res.status(404).json({ error: 'Run not found or already completed' });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
res.json({ success: true, runId });
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
export default router;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { messages, conversations, projects } from '../database.js';
|
|
3
|
+
import { continueSessionWithExistingMessage } from '../services/sessionManager.js';
|
|
4
|
+
import { broadcastToSession } from '../websocket.js';
|
|
5
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
6
|
+
import { requireSession } from '../middleware/sessionLookup.js';
|
|
7
|
+
|
|
8
|
+
const router = Router();
|
|
9
|
+
|
|
10
|
+
// GET /api/sessions/:id/conversations - List all conversations for a session
|
|
11
|
+
router.get('/:id/conversations', requireSession, (req, res) => {
|
|
12
|
+
// Use getBySessionIdWithBranchInfo to include branching metadata
|
|
13
|
+
const sessionConversations = conversations.getBySessionIdWithBranchInfo(req.params.id);
|
|
14
|
+
res.json(sessionConversations);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// POST /api/sessions/:id/conversations - Create new conversation
|
|
18
|
+
router.post('/:id/conversations', requireSession, async (req, res) => {
|
|
19
|
+
// Block creating new conversation while session is running
|
|
20
|
+
if (req.session_.status === 'running') {
|
|
21
|
+
return res.status(400).json({ error: 'Cannot create new conversation while session is running' });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { name } = req.body;
|
|
25
|
+
|
|
26
|
+
const conversation = conversations.create(req.params.id, name || null, true);
|
|
27
|
+
|
|
28
|
+
// Broadcast conversation created event
|
|
29
|
+
broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_CREATED, {
|
|
30
|
+
sessionId: req.params.id,
|
|
31
|
+
conversation,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
res.status(201).json(conversation);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// GET /api/sessions/:id/conversations/:convId - Get specific conversation
|
|
38
|
+
router.get('/:id/conversations/:convId', (req, res) => {
|
|
39
|
+
const conversation = conversations.getById(req.params.convId);
|
|
40
|
+
if (!conversation || conversation.sessionId !== req.params.id) {
|
|
41
|
+
return res.status(404).json({ error: 'Conversation not found' });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Include message count
|
|
45
|
+
const messageCount = messages.getCountByConversationId(req.params.convId);
|
|
46
|
+
res.json({ ...conversation, messageCount });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// PATCH /api/sessions/:id/conversations/:convId - Update conversation
|
|
50
|
+
router.patch('/:id/conversations/:convId', requireSession, async (req, res) => {
|
|
51
|
+
const conversation = conversations.getById(req.params.convId);
|
|
52
|
+
if (!conversation || conversation.sessionId !== req.params.id) {
|
|
53
|
+
return res.status(404).json({ error: 'Conversation not found' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { name, isActive } = req.body;
|
|
57
|
+
|
|
58
|
+
// Block switching conversation while session is running
|
|
59
|
+
if (isActive && req.session_.status === 'running') {
|
|
60
|
+
return res.status(400).json({ error: 'Cannot switch conversation while session is running' });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const updateData = {};
|
|
64
|
+
if (name !== undefined) updateData.name = name;
|
|
65
|
+
if (isActive !== undefined) updateData.isActive = isActive;
|
|
66
|
+
|
|
67
|
+
const updated = conversations.update(req.params.convId, updateData);
|
|
68
|
+
|
|
69
|
+
// Broadcast conversation updated event
|
|
70
|
+
broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_UPDATED, {
|
|
71
|
+
sessionId: req.params.id,
|
|
72
|
+
conversation: updated,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
res.json(updated);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// DELETE /api/sessions/:id/conversations/:convId - Delete conversation
|
|
79
|
+
router.delete('/:id/conversations/:convId', requireSession, (req, res) => {
|
|
80
|
+
// Block deleting conversation while session is running
|
|
81
|
+
if (req.session_.status === 'running') {
|
|
82
|
+
return res.status(400).json({ error: 'Cannot delete conversation while session is running' });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const conversation = conversations.getById(req.params.convId);
|
|
86
|
+
if (!conversation || conversation.sessionId !== req.params.id) {
|
|
87
|
+
return res.status(404).json({ error: 'Conversation not found' });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Delete and handle active conversation logic
|
|
91
|
+
const newActive = conversations.deleteAndHandleActive(req.params.convId);
|
|
92
|
+
|
|
93
|
+
// Broadcast conversation deleted event
|
|
94
|
+
broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_DELETED, {
|
|
95
|
+
sessionId: req.params.id,
|
|
96
|
+
conversationId: req.params.convId,
|
|
97
|
+
newActiveConversation: newActive,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
res.status(204).send();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// POST /api/sessions/:id/conversations/:convId/branch - Create a branch from a conversation
|
|
104
|
+
router.post('/:id/conversations/:convId/branch', requireSession, async (req, res) => {
|
|
105
|
+
// Block branching while session is running
|
|
106
|
+
if (req.session_.status === 'running') {
|
|
107
|
+
return res.status(400).json({ error: 'Cannot branch conversation while session is running' });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const conversation = conversations.getById(req.params.convId);
|
|
111
|
+
if (!conversation || conversation.sessionId !== req.params.id) {
|
|
112
|
+
return res.status(404).json({ error: 'Conversation not found' });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const { messageId, prompt } = req.body;
|
|
116
|
+
|
|
117
|
+
if (!messageId) {
|
|
118
|
+
return res.status(400).json({ error: 'messageId is required' });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!prompt || !prompt.trim()) {
|
|
122
|
+
return res.status(400).json({ error: 'prompt is required' });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
// Create the branch
|
|
127
|
+
// Note: name is auto-generated from the prompt in ConversationRepository.branch()
|
|
128
|
+
const branchConversation = conversations.branch(
|
|
129
|
+
req.params.convId,
|
|
130
|
+
messageId,
|
|
131
|
+
null, // name is auto-generated from prompt
|
|
132
|
+
prompt
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Broadcast the new conversation to session subscribers
|
|
136
|
+
broadcastToSession(req.params.id, WS_MESSAGE_TYPES.CONVERSATION_CREATED, {
|
|
137
|
+
sessionId: req.params.id,
|
|
138
|
+
conversation: branchConversation,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Auto-submit to Claude: Start the session with the new prompt
|
|
142
|
+
// The branch already has the user message, so we use continueSessionWithExistingMessage
|
|
143
|
+
// which triggers Claude's response WITHOUT creating a duplicate user message
|
|
144
|
+
try {
|
|
145
|
+
const project = projects.getById(req.session_.projectId);
|
|
146
|
+
const workingDirectory = req.session_.gitWorktree || project?.workingDirectory;
|
|
147
|
+
if (workingDirectory) {
|
|
148
|
+
await continueSessionWithExistingMessage(
|
|
149
|
+
req.params.id,
|
|
150
|
+
branchConversation.id,
|
|
151
|
+
workingDirectory,
|
|
152
|
+
{ systemPrompt: project?.systemPrompt }
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
} catch (err) {
|
|
156
|
+
console.error('Failed to auto-start branched conversation:', err);
|
|
157
|
+
// Don't fail the whole request if auto-start fails
|
|
158
|
+
// User can manually trigger from the UI
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
res.status(201).json(branchConversation);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('Branch conversation error:', error);
|
|
164
|
+
res.status(400).json({ error: error.message });
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
export default router;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { messages } from '../database.js';
|
|
3
|
+
import { broadcastToSession } from '../websocket.js';
|
|
4
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
5
|
+
import { requireSession } from '../middleware/sessionLookup.js';
|
|
6
|
+
import { validateDraftSession, startDraft, DraftSessionError } from '../services/draftSessionService.js';
|
|
7
|
+
|
|
8
|
+
const router = Router();
|
|
9
|
+
|
|
10
|
+
// PUT /api/sessions/:id/initial-prompt - Update the initial prompt for a draft session
|
|
11
|
+
router.put('/:id/initial-prompt', requireSession, (req, res) => {
|
|
12
|
+
const validation = validateDraftSession(req.session_);
|
|
13
|
+
if (!validation.valid) {
|
|
14
|
+
return res.status(400).json({ error: validation.error });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Get the request body
|
|
18
|
+
const { prompt } = req.body;
|
|
19
|
+
|
|
20
|
+
// Validate prompt is provided and non-empty
|
|
21
|
+
if (!prompt || typeof prompt !== 'string' || prompt.trim() === '') {
|
|
22
|
+
return res.status(400).json({ error: 'Prompt must be a non-empty string' });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const allMessages = messages.getBySessionId(req.session_.id);
|
|
27
|
+
// Find the first user message and update it
|
|
28
|
+
const userMessages = allMessages.filter(msg => msg.role === 'user');
|
|
29
|
+
if (userMessages.length === 0) {
|
|
30
|
+
return res.status(400).json({ error: 'No initial prompt found' });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const initialMessage = userMessages[0];
|
|
34
|
+
const updatedMessage = messages.updateContent(initialMessage.id, prompt);
|
|
35
|
+
|
|
36
|
+
// Broadcast the update to session subscribers
|
|
37
|
+
broadcastToSession(req.session_.id, WS_MESSAGE_TYPES.MESSAGE_UPDATED, {
|
|
38
|
+
sessionId: req.session_.id,
|
|
39
|
+
message: updatedMessage,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
res.json({ success: true, message: updatedMessage });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Update initial prompt error:', error);
|
|
45
|
+
res.status(500).json({ error: error.message });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// POST /api/sessions/:id/start - Start a draft session (waiting status with no assistant messages)
|
|
50
|
+
router.post('/:id/start', requireSession, async (req, res) => {
|
|
51
|
+
const validation = validateDraftSession(req.session_);
|
|
52
|
+
if (!validation.valid) {
|
|
53
|
+
return res.status(400).json({ error: validation.error });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const updatedSession = await startDraft(req.session_, {
|
|
58
|
+
prompt: req.body.prompt,
|
|
59
|
+
model: req.body.model,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
res.json({ success: true, session: updatedSession });
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof DraftSessionError) {
|
|
65
|
+
return res.status(error.statusCode).json({ error: error.message });
|
|
66
|
+
}
|
|
67
|
+
console.error('Start session error:', error);
|
|
68
|
+
res.status(500).json({ error: error.message });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export default router;
|