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,315 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Canvas item repository class
|
|
6
|
+
*/
|
|
7
|
+
export class CanvasItemRepository extends BaseRepository {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('canvas_items', CanvasItemRepository.#mapCanvasItem);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static #mapCanvasItem(row) {
|
|
13
|
+
// Parse JSON data for json type
|
|
14
|
+
let data = row.data;
|
|
15
|
+
if (row.type === 'json' && typeof data === 'string') {
|
|
16
|
+
try {
|
|
17
|
+
data = JSON.parse(data);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
// If parsing fails, keep as string
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
id: row.id,
|
|
25
|
+
sessionId: row.session_id,
|
|
26
|
+
type: row.type,
|
|
27
|
+
content: row.content,
|
|
28
|
+
data,
|
|
29
|
+
mimeType: row.mime_type,
|
|
30
|
+
filename: row.filename,
|
|
31
|
+
width: row.width,
|
|
32
|
+
height: row.height,
|
|
33
|
+
deletedAt: row.deleted_at,
|
|
34
|
+
createdAt: row.created_at,
|
|
35
|
+
updatedAt: row.updated_at,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
create(sessionId, data) {
|
|
40
|
+
const id = databaseManager.generateId();
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
|
|
43
|
+
// Serialize data field to JSON if it's an object
|
|
44
|
+
let dataValue = data.data || null;
|
|
45
|
+
if (dataValue !== null && typeof dataValue === 'object') {
|
|
46
|
+
dataValue = JSON.stringify(dataValue);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.db
|
|
50
|
+
.prepare(
|
|
51
|
+
`INSERT INTO canvas_items (id, session_id, type, content, data, mime_type, filename, width, height, created_at, updated_at)
|
|
52
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
53
|
+
)
|
|
54
|
+
.run(
|
|
55
|
+
id,
|
|
56
|
+
sessionId,
|
|
57
|
+
data.type,
|
|
58
|
+
data.content || null,
|
|
59
|
+
dataValue,
|
|
60
|
+
data.mimeType || null,
|
|
61
|
+
data.filename || null,
|
|
62
|
+
data.width || null,
|
|
63
|
+
data.height || null,
|
|
64
|
+
now,
|
|
65
|
+
now
|
|
66
|
+
);
|
|
67
|
+
return this.getById(id);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getBySessionId(sessionId) {
|
|
71
|
+
const rows = this.db
|
|
72
|
+
.prepare('SELECT * FROM canvas_items WHERE session_id = ? AND deleted_at IS NULL ORDER BY created_at DESC')
|
|
73
|
+
.all(sessionId);
|
|
74
|
+
return this.mapAll(rows);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get only the latest version of each file for a session (for agent-facing API)
|
|
79
|
+
* Returns one item per unique filename, always the newest version
|
|
80
|
+
* @param {string} sessionId
|
|
81
|
+
* @returns {Array} Array of latest canvas items (one per filename)
|
|
82
|
+
*/
|
|
83
|
+
getLatestVersionsBySessionId(sessionId) {
|
|
84
|
+
// Use a subquery to get the max rowid (which is unique and auto-incrementing) for each filename
|
|
85
|
+
// rowid is more reliable than created_at since items can have identical timestamps
|
|
86
|
+
const rows = this.db
|
|
87
|
+
.prepare(
|
|
88
|
+
`SELECT ci.*
|
|
89
|
+
FROM canvas_items ci
|
|
90
|
+
INNER JOIN (
|
|
91
|
+
SELECT filename, MAX(rowid) as max_rowid
|
|
92
|
+
FROM canvas_items
|
|
93
|
+
WHERE session_id = ? AND deleted_at IS NULL
|
|
94
|
+
GROUP BY filename
|
|
95
|
+
) latest ON ci.filename = latest.filename AND ci.rowid = latest.max_rowid
|
|
96
|
+
WHERE ci.session_id = ? AND ci.deleted_at IS NULL
|
|
97
|
+
ORDER BY ci.created_at DESC`
|
|
98
|
+
)
|
|
99
|
+
.all(sessionId, sessionId);
|
|
100
|
+
return this.mapAll(rows);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get all versions of a file by filename, ordered newest first
|
|
105
|
+
* @param {string} sessionId
|
|
106
|
+
* @param {string} filename
|
|
107
|
+
* @returns {Array} Array of canvas items with matching filename
|
|
108
|
+
*/
|
|
109
|
+
getAllVersionsByFilename(sessionId, filename) {
|
|
110
|
+
const rows = this.db
|
|
111
|
+
.prepare(
|
|
112
|
+
`SELECT * FROM canvas_items
|
|
113
|
+
WHERE session_id = ? AND filename = ? AND deleted_at IS NULL
|
|
114
|
+
ORDER BY created_at DESC, rowid DESC`
|
|
115
|
+
)
|
|
116
|
+
.all(sessionId, filename);
|
|
117
|
+
return this.mapAll(rows);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get only deleted items for trash view
|
|
122
|
+
* @param {string} sessionId
|
|
123
|
+
* @returns {Array} Array of deleted canvas items
|
|
124
|
+
*/
|
|
125
|
+
getDeletedBySessionId(sessionId) {
|
|
126
|
+
const rows = this.db
|
|
127
|
+
.prepare('SELECT * FROM canvas_items WHERE session_id = ? AND deleted_at IS NOT NULL ORDER BY deleted_at DESC')
|
|
128
|
+
.all(sessionId);
|
|
129
|
+
return this.mapAll(rows);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Soft delete: set deleted_at timestamp
|
|
134
|
+
* @param {string} itemId
|
|
135
|
+
* @returns {Object} The updated item
|
|
136
|
+
*/
|
|
137
|
+
softDelete(itemId) {
|
|
138
|
+
const now = Date.now();
|
|
139
|
+
this.db
|
|
140
|
+
.prepare('UPDATE canvas_items SET deleted_at = ? WHERE id = ?')
|
|
141
|
+
.run(now, itemId);
|
|
142
|
+
return this.getById(itemId);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Recover: clear deleted_at timestamp
|
|
147
|
+
* @param {string} itemId
|
|
148
|
+
* @returns {Object} The recovered item
|
|
149
|
+
*/
|
|
150
|
+
recover(itemId) {
|
|
151
|
+
const now = Date.now();
|
|
152
|
+
this.db
|
|
153
|
+
.prepare('UPDATE canvas_items SET deleted_at = NULL, updated_at = ? WHERE id = ?')
|
|
154
|
+
.run(now, itemId);
|
|
155
|
+
return this.getById(itemId);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Recover all versions of a file
|
|
160
|
+
* @param {string} sessionId
|
|
161
|
+
* @param {string} filename
|
|
162
|
+
*/
|
|
163
|
+
recoverByFilename(sessionId, filename) {
|
|
164
|
+
const now = Date.now();
|
|
165
|
+
this.db
|
|
166
|
+
.prepare('UPDATE canvas_items SET deleted_at = NULL, updated_at = ? WHERE session_id = ? AND filename = ? AND deleted_at IS NOT NULL')
|
|
167
|
+
.run(now, sessionId, filename);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Permanent delete (for emptying trash)
|
|
172
|
+
* @param {string} itemId
|
|
173
|
+
*/
|
|
174
|
+
permanentDelete(itemId) {
|
|
175
|
+
this.db.prepare('DELETE FROM canvas_items WHERE id = ?').run(itemId);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Expand item IDs to include all versions sharing the same filename.
|
|
180
|
+
* For each given ID, finds the item's filename + session, then collects
|
|
181
|
+
* all items with that filename in that session.
|
|
182
|
+
* @param {string[]} itemIds - Array of item IDs
|
|
183
|
+
* @param {object} options
|
|
184
|
+
* @param {boolean} [options.deletedOnly] - Only include deleted items (for trash operations)
|
|
185
|
+
* @param {boolean} [options.activeOnly] - Only include active (non-deleted) items
|
|
186
|
+
* @returns {string[]} Expanded array of item IDs (deduplicated)
|
|
187
|
+
*/
|
|
188
|
+
expandToAllVersions(itemIds, { deletedOnly = false, activeOnly = false } = {}) {
|
|
189
|
+
if (!itemIds || itemIds.length === 0) return [];
|
|
190
|
+
|
|
191
|
+
const allIds = new Set();
|
|
192
|
+
|
|
193
|
+
for (const itemId of itemIds) {
|
|
194
|
+
const item = this.getById(itemId);
|
|
195
|
+
if (!item || !item.filename) {
|
|
196
|
+
allIds.add(itemId);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let filter = 'session_id = ? AND filename = ?';
|
|
201
|
+
if (deletedOnly) filter += ' AND deleted_at IS NOT NULL';
|
|
202
|
+
if (activeOnly) filter += ' AND deleted_at IS NULL';
|
|
203
|
+
|
|
204
|
+
const siblings = this.db
|
|
205
|
+
.prepare(`SELECT id FROM canvas_items WHERE ${filter}`)
|
|
206
|
+
.all(item.sessionId, item.filename);
|
|
207
|
+
|
|
208
|
+
for (const sibling of siblings) {
|
|
209
|
+
allIds.add(sibling.id);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return [...allIds];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Duplicates all canvas items from one session to another.
|
|
218
|
+
* Only copies non-deleted items.
|
|
219
|
+
* @param {string} sourceSessionId - Source session ID
|
|
220
|
+
* @param {string} targetSessionId - Target session ID
|
|
221
|
+
*/
|
|
222
|
+
/**
|
|
223
|
+
* Update the content of an existing canvas item in-place
|
|
224
|
+
* @param {string} itemId
|
|
225
|
+
* @param {string} content
|
|
226
|
+
* @returns {Object|undefined} The updated item, or undefined if not found
|
|
227
|
+
*/
|
|
228
|
+
updateContent(itemId, content) {
|
|
229
|
+
const now = Date.now();
|
|
230
|
+
const result = this.db
|
|
231
|
+
.prepare('UPDATE canvas_items SET content = ?, updated_at = ? WHERE id = ?')
|
|
232
|
+
.run(content, now, itemId);
|
|
233
|
+
if (result.changes === 0) return undefined;
|
|
234
|
+
return this.getById(itemId);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
duplicateForSession(sourceSessionId, targetSessionId) {
|
|
238
|
+
const items = this.getBySessionId(sourceSessionId);
|
|
239
|
+
|
|
240
|
+
for (const item of items) {
|
|
241
|
+
this.create(targetSessionId, {
|
|
242
|
+
type: item.type,
|
|
243
|
+
content: item.content,
|
|
244
|
+
data: item.data,
|
|
245
|
+
mimeType: item.mimeType,
|
|
246
|
+
filename: item.filename,
|
|
247
|
+
width: item.width,
|
|
248
|
+
height: item.height,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Soft delete multiple items atomically
|
|
255
|
+
* @param {string[]} itemIds - Array of item IDs to soft delete
|
|
256
|
+
* @returns {number} Count of items actually deleted
|
|
257
|
+
*/
|
|
258
|
+
softDeleteBatch(itemIds) {
|
|
259
|
+
if (!itemIds || itemIds.length === 0) {
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const now = Date.now();
|
|
264
|
+
const placeholders = itemIds.map(() => '?').join(',');
|
|
265
|
+
|
|
266
|
+
const updateStmt = this.db.prepare(
|
|
267
|
+
`UPDATE canvas_items SET deleted_at = ? WHERE id IN (${placeholders}) AND deleted_at IS NULL`
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const result = updateStmt.run(now, ...itemIds);
|
|
271
|
+
return result.changes || 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Recover multiple items atomically
|
|
276
|
+
* @param {string[]} itemIds - Array of item IDs to recover
|
|
277
|
+
* @returns {number} Count of items actually recovered
|
|
278
|
+
*/
|
|
279
|
+
recoverBatch(itemIds) {
|
|
280
|
+
if (!itemIds || itemIds.length === 0) {
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const now = Date.now();
|
|
285
|
+
const placeholders = itemIds.map(() => '?').join(',');
|
|
286
|
+
|
|
287
|
+
const updateStmt = this.db.prepare(
|
|
288
|
+
`UPDATE canvas_items SET deleted_at = NULL, updated_at = ? WHERE id IN (${placeholders}) AND deleted_at IS NOT NULL`
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const result = updateStmt.run(now, ...itemIds);
|
|
292
|
+
return result.changes || 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Permanently delete multiple items atomically
|
|
297
|
+
* @param {string[]} itemIds - Array of item IDs to permanently delete (must be from trash)
|
|
298
|
+
* @returns {number} Count of items actually deleted
|
|
299
|
+
*/
|
|
300
|
+
permanentDeleteBatch(itemIds) {
|
|
301
|
+
if (!itemIds || itemIds.length === 0) {
|
|
302
|
+
return 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Only allow permanent deletion of items that are in trash (deleted_at IS NOT NULL)
|
|
306
|
+
const placeholders = itemIds.map(() => '?').join(',');
|
|
307
|
+
|
|
308
|
+
const deleteStmt = this.db.prepare(
|
|
309
|
+
`DELETE FROM canvas_items WHERE id IN (${placeholders}) AND deleted_at IS NOT NULL`
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const result = deleteStmt.run(...itemIds);
|
|
313
|
+
return result.changes || 0;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Command button repository class
|
|
6
|
+
*/
|
|
7
|
+
export class CommandButtonRepository extends BaseRepository {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('command_buttons', CommandButtonRepository.#mapButton);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static #mapButton(row) {
|
|
13
|
+
return {
|
|
14
|
+
id: row.id,
|
|
15
|
+
projectId: row.project_id,
|
|
16
|
+
label: row.label,
|
|
17
|
+
command: row.command,
|
|
18
|
+
sortOrder: row.sort_order,
|
|
19
|
+
showOnList: row.show_on_list === 1,
|
|
20
|
+
createdAt: row.created_at,
|
|
21
|
+
updatedAt: row.updated_at,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
create({ projectId, label, command, sortOrder = 0, showOnList = false }) {
|
|
26
|
+
const id = databaseManager.generateId();
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
this.db
|
|
29
|
+
.prepare(
|
|
30
|
+
`INSERT INTO command_buttons (id, project_id, label, command, sort_order, show_on_list, created_at, updated_at)
|
|
31
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
32
|
+
)
|
|
33
|
+
.run(id, projectId, label, command, sortOrder, showOnList ? 1 : 0, now, now);
|
|
34
|
+
return this.getById(id);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getByProjectId(projectId) {
|
|
38
|
+
const rows = this.db
|
|
39
|
+
.prepare('SELECT * FROM command_buttons WHERE project_id = ? ORDER BY sort_order ASC, created_at ASC')
|
|
40
|
+
.all(projectId);
|
|
41
|
+
return this.mapAll(rows);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
update(id, data) {
|
|
45
|
+
const updates = [];
|
|
46
|
+
const values = [];
|
|
47
|
+
|
|
48
|
+
if (data.label !== undefined) {
|
|
49
|
+
updates.push('label = ?');
|
|
50
|
+
values.push(data.label);
|
|
51
|
+
}
|
|
52
|
+
if (data.command !== undefined) {
|
|
53
|
+
updates.push('command = ?');
|
|
54
|
+
values.push(data.command);
|
|
55
|
+
}
|
|
56
|
+
if (data.sortOrder !== undefined) {
|
|
57
|
+
updates.push('sort_order = ?');
|
|
58
|
+
values.push(data.sortOrder);
|
|
59
|
+
}
|
|
60
|
+
if (data.showOnList !== undefined) {
|
|
61
|
+
updates.push('show_on_list = ?');
|
|
62
|
+
values.push(data.showOnList ? 1 : 0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (updates.length === 0) return this.getById(id);
|
|
66
|
+
|
|
67
|
+
updates.push('updated_at = ?');
|
|
68
|
+
values.push(Date.now());
|
|
69
|
+
values.push(id);
|
|
70
|
+
|
|
71
|
+
this.db.prepare(`UPDATE command_buttons SET ${updates.join(', ')} WHERE id = ?`).run(...values);
|
|
72
|
+
|
|
73
|
+
return this.getById(id);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Command run repository class for persisting command execution history
|
|
5
|
+
*/
|
|
6
|
+
export class CommandRunRepository extends BaseRepository {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('command_runs', CommandRunRepository.#mapRun);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static #mapRun(row) {
|
|
12
|
+
return {
|
|
13
|
+
id: row.id,
|
|
14
|
+
sessionId: row.session_id,
|
|
15
|
+
buttonId: row.button_id,
|
|
16
|
+
status: row.status,
|
|
17
|
+
output: row.output || '',
|
|
18
|
+
exitCode: row.exit_code,
|
|
19
|
+
startedAt: row.started_at,
|
|
20
|
+
completedAt: row.completed_at,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
create({ id, sessionId, buttonId }) {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
this.db
|
|
27
|
+
.prepare(
|
|
28
|
+
`INSERT INTO command_runs (id, session_id, button_id, status, output, started_at)
|
|
29
|
+
VALUES (?, ?, ?, 'running', '', ?)`
|
|
30
|
+
)
|
|
31
|
+
.run(id, sessionId, buttonId, now);
|
|
32
|
+
return this.getById(id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Append text to run output (buffered for performance)
|
|
37
|
+
*/
|
|
38
|
+
appendOutput(runId, text) {
|
|
39
|
+
if (!text) return;
|
|
40
|
+
this.db
|
|
41
|
+
.prepare(`UPDATE command_runs SET output = output || ? WHERE id = ?`)
|
|
42
|
+
.run(text, runId);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Mark run as completed with exit code and final output
|
|
47
|
+
*/
|
|
48
|
+
complete(runId, exitCode, finalOutput) {
|
|
49
|
+
const status = exitCode === 0 ? 'success' : 'error';
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
this.db
|
|
52
|
+
.prepare(
|
|
53
|
+
`UPDATE command_runs SET status = ?, output = ?, exit_code = ?, completed_at = ? WHERE id = ?`
|
|
54
|
+
)
|
|
55
|
+
.run(status, finalOutput, exitCode, now, runId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Mark run as killed
|
|
60
|
+
*/
|
|
61
|
+
markKilled(runId, finalOutput) {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
this.db
|
|
64
|
+
.prepare(
|
|
65
|
+
`UPDATE command_runs SET status = 'killed', output = ?, completed_at = ? WHERE id = ?`
|
|
66
|
+
)
|
|
67
|
+
.run(finalOutput, now, runId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get all runs for a session (both running and recent completed)
|
|
72
|
+
*/
|
|
73
|
+
getBySessionId(sessionId) {
|
|
74
|
+
const rows = this.db
|
|
75
|
+
.prepare('SELECT * FROM command_runs WHERE session_id = ? ORDER BY started_at DESC')
|
|
76
|
+
.all(sessionId);
|
|
77
|
+
return this.mapAll(rows);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get runs for a session from last X milliseconds (recent runs)
|
|
82
|
+
* If includeRunning is true, also include running commands regardless of age
|
|
83
|
+
*/
|
|
84
|
+
getRecentBySessionId(sessionId, windowMs = 3600000, includeRunning = true) {
|
|
85
|
+
const cutoffTime = Date.now() - windowMs;
|
|
86
|
+
let query = `SELECT * FROM command_runs WHERE session_id = ?`;
|
|
87
|
+
|
|
88
|
+
if (includeRunning) {
|
|
89
|
+
query += ` AND (started_at > ? OR status = 'running')`;
|
|
90
|
+
} else {
|
|
91
|
+
query += ` AND started_at > ?`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
query += ` ORDER BY started_at DESC`;
|
|
95
|
+
|
|
96
|
+
const rows = this.db.prepare(query).all(sessionId, cutoffTime);
|
|
97
|
+
return this.mapAll(rows);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get a single run by ID
|
|
102
|
+
*/
|
|
103
|
+
getById(id) {
|
|
104
|
+
const row = this.db.prepare('SELECT * FROM command_runs WHERE id = ?').get(id);
|
|
105
|
+
return this.map(row);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get most recent run for a button
|
|
110
|
+
*/
|
|
111
|
+
getLastRunForButton(buttonId) {
|
|
112
|
+
const row = this.db
|
|
113
|
+
.prepare('SELECT * FROM command_runs WHERE button_id = ? ORDER BY started_at DESC LIMIT 1')
|
|
114
|
+
.get(buttonId);
|
|
115
|
+
return this.map(row);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Delete runs older than specified milliseconds
|
|
120
|
+
*/
|
|
121
|
+
deleteOlderThan(ageMs) {
|
|
122
|
+
const cutoffTime = Date.now() - ageMs;
|
|
123
|
+
const result = this.db
|
|
124
|
+
.prepare('DELETE FROM command_runs WHERE completed_at < ?')
|
|
125
|
+
.run(cutoffTime);
|
|
126
|
+
return result.changes;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Delete a single run by ID
|
|
131
|
+
* @param {string} id - Run ID
|
|
132
|
+
* @returns {number} Number of rows deleted (0 or 1)
|
|
133
|
+
*/
|
|
134
|
+
deleteById(id) {
|
|
135
|
+
const result = this.db.prepare('DELETE FROM command_runs WHERE id = ?').run(id);
|
|
136
|
+
return result.changes;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Delete all non-running runs for a button within a session.
|
|
141
|
+
* Returns info about deleted runs (needed for WebSocket broadcasting).
|
|
142
|
+
* @param {string} buttonId - Button ID
|
|
143
|
+
* @param {string} sessionId - Session ID
|
|
144
|
+
* @returns {{ deletedCount: number, deletedRuns: Array<{ id: string, buttonId: string }> }}
|
|
145
|
+
*/
|
|
146
|
+
deleteByButtonAndSession(buttonId, sessionId) {
|
|
147
|
+
const deleteRuns = this.db.transaction(() => {
|
|
148
|
+
// First get the IDs of runs we'll delete (for broadcasting)
|
|
149
|
+
const runs = this.db
|
|
150
|
+
.prepare('SELECT id, button_id FROM command_runs WHERE button_id = ? AND session_id = ? AND status != ?')
|
|
151
|
+
.all(buttonId, sessionId, 'running');
|
|
152
|
+
|
|
153
|
+
const result = this.db
|
|
154
|
+
.prepare('DELETE FROM command_runs WHERE button_id = ? AND session_id = ? AND status != ?')
|
|
155
|
+
.run(buttonId, sessionId, 'running');
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
deletedCount: result.changes,
|
|
159
|
+
deletedRuns: runs.map(r => ({ id: r.id, buttonId: r.button_id })),
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return deleteRuns();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Delete all runs for a session (useful for testing or cleanup)
|
|
168
|
+
*/
|
|
169
|
+
deleteBySessionId(sessionId) {
|
|
170
|
+
const result = this.db
|
|
171
|
+
.prepare('DELETE FROM command_runs WHERE session_id = ?')
|
|
172
|
+
.run(sessionId);
|
|
173
|
+
return result.changes;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get the latest run for each (session_id, button_id) combination within a project
|
|
178
|
+
* Returns the most recent run per button per session regardless of age
|
|
179
|
+
*/
|
|
180
|
+
getLatestRunsForProject(projectId) {
|
|
181
|
+
const rows = this.db
|
|
182
|
+
.prepare(
|
|
183
|
+
`SELECT *
|
|
184
|
+
FROM (
|
|
185
|
+
SELECT cr.id, cr.session_id, cr.button_id, cr.status, cr.exit_code, cr.started_at, cr.completed_at,
|
|
186
|
+
ROW_NUMBER() OVER (PARTITION BY cr.session_id, cr.button_id ORDER BY COALESCE(cr.completed_at, cr.started_at) DESC, cr.id DESC) as rn
|
|
187
|
+
FROM command_runs cr
|
|
188
|
+
INNER JOIN sessions s ON cr.session_id = s.id
|
|
189
|
+
WHERE s.project_id = ?
|
|
190
|
+
)
|
|
191
|
+
WHERE rn = 1
|
|
192
|
+
ORDER BY COALESCE(completed_at, started_at) DESC, id DESC`
|
|
193
|
+
)
|
|
194
|
+
.all(projectId);
|
|
195
|
+
return this.mapAll(rows);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get the latest run for each button in a session (one per button)
|
|
200
|
+
* @param {string} sessionId - Session ID
|
|
201
|
+
* @returns {Array} Array of CommandRun objects
|
|
202
|
+
*/
|
|
203
|
+
getLatestRunsForSession(sessionId) {
|
|
204
|
+
const rows = this.db
|
|
205
|
+
.prepare(
|
|
206
|
+
`SELECT *
|
|
207
|
+
FROM (
|
|
208
|
+
SELECT cr.id, cr.session_id, cr.button_id, cr.status, cr.exit_code, cr.output, cr.started_at, cr.completed_at,
|
|
209
|
+
ROW_NUMBER() OVER (PARTITION BY cr.button_id ORDER BY COALESCE(cr.completed_at, cr.started_at) DESC, cr.id DESC) as rn
|
|
210
|
+
FROM command_runs cr
|
|
211
|
+
WHERE cr.session_id = ?
|
|
212
|
+
)
|
|
213
|
+
WHERE rn = 1
|
|
214
|
+
ORDER BY COALESCE(completed_at, started_at) DESC, id DESC`
|
|
215
|
+
)
|
|
216
|
+
.all(sessionId);
|
|
217
|
+
return this.mapAll(rows);
|
|
218
|
+
}
|
|
219
|
+
}
|