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,86 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { workLogs } from '../database.js';
|
|
3
|
+
import { broadcastToSession } from '../websocket.js';
|
|
4
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
5
|
+
import { textAccumulators, thinkingAccumulators } from '../services/streamEventHandler.js';
|
|
6
|
+
import { requireSession } from '../middleware/sessionLookup.js';
|
|
7
|
+
|
|
8
|
+
const router = Router();
|
|
9
|
+
|
|
10
|
+
// GET /api/sessions/:id/work-logs - Get work logs for session
|
|
11
|
+
router.get('/:id/work-logs', requireSession, (req, res) => {
|
|
12
|
+
// Return work logs grouped by message ID
|
|
13
|
+
const grouped = workLogs.getBySessionIdGrouped(req.params.id);
|
|
14
|
+
res.json(grouped);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// GET /api/sessions/:id/streaming-state - Get current streaming snapshot for a running session
|
|
18
|
+
// Returns recent pending work logs, accumulated partial text, and thinking
|
|
19
|
+
router.get('/:id/streaming-state', requireSession, (req, res) => {
|
|
20
|
+
const sessionId = req.params.id;
|
|
21
|
+
const pendingWorkLogs = workLogs.getRecentPendingBySessionId(sessionId);
|
|
22
|
+
const partialText = textAccumulators.get(sessionId) || '';
|
|
23
|
+
const thinking = thinkingAccumulators.get(sessionId) || null;
|
|
24
|
+
|
|
25
|
+
res.json({
|
|
26
|
+
workLogs: pendingWorkLogs,
|
|
27
|
+
partialText,
|
|
28
|
+
thinking,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// POST /api/sessions/:id/work-logs - Create work log (for testing)
|
|
33
|
+
router.post('/:id/work-logs', requireSession, (req, res) => {
|
|
34
|
+
const { type, content, toolName, messageId } = req.body;
|
|
35
|
+
if (!type || !content) {
|
|
36
|
+
return res.status(400).json({ error: 'Type and content are required' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const log = workLogs.create(req.params.id, type, content, { messageId: messageId || null, toolName: toolName || null });
|
|
40
|
+
|
|
41
|
+
// Broadcast to session subscribers
|
|
42
|
+
broadcastToSession(req.params.id, WS_MESSAGE_TYPES.SESSION_WORK_LOG, {
|
|
43
|
+
sessionId: req.params.id,
|
|
44
|
+
log,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
res.status(201).json(log);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// POST /api/sessions/:id/partial-text - Set partial text (for testing)
|
|
51
|
+
router.post('/:id/partial-text', requireSession, (req, res) => {
|
|
52
|
+
const { text } = req.body;
|
|
53
|
+
if (typeof text !== 'string') {
|
|
54
|
+
return res.status(400).json({ error: 'Text must be a string' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
textAccumulators.set(req.params.id, text);
|
|
58
|
+
|
|
59
|
+
// Broadcast to session subscribers
|
|
60
|
+
broadcastToSession(req.params.id, WS_MESSAGE_TYPES.SESSION_PARTIAL, {
|
|
61
|
+
sessionId: req.params.id,
|
|
62
|
+
text,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
res.status(201).json({ text });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// POST /api/sessions/:id/thinking - Set thinking (for testing)
|
|
69
|
+
router.post('/:id/thinking', requireSession, (req, res) => {
|
|
70
|
+
const { thinking } = req.body;
|
|
71
|
+
if (typeof thinking !== 'string') {
|
|
72
|
+
return res.status(400).json({ error: 'Thinking must be a string' });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
thinkingAccumulators.set(req.params.id, thinking);
|
|
76
|
+
|
|
77
|
+
// Broadcast to session subscribers
|
|
78
|
+
broadcastToSession(req.params.id, WS_MESSAGE_TYPES.SESSION_THINKING_PARTIAL, {
|
|
79
|
+
sessionId: req.params.id,
|
|
80
|
+
thinking,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
res.status(201).json({ thinking });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export default router;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { extname, resolve, normalize } from 'path';
|
|
4
|
+
import { sessions, messages, projects, commandRuns, sessionSummaries } from '../database.js';
|
|
5
|
+
import { getChanges, getChangesBranch } from '../services/diffService.js';
|
|
6
|
+
import * as gitService from '../services/gitService.js';
|
|
7
|
+
import { requireSession, requireSessionAndProject } from '../middleware/sessionLookup.js';
|
|
8
|
+
import { commandRunner } from '../services/commandRunner.js';
|
|
9
|
+
|
|
10
|
+
// Import sub-routers
|
|
11
|
+
import notesRouter from './sessions-notes.js';
|
|
12
|
+
import conversationsRouter from './sessions-conversations.js';
|
|
13
|
+
import commandsRouter from './sessions-commands.js';
|
|
14
|
+
import patchRouter from './sessions-patch.js';
|
|
15
|
+
import archiveRouter from './sessions-archive.js';
|
|
16
|
+
import lifecycleRouter from './sessions-lifecycle.js';
|
|
17
|
+
import streamingRouter from './sessions-streaming.js';
|
|
18
|
+
import messagesRouter from './sessions-messages.js';
|
|
19
|
+
import draftRouter from './sessions-draft.js';
|
|
20
|
+
|
|
21
|
+
const router = Router();
|
|
22
|
+
|
|
23
|
+
// Mount sub-routers
|
|
24
|
+
router.use('/', notesRouter);
|
|
25
|
+
router.use('/', conversationsRouter);
|
|
26
|
+
router.use('/', commandsRouter);
|
|
27
|
+
router.use('/', patchRouter);
|
|
28
|
+
router.use('/', archiveRouter);
|
|
29
|
+
router.use('/', lifecycleRouter);
|
|
30
|
+
router.use('/', streamingRouter);
|
|
31
|
+
router.use('/', messagesRouter);
|
|
32
|
+
router.use('/', draftRouter);
|
|
33
|
+
|
|
34
|
+
// TTL cache for files-count endpoint (60 second TTL)
|
|
35
|
+
const filesCountCache = new Map();
|
|
36
|
+
const FILES_COUNT_CACHE_TTL = 60_000; // 60 seconds
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get cached files count or null if expired/missing
|
|
40
|
+
* @param {string} sessionId
|
|
41
|
+
* @returns {{ count: number } | null}
|
|
42
|
+
*/
|
|
43
|
+
function getCachedFilesCount(sessionId) {
|
|
44
|
+
const cached = filesCountCache.get(sessionId);
|
|
45
|
+
if (cached && (Date.now() - cached.timestamp) < FILES_COUNT_CACHE_TTL) {
|
|
46
|
+
return { count: cached.count };
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set files count in cache
|
|
53
|
+
* @param {string} sessionId
|
|
54
|
+
* @param {number} count
|
|
55
|
+
*/
|
|
56
|
+
function setCachedFilesCount(sessionId, count) {
|
|
57
|
+
filesCountCache.set(sessionId, { count, timestamp: Date.now() });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Invalidate files count cache for a session
|
|
62
|
+
* @param {string} sessionId
|
|
63
|
+
*/
|
|
64
|
+
export function invalidateFilesCountCache(sessionId) {
|
|
65
|
+
filesCountCache.delete(sessionId);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// GET /api/sessions - Get all active/waiting sessions across all projects
|
|
69
|
+
router.get('/', (req, res) => {
|
|
70
|
+
const activeSessions = sessions.getActiveAndWaiting();
|
|
71
|
+
res.json(activeSessions);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// GET /api/sessions/scheduled - Get all scheduled sessions (optionally filtered by project)
|
|
75
|
+
router.get('/scheduled', (req, res) => {
|
|
76
|
+
const { projectId } = req.query;
|
|
77
|
+
const scheduledSessions = sessions.getScheduledSessions(projectId || null);
|
|
78
|
+
res.json(scheduledSessions);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// POST /api/sessions/summaries/batch - Get summaries for multiple sessions in one request
|
|
82
|
+
// Must be registered before /:id routes to avoid Express matching 'summaries' as an :id param
|
|
83
|
+
router.post('/summaries/batch', (req, res) => {
|
|
84
|
+
const { ids } = req.body;
|
|
85
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
86
|
+
return res.status(400).json({ error: 'ids array is required and must not be empty' });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const summaryList = sessionSummaries.getBySessionIds(ids);
|
|
90
|
+
|
|
91
|
+
// Build a map of sessionId -> summary (or null if not found)
|
|
92
|
+
const result = {};
|
|
93
|
+
for (const id of ids) {
|
|
94
|
+
result[id] = null;
|
|
95
|
+
}
|
|
96
|
+
for (const summary of summaryList) {
|
|
97
|
+
result[summary.sessionId] = summary;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
res.json(result);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// GET /api/sessions/:id - Get session details
|
|
104
|
+
// Includes latestCommandRuns (merged from DB completed runs + in-memory running commands)
|
|
105
|
+
router.get('/:id', requireSession, (req, res) => {
|
|
106
|
+
// Add hasResponses flag to indicate if session has ever received assistant responses
|
|
107
|
+
// This is used by the frontend to determine if a session is a draft
|
|
108
|
+
const allMessages = messages.getBySessionId(req.params.id);
|
|
109
|
+
const hasResponses = allMessages.some(msg => msg.role === 'assistant');
|
|
110
|
+
|
|
111
|
+
// Get command run statuses (latest run per button for this session)
|
|
112
|
+
// Completed runs from DB
|
|
113
|
+
const dbRuns = commandRuns.getLatestRunsForSession(req.params.id);
|
|
114
|
+
// Currently running commands from memory - filter all runs for this session
|
|
115
|
+
const allRunning = commandRunner.getRunsBySession(req.params.id);
|
|
116
|
+
const runningRuns = allRunning.filter(run => run.status === 'running');
|
|
117
|
+
|
|
118
|
+
// Build map of buttonId -> run data
|
|
119
|
+
// Running commands take precedence over completed ones (more current state)
|
|
120
|
+
const runsByButton = {};
|
|
121
|
+
|
|
122
|
+
// First add DB runs (completed)
|
|
123
|
+
for (const run of dbRuns) {
|
|
124
|
+
runsByButton[run.buttonId] = {
|
|
125
|
+
buttonId: run.buttonId,
|
|
126
|
+
status: run.status,
|
|
127
|
+
exitCode: run.exitCode,
|
|
128
|
+
runId: run.id,
|
|
129
|
+
startedAt: run.startedAt,
|
|
130
|
+
completedAt: run.completedAt,
|
|
131
|
+
output: run.output || null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Then overlay running commands (takes precedence)
|
|
136
|
+
for (const run of runningRuns) {
|
|
137
|
+
runsByButton[run.buttonId] = {
|
|
138
|
+
buttonId: run.buttonId,
|
|
139
|
+
status: 'running',
|
|
140
|
+
exitCode: null,
|
|
141
|
+
runId: run.runId,
|
|
142
|
+
startedAt: run.startedAt,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const latestCommandRuns = Object.values(runsByButton);
|
|
147
|
+
|
|
148
|
+
res.json({ ...req.session_, hasResponses, latestCommandRuns });
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// GET /api/sessions/:id/changes - Get git changes for session
|
|
152
|
+
// Query params:
|
|
153
|
+
// compareMode: 'local' (default) or 'branch' - determines what to compare against
|
|
154
|
+
// branch: branch ref to compare against (e.g., 'origin/main') - used when compareMode='branch'
|
|
155
|
+
router.get('/:id/changes', requireSessionAndProject, async (req, res) => {
|
|
156
|
+
try {
|
|
157
|
+
const { compareMode = 'local', branch } = req.query;
|
|
158
|
+
|
|
159
|
+
let changes;
|
|
160
|
+
if (compareMode === 'branch' && branch) {
|
|
161
|
+
// Get changes compared to a specific branch
|
|
162
|
+
changes = await getChangesBranch(req.workingDirectory, branch);
|
|
163
|
+
} else {
|
|
164
|
+
// Default: get local changes (staged, unstaged, untracked)
|
|
165
|
+
changes = await getChanges(req.workingDirectory);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
res.json(changes);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
res.status(500).json({ error: error.message });
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Image MIME types for the file endpoint
|
|
175
|
+
const IMAGE_MIME_TYPES = {
|
|
176
|
+
'.png': 'image/png',
|
|
177
|
+
'.jpg': 'image/jpeg',
|
|
178
|
+
'.jpeg': 'image/jpeg',
|
|
179
|
+
'.gif': 'image/gif',
|
|
180
|
+
'.webp': 'image/webp',
|
|
181
|
+
'.svg': 'image/svg+xml',
|
|
182
|
+
'.bmp': 'image/bmp',
|
|
183
|
+
'.ico': 'image/x-icon',
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// GET /api/sessions/:id/file - Get a file from the session's working directory
|
|
187
|
+
// Used for displaying images in the diff viewer
|
|
188
|
+
router.get('/:id/file', requireSessionAndProject, (req, res) => {
|
|
189
|
+
const { path: filePath } = req.query;
|
|
190
|
+
if (!filePath) {
|
|
191
|
+
return res.status(400).json({ error: 'path query parameter is required' });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Security: ensure the requested path is within the working directory
|
|
195
|
+
const fullPath = resolve(req.workingDirectory, filePath);
|
|
196
|
+
const normalizedDir = normalize(req.workingDirectory);
|
|
197
|
+
if (!fullPath.startsWith(normalizedDir)) {
|
|
198
|
+
return res.status(403).json({ error: 'Access denied: path outside working directory' });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!existsSync(fullPath)) {
|
|
202
|
+
return res.status(404).json({ error: 'File not found' });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const ext = extname(fullPath).toLowerCase();
|
|
207
|
+
const mimeType = IMAGE_MIME_TYPES[ext];
|
|
208
|
+
|
|
209
|
+
if (!mimeType) {
|
|
210
|
+
return res.status(400).json({ error: 'Only image files are supported' });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const fileBuffer = readFileSync(fullPath);
|
|
214
|
+
const base64 = fileBuffer.toString('base64');
|
|
215
|
+
|
|
216
|
+
res.json({
|
|
217
|
+
data: base64,
|
|
218
|
+
mimeType,
|
|
219
|
+
filename: filePath,
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
res.status(500).json({ error: error.message });
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// GET /api/sessions/:id/default-branch - Get the default branch for branch comparison
|
|
227
|
+
router.get('/:id/default-branch', requireSessionAndProject, async (req, res) => {
|
|
228
|
+
try {
|
|
229
|
+
const branch = await gitService.getOriginDefaultBranch(req.workingDirectory);
|
|
230
|
+
res.json({ branch });
|
|
231
|
+
} catch (error) {
|
|
232
|
+
res.status(500).json({ error: error.message });
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// GET /api/sessions/:id/files-count - Get count of modified files
|
|
237
|
+
// Uses a 60-second TTL cache to avoid expensive git operations on every request
|
|
238
|
+
router.get('/:id/files-count', requireSession, async (req, res) => {
|
|
239
|
+
// Check cache first
|
|
240
|
+
const cached = getCachedFilesCount(req.params.id);
|
|
241
|
+
if (cached) {
|
|
242
|
+
return res.json(cached);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const project = projects.getById(req.session_.projectId);
|
|
246
|
+
if (!project) {
|
|
247
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Use gitWorktree if set, otherwise use the project's working directory
|
|
251
|
+
const directory = req.session_.gitWorktree || project.workingDirectory;
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
// Get the default branch to compare against
|
|
255
|
+
const defaultBranch = await gitService.getOriginDefaultBranch(directory);
|
|
256
|
+
const count = await gitService.getModifiedFilesCount(directory, defaultBranch);
|
|
257
|
+
|
|
258
|
+
// Cache the result
|
|
259
|
+
setCachedFilesCount(req.params.id, count);
|
|
260
|
+
|
|
261
|
+
res.json({ count });
|
|
262
|
+
} catch (error) {
|
|
263
|
+
res.status(500).json({ error: error.message, count: 0 });
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// PATCH /:id and PATCH /:id/pending-prompt are handled by sessions-patch.js sub-router
|
|
268
|
+
|
|
269
|
+
export default router;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { settings } from '../db/index.js';
|
|
3
|
+
import { DEFAULT_SESSION_TITLE_PROMPT } from '../services/summaryService.js';
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* GET /api/settings/token-weights
|
|
9
|
+
* Get current token cost weights
|
|
10
|
+
*/
|
|
11
|
+
router.get('/token-weights', (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const weights = settings.getTokenCostWeights();
|
|
14
|
+
res.json(weights);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('Error getting token weights:', error);
|
|
17
|
+
res.status(500).json({ error: 'Failed to get token weights' });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* PUT /api/settings/token-weights
|
|
23
|
+
* Update token cost weights
|
|
24
|
+
*/
|
|
25
|
+
router.put('/token-weights', (req, res) => {
|
|
26
|
+
try {
|
|
27
|
+
const { input, output, cacheRead, cacheCreation } = req.body;
|
|
28
|
+
|
|
29
|
+
// Validate required fields are present and are numbers
|
|
30
|
+
if (typeof input !== 'number' || typeof output !== 'number' ||
|
|
31
|
+
typeof cacheRead !== 'number' || typeof cacheCreation !== 'number') {
|
|
32
|
+
return res.status(400).json({
|
|
33
|
+
error: 'All weights (input, output, cacheRead, cacheCreation) must be numbers'
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate weights are positive
|
|
38
|
+
if (input < 0 || output < 0 || cacheRead < 0 || cacheCreation < 0) {
|
|
39
|
+
return res.status(400).json({
|
|
40
|
+
error: 'All weights must be non-negative numbers'
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const updatedWeights = settings.setTokenCostWeights({
|
|
45
|
+
input,
|
|
46
|
+
output,
|
|
47
|
+
cacheRead,
|
|
48
|
+
cacheCreation,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
res.json(updatedWeights);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Error updating token weights:', error);
|
|
54
|
+
res.status(500).json({ error: 'Failed to update token weights' });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* DELETE /api/settings/token-weights
|
|
60
|
+
* Reset token cost weights to defaults
|
|
61
|
+
*/
|
|
62
|
+
router.delete('/token-weights', (req, res) => {
|
|
63
|
+
try {
|
|
64
|
+
const defaults = settings.resetTokenCostWeights();
|
|
65
|
+
res.json(defaults);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Error resetting token weights:', error);
|
|
68
|
+
res.status(500).json({ error: 'Failed to reset token weights' });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* GET /api/settings/summary
|
|
74
|
+
* Get summary settings
|
|
75
|
+
*/
|
|
76
|
+
router.get('/summary', (req, res) => {
|
|
77
|
+
try {
|
|
78
|
+
const summarySettings = settings.getSummarySettings();
|
|
79
|
+
// Include the default prompt for UI display/editing
|
|
80
|
+
res.json({
|
|
81
|
+
...summarySettings,
|
|
82
|
+
defaultSessionTitlePrompt: DEFAULT_SESSION_TITLE_PROMPT,
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error('Error getting summary settings:', error);
|
|
86
|
+
res.status(500).json({ error: 'Failed to get summary settings' });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* PUT /api/settings/summary
|
|
92
|
+
* Update summary settings
|
|
93
|
+
*/
|
|
94
|
+
router.put('/summary', (req, res) => {
|
|
95
|
+
try {
|
|
96
|
+
const { disableSessionSummaries, sessionTitlePrompt } = req.body;
|
|
97
|
+
|
|
98
|
+
// Validate that all required fields are present
|
|
99
|
+
if (typeof disableSessionSummaries !== 'boolean' ||
|
|
100
|
+
typeof sessionTitlePrompt !== 'string') {
|
|
101
|
+
return res.status(400).json({
|
|
102
|
+
error: 'Invalid summary settings. disableSessionSummaries must be a boolean, sessionTitlePrompt must be a string'
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const updatedSettings = settings.setSummarySettings({
|
|
107
|
+
disableSessionSummaries,
|
|
108
|
+
sessionTitlePrompt,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Include the default prompt for UI display/editing
|
|
112
|
+
res.json({
|
|
113
|
+
...updatedSettings,
|
|
114
|
+
defaultSessionTitlePrompt: DEFAULT_SESSION_TITLE_PROMPT,
|
|
115
|
+
});
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error('Error updating summary settings:', error);
|
|
118
|
+
res.status(500).json({ error: 'Failed to update summary settings' });
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* DELETE /api/settings/summary
|
|
124
|
+
* Reset summary settings to defaults
|
|
125
|
+
*/
|
|
126
|
+
router.delete('/summary', (req, res) => {
|
|
127
|
+
try {
|
|
128
|
+
const defaults = settings.resetSummarySettings();
|
|
129
|
+
// Include the default prompt for UI display/editing
|
|
130
|
+
res.json({
|
|
131
|
+
...defaults,
|
|
132
|
+
defaultSessionTitlePrompt: DEFAULT_SESSION_TITLE_PROMPT,
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Error resetting summary settings:', error);
|
|
136
|
+
res.status(500).json({ error: 'Failed to reset summary settings' });
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* GET /api/settings/general
|
|
142
|
+
* Get general settings (includes privacy settings)
|
|
143
|
+
*/
|
|
144
|
+
router.get('/general', (req, res) => {
|
|
145
|
+
try {
|
|
146
|
+
const generalSettings = settings.getGeneralSettings();
|
|
147
|
+
res.json(generalSettings);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error('Error getting general settings:', error);
|
|
150
|
+
res.status(500).json({ error: 'Failed to get general settings' });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* PUT /api/settings/general
|
|
156
|
+
* Update general settings
|
|
157
|
+
*/
|
|
158
|
+
router.put('/general', (req, res) => {
|
|
159
|
+
try {
|
|
160
|
+
const { disableAnalytics } = req.body;
|
|
161
|
+
|
|
162
|
+
// Validate that disableAnalytics is a boolean
|
|
163
|
+
if (typeof disableAnalytics !== 'boolean') {
|
|
164
|
+
return res.status(400).json({
|
|
165
|
+
error: 'Invalid general settings. disableAnalytics must be a boolean'
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const updatedSettings = settings.setGeneralSettings({
|
|
170
|
+
disableAnalytics,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
res.json(updatedSettings);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error('Error updating general settings:', error);
|
|
176
|
+
res.status(500).json({ error: 'Failed to update general settings' });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* DELETE /api/settings/general
|
|
182
|
+
* Reset general settings to defaults
|
|
183
|
+
*/
|
|
184
|
+
router.delete('/general', (req, res) => {
|
|
185
|
+
try {
|
|
186
|
+
const defaults = settings.resetGeneralSettings();
|
|
187
|
+
res.json(defaults);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('Error resetting general settings:', error);
|
|
190
|
+
res.status(500).json({ error: 'Failed to reset general settings' });
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export default router;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { sessionTemplates } from '../database.js';
|
|
3
|
+
import { CreateSessionTemplateRequest, UpdateSessionTemplateRequest } from '../../../shared/src/contracts/templates.js';
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
|
|
7
|
+
// GET /api/templates - List all global templates
|
|
8
|
+
router.get('/', (_req, res) => {
|
|
9
|
+
const templates = sessionTemplates.getGlobal();
|
|
10
|
+
res.json(templates);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// POST /api/templates - Create global template
|
|
14
|
+
router.post('/', (req, res) => {
|
|
15
|
+
const result = CreateSessionTemplateRequest.safeParse(req.body);
|
|
16
|
+
if (!result.success) {
|
|
17
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const template = sessionTemplates.create({
|
|
21
|
+
projectId: null, // Global template
|
|
22
|
+
...result.data,
|
|
23
|
+
});
|
|
24
|
+
res.status(201).json(template);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// GET /api/templates/:id - Get template by ID
|
|
28
|
+
router.get('/:id', (req, res) => {
|
|
29
|
+
const template = sessionTemplates.getById(req.params.id);
|
|
30
|
+
if (!template) {
|
|
31
|
+
return res.status(404).json({ error: 'Template not found' });
|
|
32
|
+
}
|
|
33
|
+
res.json(template);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// PATCH /api/templates/:id - Update template
|
|
37
|
+
router.patch('/:id', (req, res) => {
|
|
38
|
+
const template = sessionTemplates.getById(req.params.id);
|
|
39
|
+
if (!template) {
|
|
40
|
+
return res.status(404).json({ error: 'Template not found' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = UpdateSessionTemplateRequest.safeParse(req.body);
|
|
44
|
+
if (!result.success) {
|
|
45
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const updated = sessionTemplates.update(req.params.id, result.data);
|
|
49
|
+
res.json(updated);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// DELETE /api/templates/:id - Delete template
|
|
53
|
+
router.delete('/:id', (req, res) => {
|
|
54
|
+
const template = sessionTemplates.getById(req.params.id);
|
|
55
|
+
if (!template) {
|
|
56
|
+
return res.status(404).json({ error: 'Template not found' });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
sessionTemplates.delete(req.params.id);
|
|
60
|
+
res.status(204).send();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export default router;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import apiRouter from './api/index.js';
|
|
6
|
+
import { MAX_JSON_SIZE } from '../../shared/src/index.js';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create Express application
|
|
13
|
+
* @param {Object} options
|
|
14
|
+
* @param {boolean} options.production - Enable production mode
|
|
15
|
+
* @returns {express.Application}
|
|
16
|
+
*/
|
|
17
|
+
export function createApp(options = {}) {
|
|
18
|
+
const app = express();
|
|
19
|
+
|
|
20
|
+
// CORS (allow all in dev)
|
|
21
|
+
app.use(cors());
|
|
22
|
+
|
|
23
|
+
// Body parsing
|
|
24
|
+
app.use(express.json({ limit: MAX_JSON_SIZE }));
|
|
25
|
+
app.use(express.urlencoded({ extended: true, limit: MAX_JSON_SIZE }));
|
|
26
|
+
|
|
27
|
+
// Multipart form data parsing is handled per-route with specific middleware
|
|
28
|
+
// to avoid conflicts between upload.single() and upload.array() across different endpoints
|
|
29
|
+
|
|
30
|
+
// API routes
|
|
31
|
+
app.use('/api', apiRouter);
|
|
32
|
+
|
|
33
|
+
// Static files and SPA fallback (production only - in dev, Vite serves frontend)
|
|
34
|
+
if (options.production) {
|
|
35
|
+
const staticPath = join(__dirname, '../../web/dist');
|
|
36
|
+
app.use(express.static(staticPath));
|
|
37
|
+
|
|
38
|
+
// SPA fallback for client-side routing
|
|
39
|
+
app.get('/*', (_req, res) => {
|
|
40
|
+
res.sendFile(join(staticPath, 'index.html'));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Error handler
|
|
45
|
+
app.use((err, _req, res, _next) => {
|
|
46
|
+
console.error('Server error:', err);
|
|
47
|
+
res.status(500).json({ error: err.message || 'Internal server error' });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return app;
|
|
51
|
+
}
|