@gokulvenkatareddy/cortex 0.1.7

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.
Files changed (299) hide show
  1. package/README.md +1295 -0
  2. package/apps/octogent/.github/workflows/ci.yml +40 -0
  3. package/apps/octogent/.shims/claude +4 -0
  4. package/apps/octogent/AGENTS.md +71 -0
  5. package/apps/octogent/CONTRIBUTING.md +72 -0
  6. package/apps/octogent/LICENSE +21 -0
  7. package/apps/octogent/README.md +184 -0
  8. package/apps/octogent/apps/api/AGENTS.md +32 -0
  9. package/apps/octogent/apps/api/package.json +19 -0
  10. package/apps/octogent/apps/api/src/agentStateDetection.ts +181 -0
  11. package/apps/octogent/apps/api/src/claudeSessionScanner.ts +235 -0
  12. package/apps/octogent/apps/api/src/claudeSkills.ts +182 -0
  13. package/apps/octogent/apps/api/src/claudeUsage.ts +922 -0
  14. package/apps/octogent/apps/api/src/cli.ts +595 -0
  15. package/apps/octogent/apps/api/src/codeIntelStore.ts +46 -0
  16. package/apps/octogent/apps/api/src/codexUsage.ts +278 -0
  17. package/apps/octogent/apps/api/src/createApiServer/codeIntelRoutes.ts +60 -0
  18. package/apps/octogent/apps/api/src/createApiServer/conversationRoutes.ts +128 -0
  19. package/apps/octogent/apps/api/src/createApiServer/deckRoutes.ts +873 -0
  20. package/apps/octogent/apps/api/src/createApiServer/gitParsers.ts +140 -0
  21. package/apps/octogent/apps/api/src/createApiServer/gitRoutes.ts +214 -0
  22. package/apps/octogent/apps/api/src/createApiServer/miscRoutes.ts +316 -0
  23. package/apps/octogent/apps/api/src/createApiServer/monitorParsers.ts +137 -0
  24. package/apps/octogent/apps/api/src/createApiServer/monitorRoutes.ts +95 -0
  25. package/apps/octogent/apps/api/src/createApiServer/requestHandler.ts +311 -0
  26. package/apps/octogent/apps/api/src/createApiServer/requestParsers.ts +25 -0
  27. package/apps/octogent/apps/api/src/createApiServer/routeHelpers.ts +97 -0
  28. package/apps/octogent/apps/api/src/createApiServer/security.ts +70 -0
  29. package/apps/octogent/apps/api/src/createApiServer/terminalParsers.ts +167 -0
  30. package/apps/octogent/apps/api/src/createApiServer/terminalRoutes.ts +315 -0
  31. package/apps/octogent/apps/api/src/createApiServer/types.ts +24 -0
  32. package/apps/octogent/apps/api/src/createApiServer/uiStateParsers.ts +255 -0
  33. package/apps/octogent/apps/api/src/createApiServer/upgradeHandler.ts +38 -0
  34. package/apps/octogent/apps/api/src/createApiServer/usageRoutes.ts +84 -0
  35. package/apps/octogent/apps/api/src/createApiServer.ts +176 -0
  36. package/apps/octogent/apps/api/src/deck/readDeckTentacles.ts +595 -0
  37. package/apps/octogent/apps/api/src/githubRepoSummary.ts +397 -0
  38. package/apps/octogent/apps/api/src/logging.ts +9 -0
  39. package/apps/octogent/apps/api/src/monitor/defaults.ts +3 -0
  40. package/apps/octogent/apps/api/src/monitor/index.ts +8 -0
  41. package/apps/octogent/apps/api/src/monitor/repository.ts +303 -0
  42. package/apps/octogent/apps/api/src/monitor/service.ts +349 -0
  43. package/apps/octogent/apps/api/src/monitor/types.ts +120 -0
  44. package/apps/octogent/apps/api/src/monitor/xProvider.ts +587 -0
  45. package/apps/octogent/apps/api/src/projectPersistence.ts +377 -0
  46. package/apps/octogent/apps/api/src/prompts/index.ts +10 -0
  47. package/apps/octogent/apps/api/src/prompts/promptResolver.ts +145 -0
  48. package/apps/octogent/apps/api/src/runtimeMetadata.ts +69 -0
  49. package/apps/octogent/apps/api/src/server.ts +80 -0
  50. package/apps/octogent/apps/api/src/setupState.ts +80 -0
  51. package/apps/octogent/apps/api/src/setupStatus.ts +174 -0
  52. package/apps/octogent/apps/api/src/startupPrerequisites.ts +146 -0
  53. package/apps/octogent/apps/api/src/terminalRuntime/channelMessaging.ts +87 -0
  54. package/apps/octogent/apps/api/src/terminalRuntime/claudeTranscript.ts +279 -0
  55. package/apps/octogent/apps/api/src/terminalRuntime/constants.ts +15 -0
  56. package/apps/octogent/apps/api/src/terminalRuntime/conversations.ts +492 -0
  57. package/apps/octogent/apps/api/src/terminalRuntime/gitOperations.ts +341 -0
  58. package/apps/octogent/apps/api/src/terminalRuntime/hookProcessor.ts +405 -0
  59. package/apps/octogent/apps/api/src/terminalRuntime/protocol.ts +46 -0
  60. package/apps/octogent/apps/api/src/terminalRuntime/ptyEnvironment.ts +50 -0
  61. package/apps/octogent/apps/api/src/terminalRuntime/registry.ts +423 -0
  62. package/apps/octogent/apps/api/src/terminalRuntime/sessionRuntime.ts +671 -0
  63. package/apps/octogent/apps/api/src/terminalRuntime/systemClients.ts +432 -0
  64. package/apps/octogent/apps/api/src/terminalRuntime/types.ts +157 -0
  65. package/apps/octogent/apps/api/src/terminalRuntime/worktreeManager.ts +135 -0
  66. package/apps/octogent/apps/api/src/terminalRuntime.ts +567 -0
  67. package/apps/octogent/apps/api/src/usageUtils.ts +16 -0
  68. package/apps/octogent/apps/api/src/ws-shim.d.ts +28 -0
  69. package/apps/octogent/apps/api/tests/agentStateDetection.test.ts +67 -0
  70. package/apps/octogent/apps/api/tests/claudeUsage.test.ts +583 -0
  71. package/apps/octogent/apps/api/tests/codexUsage.test.ts +107 -0
  72. package/apps/octogent/apps/api/tests/createApiServer.test.ts +3207 -0
  73. package/apps/octogent/apps/api/tests/githubRepoSummary.test.ts +100 -0
  74. package/apps/octogent/apps/api/tests/logging.test.ts +33 -0
  75. package/apps/octogent/apps/api/tests/monitorApi.test.ts +467 -0
  76. package/apps/octogent/apps/api/tests/monitorCore.test.ts +104 -0
  77. package/apps/octogent/apps/api/tests/promptResolver.test.ts +109 -0
  78. package/apps/octogent/apps/api/tests/protocol.test.ts +14 -0
  79. package/apps/octogent/apps/api/tests/sessionRuntime.test.ts +608 -0
  80. package/apps/octogent/apps/api/tests/startupPrerequisites.test.ts +70 -0
  81. package/apps/octogent/apps/api/tests/upgradeHandler.test.ts +40 -0
  82. package/apps/octogent/apps/api/tests/xMonitorProvider.test.ts +109 -0
  83. package/apps/octogent/apps/api/tsconfig.json +7 -0
  84. package/apps/octogent/apps/api/vitest.config.ts +7 -0
  85. package/apps/octogent/apps/web/AGENTS.md +38 -0
  86. package/apps/octogent/apps/web/index.html +13 -0
  87. package/apps/octogent/apps/web/package.json +32 -0
  88. package/apps/octogent/apps/web/public/octopus-favicon.svg +26 -0
  89. package/apps/octogent/apps/web/src/App.tsx +646 -0
  90. package/apps/octogent/apps/web/src/app/canvas/types.ts +34 -0
  91. package/apps/octogent/apps/web/src/app/codeIntelAggregation.ts +278 -0
  92. package/apps/octogent/apps/web/src/app/constants.ts +28 -0
  93. package/apps/octogent/apps/web/src/app/conversationNormalizers.ts +135 -0
  94. package/apps/octogent/apps/web/src/app/formatTimestamp.ts +18 -0
  95. package/apps/octogent/apps/web/src/app/githubMetrics.ts +76 -0
  96. package/apps/octogent/apps/web/src/app/githubNormalizers.ts +91 -0
  97. package/apps/octogent/apps/web/src/app/hooks/useAgentRuntimeStates.ts +18 -0
  98. package/apps/octogent/apps/web/src/app/hooks/useBackendLivenessPolling.ts +53 -0
  99. package/apps/octogent/apps/web/src/app/hooks/useCanvasGraphData.ts +449 -0
  100. package/apps/octogent/apps/web/src/app/hooks/useCanvasTransform.ts +260 -0
  101. package/apps/octogent/apps/web/src/app/hooks/useClaudeUsagePolling.ts +40 -0
  102. package/apps/octogent/apps/web/src/app/hooks/useClickOutside.ts +30 -0
  103. package/apps/octogent/apps/web/src/app/hooks/useCodeIntelRuntime.ts +83 -0
  104. package/apps/octogent/apps/web/src/app/hooks/useCodexUsagePolling.ts +35 -0
  105. package/apps/octogent/apps/web/src/app/hooks/useConsoleKeyboardShortcuts.ts +31 -0
  106. package/apps/octogent/apps/web/src/app/hooks/useConversationsRuntime.ts +377 -0
  107. package/apps/octogent/apps/web/src/app/hooks/useForceSimulation.ts +319 -0
  108. package/apps/octogent/apps/web/src/app/hooks/useGitHubPrimaryViewModel.ts +143 -0
  109. package/apps/octogent/apps/web/src/app/hooks/useGithubSummaryPolling.ts +28 -0
  110. package/apps/octogent/apps/web/src/app/hooks/useInitialColumnsHydration.ts +64 -0
  111. package/apps/octogent/apps/web/src/app/hooks/useMonitorRuntime.ts +220 -0
  112. package/apps/octogent/apps/web/src/app/hooks/usePersistedUiState.ts +536 -0
  113. package/apps/octogent/apps/web/src/app/hooks/usePollingData.ts +79 -0
  114. package/apps/octogent/apps/web/src/app/hooks/usePromptLibrary.ts +185 -0
  115. package/apps/octogent/apps/web/src/app/hooks/useTentacleGitLifecycle.ts +530 -0
  116. package/apps/octogent/apps/web/src/app/hooks/useTerminalCompletionNotification.ts +94 -0
  117. package/apps/octogent/apps/web/src/app/hooks/useTerminalMutations.ts +266 -0
  118. package/apps/octogent/apps/web/src/app/hooks/useTerminalStateReconciliation.ts +23 -0
  119. package/apps/octogent/apps/web/src/app/hooks/useUsageHeatmapPolling.ts +43 -0
  120. package/apps/octogent/apps/web/src/app/hooks/useWorkspaceSetup.ts +80 -0
  121. package/apps/octogent/apps/web/src/app/hotkeys.ts +31 -0
  122. package/apps/octogent/apps/web/src/app/monitorNormalizers.ts +145 -0
  123. package/apps/octogent/apps/web/src/app/notificationSounds.ts +164 -0
  124. package/apps/octogent/apps/web/src/app/terminalRuntimeStateStore.ts +261 -0
  125. package/apps/octogent/apps/web/src/app/terminalState.ts +21 -0
  126. package/apps/octogent/apps/web/src/app/types.ts +42 -0
  127. package/apps/octogent/apps/web/src/app/uiStateNormalizers.ts +113 -0
  128. package/apps/octogent/apps/web/src/app/usageNormalizers.ts +58 -0
  129. package/apps/octogent/apps/web/src/components/ActiveAgentsSidebar.tsx +60 -0
  130. package/apps/octogent/apps/web/src/components/ActivityPrimaryView.tsx +21 -0
  131. package/apps/octogent/apps/web/src/components/AgentStateBadge.tsx +47 -0
  132. package/apps/octogent/apps/web/src/components/CanvasPrimaryView.tsx +1532 -0
  133. package/apps/octogent/apps/web/src/components/ClearAllConversationsDialog.tsx +33 -0
  134. package/apps/octogent/apps/web/src/components/CodeIntelArcDiagram.tsx +245 -0
  135. package/apps/octogent/apps/web/src/components/CodeIntelPrimaryView.tsx +104 -0
  136. package/apps/octogent/apps/web/src/components/CodeIntelTreemap.tsx +138 -0
  137. package/apps/octogent/apps/web/src/components/ConsolePrimaryNav.tsx +31 -0
  138. package/apps/octogent/apps/web/src/components/ConversationsPrimaryView.tsx +243 -0
  139. package/apps/octogent/apps/web/src/components/DeckPrimaryView.tsx +613 -0
  140. package/apps/octogent/apps/web/src/components/DeleteTentacleDialog.tsx +91 -0
  141. package/apps/octogent/apps/web/src/components/EmptyOctopus.tsx +715 -0
  142. package/apps/octogent/apps/web/src/components/GitHubPrimaryView.tsx +494 -0
  143. package/apps/octogent/apps/web/src/components/MonitorPrimaryView.tsx +475 -0
  144. package/apps/octogent/apps/web/src/components/PrimaryViewRouter.tsx +99 -0
  145. package/apps/octogent/apps/web/src/components/PromptsPrimaryView.tsx +243 -0
  146. package/apps/octogent/apps/web/src/components/RuntimeStatusStrip.tsx +273 -0
  147. package/apps/octogent/apps/web/src/components/SettingsPrimaryView.tsx +92 -0
  148. package/apps/octogent/apps/web/src/components/SidebarActionPanel.tsx +124 -0
  149. package/apps/octogent/apps/web/src/components/SidebarConversationsList.tsx +279 -0
  150. package/apps/octogent/apps/web/src/components/SidebarPromptsList.tsx +116 -0
  151. package/apps/octogent/apps/web/src/components/TelemetryTape.tsx +106 -0
  152. package/apps/octogent/apps/web/src/components/TentacleGitActionsDialog.tsx +341 -0
  153. package/apps/octogent/apps/web/src/components/Terminal.tsx +524 -0
  154. package/apps/octogent/apps/web/src/components/TerminalPromptPicker.tsx +140 -0
  155. package/apps/octogent/apps/web/src/components/UsageHeatmap.tsx +702 -0
  156. package/apps/octogent/apps/web/src/components/canvas/CanvasTentaclePanel.tsx +485 -0
  157. package/apps/octogent/apps/web/src/components/canvas/CanvasTerminalColumn.tsx +89 -0
  158. package/apps/octogent/apps/web/src/components/canvas/DeleteAllTerminalsDialog.tsx +221 -0
  159. package/apps/octogent/apps/web/src/components/canvas/OctopusNode.tsx +307 -0
  160. package/apps/octogent/apps/web/src/components/canvas/SessionNode.tsx +185 -0
  161. package/apps/octogent/apps/web/src/components/deck/ActionCards.tsx +118 -0
  162. package/apps/octogent/apps/web/src/components/deck/AddTentacleForm.tsx +269 -0
  163. package/apps/octogent/apps/web/src/components/deck/DeckBottomActions.tsx +56 -0
  164. package/apps/octogent/apps/web/src/components/deck/TentaclePod.tsx +334 -0
  165. package/apps/octogent/apps/web/src/components/deck/WorkspaceSetupCard.tsx +105 -0
  166. package/apps/octogent/apps/web/src/components/deck/octopusVisuals.ts +72 -0
  167. package/apps/octogent/apps/web/src/components/terminalReplay.ts +62 -0
  168. package/apps/octogent/apps/web/src/components/terminalWheel.ts +54 -0
  169. package/apps/octogent/apps/web/src/components/ui/ActionButton.tsx +34 -0
  170. package/apps/octogent/apps/web/src/components/ui/ConfirmationDialog.tsx +86 -0
  171. package/apps/octogent/apps/web/src/components/ui/MarkdownContent.tsx +43 -0
  172. package/apps/octogent/apps/web/src/components/ui/SettingsToggle.tsx +34 -0
  173. package/apps/octogent/apps/web/src/components/ui/StatusBadge.tsx +24 -0
  174. package/apps/octogent/apps/web/src/main.tsx +17 -0
  175. package/apps/octogent/apps/web/src/runtime/HttpTerminalSnapshotReader.ts +87 -0
  176. package/apps/octogent/apps/web/src/runtime/runtimeEndpoints.ts +412 -0
  177. package/apps/octogent/apps/web/src/styles/chrome-and-buttons.css +272 -0
  178. package/apps/octogent/apps/web/src/styles/console-canvas-activity.css +358 -0
  179. package/apps/octogent/apps/web/src/styles/console-canvas-canvas.css +1843 -0
  180. package/apps/octogent/apps/web/src/styles/console-canvas-code-intel.css +227 -0
  181. package/apps/octogent/apps/web/src/styles/console-canvas-conversations.css +705 -0
  182. package/apps/octogent/apps/web/src/styles/console-canvas-deck.css +1524 -0
  183. package/apps/octogent/apps/web/src/styles/console-canvas-github.css +541 -0
  184. package/apps/octogent/apps/web/src/styles/console-canvas-monitor.css +595 -0
  185. package/apps/octogent/apps/web/src/styles/console-canvas-pixpack.css +81 -0
  186. package/apps/octogent/apps/web/src/styles/console-canvas-prompts.css +474 -0
  187. package/apps/octogent/apps/web/src/styles/console-canvas-settings.css +207 -0
  188. package/apps/octogent/apps/web/src/styles/console-chrome-status-nav.css +441 -0
  189. package/apps/octogent/apps/web/src/styles/console-overrides-telemetry.css +320 -0
  190. package/apps/octogent/apps/web/src/styles/console-theme-tokens.css +25 -0
  191. package/apps/octogent/apps/web/src/styles/cortex-theme.css +412 -0
  192. package/apps/octogent/apps/web/src/styles/foundation.css +100 -0
  193. package/apps/octogent/apps/web/src/styles/sidebar-and-scrollbars.css +447 -0
  194. package/apps/octogent/apps/web/src/styles/terminal-and-status.css +356 -0
  195. package/apps/octogent/apps/web/src/styles.css +25 -0
  196. package/apps/octogent/apps/web/src/types/ws.d.ts +23 -0
  197. package/apps/octogent/apps/web/tests/CanvasPrimaryView.test.tsx +347 -0
  198. package/apps/octogent/apps/web/tests/HttpTerminalSnapshotReader.test.tsx +54 -0
  199. package/apps/octogent/apps/web/tests/RuntimeStatusStrip.test.tsx +70 -0
  200. package/apps/octogent/apps/web/tests/Terminal.test.tsx +87 -0
  201. package/apps/octogent/apps/web/tests/add-tentacle-form.test.tsx +48 -0
  202. package/apps/octogent/apps/web/tests/app-github-runtime.test.tsx +162 -0
  203. package/apps/octogent/apps/web/tests/app-monitor-runtime.test.tsx +657 -0
  204. package/apps/octogent/apps/web/tests/app-shell-navigation.test.tsx +109 -0
  205. package/apps/octogent/apps/web/tests/app-swarm-refresh.test.tsx +268 -0
  206. package/apps/octogent/apps/web/tests/app-ui-state-persistence.test.tsx +116 -0
  207. package/apps/octogent/apps/web/tests/app-workspace-setup.test.tsx +217 -0
  208. package/apps/octogent/apps/web/tests/canvas-tentacle-panel.test.tsx +195 -0
  209. package/apps/octogent/apps/web/tests/delete-all-terminals-dialog.test.tsx +76 -0
  210. package/apps/octogent/apps/web/tests/githubMetrics.test.tsx +52 -0
  211. package/apps/octogent/apps/web/tests/hotkeys.test.tsx +44 -0
  212. package/apps/octogent/apps/web/tests/runtimeEndpoints.test.tsx +240 -0
  213. package/apps/octogent/apps/web/tests/setup.ts +39 -0
  214. package/apps/octogent/apps/web/tests/tentacle-pod.test.tsx +62 -0
  215. package/apps/octogent/apps/web/tests/terminalReplay.test.ts +71 -0
  216. package/apps/octogent/apps/web/tests/terminalState.test.tsx +49 -0
  217. package/apps/octogent/apps/web/tests/terminalWheel.test.tsx +51 -0
  218. package/apps/octogent/apps/web/tests/test-utils/appTestHarness.ts +48 -0
  219. package/apps/octogent/apps/web/tests/uiPrimitives.test.tsx +31 -0
  220. package/apps/octogent/apps/web/tests/useAgentRuntimeStates.test.tsx +47 -0
  221. package/apps/octogent/apps/web/tsconfig.json +8 -0
  222. package/apps/octogent/apps/web/vite.api.bundle.config.mts +32 -0
  223. package/apps/octogent/apps/web/vite.config.ts +22 -0
  224. package/apps/octogent/bin/octogent +3 -0
  225. package/apps/octogent/biome.json +21 -0
  226. package/apps/octogent/docs/concepts/mental-model.md +79 -0
  227. package/apps/octogent/docs/concepts/runtime-and-api.md +60 -0
  228. package/apps/octogent/docs/concepts/tentacles.md +85 -0
  229. package/apps/octogent/docs/getting-started/installation.md +54 -0
  230. package/apps/octogent/docs/getting-started/quickstart.md +79 -0
  231. package/apps/octogent/docs/guides/inter-agent-messaging.md +43 -0
  232. package/apps/octogent/docs/guides/orchestrating-child-agents.md +49 -0
  233. package/apps/octogent/docs/guides/working-with-todos.md +56 -0
  234. package/apps/octogent/docs/index.md +40 -0
  235. package/apps/octogent/docs/reference/api.md +103 -0
  236. package/apps/octogent/docs/reference/cli.md +71 -0
  237. package/apps/octogent/docs/reference/experimental-features.md +28 -0
  238. package/apps/octogent/docs/reference/filesystem-layout.md +62 -0
  239. package/apps/octogent/docs/reference/troubleshooting.md +49 -0
  240. package/apps/octogent/package.json +35 -0
  241. package/apps/octogent/packages/core/AGENTS.md +31 -0
  242. package/apps/octogent/packages/core/package.json +12 -0
  243. package/apps/octogent/packages/core/src/adapters/InMemoryTerminalSnapshotReader.ts +10 -0
  244. package/apps/octogent/packages/core/src/application/buildTerminalList.ts +13 -0
  245. package/apps/octogent/packages/core/src/domain/agentRuntime.ts +18 -0
  246. package/apps/octogent/packages/core/src/domain/channel.ts +8 -0
  247. package/apps/octogent/packages/core/src/domain/completionSound.ts +14 -0
  248. package/apps/octogent/packages/core/src/domain/conversation.ts +48 -0
  249. package/apps/octogent/packages/core/src/domain/deck.ts +33 -0
  250. package/apps/octogent/packages/core/src/domain/git.ts +32 -0
  251. package/apps/octogent/packages/core/src/domain/monitor.ts +62 -0
  252. package/apps/octogent/packages/core/src/domain/setup.ts +27 -0
  253. package/apps/octogent/packages/core/src/domain/terminal.ts +17 -0
  254. package/apps/octogent/packages/core/src/domain/uiState.ts +22 -0
  255. package/apps/octogent/packages/core/src/domain/usage.ts +60 -0
  256. package/apps/octogent/packages/core/src/index.ts +15 -0
  257. package/apps/octogent/packages/core/src/ports/TerminalSnapshotReader.ts +5 -0
  258. package/apps/octogent/packages/core/src/util/typeCoercion.ts +20 -0
  259. package/apps/octogent/packages/core/tests/buildTerminalList.test.ts +75 -0
  260. package/apps/octogent/packages/core/tsconfig.json +7 -0
  261. package/apps/octogent/packages/core/tsconfig.tsbuildinfo +1 -0
  262. package/apps/octogent/packages/core/vitest.config.ts +7 -0
  263. package/apps/octogent/pnpm-lock.yaml +3212 -0
  264. package/apps/octogent/pnpm-workspace.yaml +3 -0
  265. package/apps/octogent/prompts/meta-prompt-generator.md +223 -0
  266. package/apps/octogent/prompts/octoboss-clean-contexts.md +30 -0
  267. package/apps/octogent/prompts/octoboss-reorganize-tentacles.md +29 -0
  268. package/apps/octogent/prompts/octoboss-reorganize-todos.md +27 -0
  269. package/apps/octogent/prompts/sandbox-init.md +3 -0
  270. package/apps/octogent/prompts/swarm-parent.md +83 -0
  271. package/apps/octogent/prompts/swarm-worker.md +50 -0
  272. package/apps/octogent/prompts/tentacle-context-init.md +1 -0
  273. package/apps/octogent/prompts/tentacle-planner.md +110 -0
  274. package/apps/octogent/prompts/tentacle-reorganize-todos.md +20 -0
  275. package/apps/octogent/prompts/tentacle-update-tentacle.md +18 -0
  276. package/apps/octogent/scripts/build-package.mjs +23 -0
  277. package/apps/octogent/scripts/dev.mjs +158 -0
  278. package/apps/octogent/scripts/smoke-public-install.mjs +271 -0
  279. package/apps/octogent/static/images/octogent-header.png +0 -0
  280. package/apps/octogent/static/images/preview_1.jpg +0 -0
  281. package/apps/octogent/static/images/preview_2.jpg +0 -0
  282. package/apps/octogent/static/images/preview_3.jpg +0 -0
  283. package/apps/octogent/static/images/preview_4.jpg +0 -0
  284. package/apps/octogent/static/images/preview_5.jpg +0 -0
  285. package/apps/octogent/static/images/preview_6.jpg +0 -0
  286. package/apps/octogent/tsconfig.base.json +16 -0
  287. package/bin/AGI +3 -0
  288. package/bin/AGI-install-app +71 -0
  289. package/bin/AGI-ui +16 -0
  290. package/bin/AGI-voice +15 -0
  291. package/bin/AGI-web +16 -0
  292. package/bin/cortex +109 -0
  293. package/bin/cortex-octogent +99 -0
  294. package/bin/import-specifier.mjs +13 -0
  295. package/bin/import-specifier.test.mjs +13 -0
  296. package/bin/octo +150 -0
  297. package/dist/cli.mjs +555650 -0
  298. package/package.json +157 -0
  299. package/scripts/setup-wizard.ts +390 -0
@@ -0,0 +1,137 @@
1
+ import type { MonitorConfigPatchInput } from "../monitor";
2
+
3
+ export const parseMonitorConfigPatch = (
4
+ payload: unknown,
5
+ ): { patch: MonitorConfigPatchInput | null; error: string | null } => {
6
+ if (payload === null || payload === undefined || typeof payload !== "object") {
7
+ return {
8
+ patch: null,
9
+ error: "Expected a JSON object body.",
10
+ };
11
+ }
12
+
13
+ const record = payload as Record<string, unknown>;
14
+ const patch: MonitorConfigPatchInput = {};
15
+
16
+ if (record.providerId !== undefined) {
17
+ if (record.providerId !== "x") {
18
+ return {
19
+ patch: null,
20
+ error: "providerId must be 'x'.",
21
+ };
22
+ }
23
+
24
+ patch.providerId = "x";
25
+ }
26
+
27
+ if (record.queryTerms !== undefined) {
28
+ if (!Array.isArray(record.queryTerms)) {
29
+ return {
30
+ patch: null,
31
+ error: "queryTerms must be an array of strings.",
32
+ };
33
+ }
34
+
35
+ const queryTerms = record.queryTerms.filter((term): term is string => typeof term === "string");
36
+ if (queryTerms.length !== record.queryTerms.length) {
37
+ return {
38
+ patch: null,
39
+ error: "queryTerms must be an array of strings.",
40
+ };
41
+ }
42
+
43
+ patch.queryTerms = queryTerms;
44
+ }
45
+
46
+ if (record.refreshPolicy !== undefined) {
47
+ if (
48
+ record.refreshPolicy === null ||
49
+ typeof record.refreshPolicy !== "object" ||
50
+ Array.isArray(record.refreshPolicy)
51
+ ) {
52
+ return {
53
+ patch: null,
54
+ error: "refreshPolicy must be an object.",
55
+ };
56
+ }
57
+
58
+ const refreshPolicyRecord = record.refreshPolicy as Record<string, unknown>;
59
+ if (
60
+ refreshPolicyRecord.maxCacheAgeMs !== undefined &&
61
+ (typeof refreshPolicyRecord.maxCacheAgeMs !== "number" ||
62
+ !Number.isFinite(refreshPolicyRecord.maxCacheAgeMs) ||
63
+ refreshPolicyRecord.maxCacheAgeMs <= 0)
64
+ ) {
65
+ return {
66
+ patch: null,
67
+ error: "refreshPolicy.maxCacheAgeMs must be a positive number.",
68
+ };
69
+ }
70
+
71
+ if (
72
+ refreshPolicyRecord.maxPosts !== undefined &&
73
+ (typeof refreshPolicyRecord.maxPosts !== "number" ||
74
+ !Number.isFinite(refreshPolicyRecord.maxPosts) ||
75
+ refreshPolicyRecord.maxPosts <= 0)
76
+ ) {
77
+ return {
78
+ patch: null,
79
+ error: "refreshPolicy.maxPosts must be a positive number.",
80
+ };
81
+ }
82
+
83
+ if (
84
+ refreshPolicyRecord.searchWindowDays !== undefined &&
85
+ (typeof refreshPolicyRecord.searchWindowDays !== "number" ||
86
+ !Number.isFinite(refreshPolicyRecord.searchWindowDays) ||
87
+ ![1, 3, 7].includes(Math.floor(refreshPolicyRecord.searchWindowDays)))
88
+ ) {
89
+ return {
90
+ patch: null,
91
+ error: "refreshPolicy.searchWindowDays must be one of: 1, 3, 7.",
92
+ };
93
+ }
94
+
95
+ patch.refreshPolicy = {};
96
+ if (refreshPolicyRecord.maxCacheAgeMs !== undefined) {
97
+ patch.refreshPolicy.maxCacheAgeMs = refreshPolicyRecord.maxCacheAgeMs;
98
+ }
99
+ if (refreshPolicyRecord.maxPosts !== undefined) {
100
+ patch.refreshPolicy.maxPosts = refreshPolicyRecord.maxPosts;
101
+ }
102
+ if (refreshPolicyRecord.searchWindowDays !== undefined) {
103
+ patch.refreshPolicy.searchWindowDays = Math.floor(refreshPolicyRecord.searchWindowDays) as
104
+ | 1
105
+ | 3
106
+ | 7;
107
+ }
108
+ }
109
+
110
+ if (record.credentials !== undefined) {
111
+ if (
112
+ record.credentials === null ||
113
+ typeof record.credentials !== "object" ||
114
+ Array.isArray(record.credentials)
115
+ ) {
116
+ return {
117
+ patch: null,
118
+ error: "credentials must be an object.",
119
+ };
120
+ }
121
+
122
+ patch.credentials = record.credentials;
123
+ }
124
+
125
+ if (record.validateCredentials !== undefined) {
126
+ if (typeof record.validateCredentials !== "boolean") {
127
+ return {
128
+ patch: null,
129
+ error: "validateCredentials must be a boolean.",
130
+ };
131
+ }
132
+
133
+ patch.validateCredentials = record.validateCredentials;
134
+ }
135
+
136
+ return { patch, error: null };
137
+ };
@@ -0,0 +1,95 @@
1
+ import { MonitorInputError } from "../monitor";
2
+ import { parseMonitorConfigPatch } from "./monitorParsers";
3
+ import type { ApiRouteHandler } from "./routeHelpers";
4
+ import { readJsonBodyOrWriteError, writeJson, writeMethodNotAllowed } from "./routeHelpers";
5
+
6
+ export const handleMonitorConfigRoute: ApiRouteHandler = async (
7
+ { request, response, requestUrl, corsOrigin },
8
+ { monitorService },
9
+ ) => {
10
+ if (requestUrl.pathname !== "/api/monitor/config") {
11
+ return false;
12
+ }
13
+
14
+ if (request.method === "GET") {
15
+ const payload = await monitorService.readConfig();
16
+ writeJson(response, 200, payload, corsOrigin);
17
+ return true;
18
+ }
19
+
20
+ if (request.method !== "PATCH") {
21
+ writeMethodNotAllowed(response, corsOrigin);
22
+ return true;
23
+ }
24
+
25
+ const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
26
+ if (!bodyReadResult.ok) {
27
+ return true;
28
+ }
29
+
30
+ const patchResult = parseMonitorConfigPatch(bodyReadResult.payload);
31
+ if (patchResult.error || !patchResult.patch) {
32
+ writeJson(
33
+ response,
34
+ 400,
35
+ { error: patchResult.error ?? "Invalid monitor config patch." },
36
+ corsOrigin,
37
+ );
38
+ return true;
39
+ }
40
+
41
+ try {
42
+ const payload = await monitorService.patchConfig(patchResult.patch);
43
+ writeJson(response, 200, payload, corsOrigin);
44
+ return true;
45
+ } catch (error) {
46
+ if (error instanceof MonitorInputError) {
47
+ writeJson(response, 400, { error: error.message }, corsOrigin);
48
+ return true;
49
+ }
50
+
51
+ throw error;
52
+ }
53
+ };
54
+
55
+ export const handleMonitorFeedRoute: ApiRouteHandler = async (
56
+ { request, response, requestUrl, corsOrigin },
57
+ { monitorService },
58
+ ) => {
59
+ if (requestUrl.pathname !== "/api/monitor/feed") {
60
+ return false;
61
+ }
62
+
63
+ if (request.method !== "GET") {
64
+ writeMethodNotAllowed(response, corsOrigin);
65
+ return true;
66
+ }
67
+
68
+ const payload = await monitorService.readFeed({
69
+ forceRefresh: false,
70
+ refreshIfStale: true,
71
+ });
72
+ writeJson(response, 200, payload, corsOrigin);
73
+ return true;
74
+ };
75
+
76
+ export const handleMonitorRefreshRoute: ApiRouteHandler = async (
77
+ { request, response, requestUrl, corsOrigin },
78
+ { monitorService },
79
+ ) => {
80
+ if (requestUrl.pathname !== "/api/monitor/refresh") {
81
+ return false;
82
+ }
83
+
84
+ if (request.method !== "POST") {
85
+ writeMethodNotAllowed(response, corsOrigin);
86
+ return true;
87
+ }
88
+
89
+ const payload = await monitorService.readFeed({
90
+ forceRefresh: true,
91
+ refreshIfStale: true,
92
+ });
93
+ writeJson(response, 200, payload, corsOrigin);
94
+ return true;
95
+ };
@@ -0,0 +1,311 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import type { IncomingMessage, ServerResponse } from "node:http";
4
+ import { extname, join } from "node:path";
5
+
6
+ import type { UsageChartResponse } from "../claudeSessionScanner";
7
+ import type { ClaudeUsageSnapshot } from "../claudeUsage";
8
+ import type { CodeIntelStore } from "../codeIntelStore";
9
+ import type { CodexUsageSnapshot } from "../codexUsage";
10
+ import type { GitHubRepoSummarySnapshot } from "../githubRepoSummary";
11
+ import { logVerbose } from "../logging";
12
+ import type { MonitorService } from "../monitor";
13
+ import { handleCodeIntelEventsRoute } from "./codeIntelRoutes";
14
+ import {
15
+ handleConversationExportRoute,
16
+ handleConversationItemRoute,
17
+ handleConversationSearchRoute,
18
+ handleConversationsCollectionRoute,
19
+ } from "./conversationRoutes";
20
+ import {
21
+ handleDeckSkillsRoute,
22
+ handleDeckTentacleItemRoute,
23
+ handleDeckTentacleSkillsRoute,
24
+ handleDeckTentacleSwarmRoute,
25
+ handleDeckTentaclesRoute,
26
+ handleDeckTodoAddRoute,
27
+ handleDeckTodoDeleteRoute,
28
+ handleDeckTodoEditRoute,
29
+ handleDeckTodoSolveRoute,
30
+ handleDeckTodoToggleRoute,
31
+ handleDeckVaultFileRoute,
32
+ } from "./deckRoutes";
33
+ import { handleTentacleGitPullRequestRoute, handleTentacleGitRoute } from "./gitRoutes";
34
+ import {
35
+ handleChannelMessagesRoute,
36
+ handleHookRoute,
37
+ handlePromptItemRoute,
38
+ handlePromptsCollectionRoute,
39
+ handleUiStateRoute,
40
+ handleWorkspaceSetupRoute,
41
+ } from "./miscRoutes";
42
+ import {
43
+ handleMonitorConfigRoute,
44
+ handleMonitorFeedRoute,
45
+ handleMonitorRefreshRoute,
46
+ } from "./monitorRoutes";
47
+ import type {
48
+ ApiRouteHandler,
49
+ RouteHandlerContext,
50
+ RouteHandlerDependencies,
51
+ TerminalRuntime,
52
+ } from "./routeHelpers";
53
+ import { writeJson, writeNoContent } from "./routeHelpers";
54
+ import {
55
+ getRequestCorsOrigin,
56
+ isAllowedHostHeader,
57
+ isAllowedOriginHeader,
58
+ readHeaderValue,
59
+ } from "./security";
60
+ import {
61
+ handleTerminalItemRoute,
62
+ handleTerminalSnapshotsRoute,
63
+ handleTerminalsCollectionRoute,
64
+ } from "./terminalRoutes";
65
+ import {
66
+ handleClaudeUsageRoute,
67
+ handleCodexUsageRoute,
68
+ handleGithubSummaryRoute,
69
+ handleUsageHeatmapRoute,
70
+ } from "./usageRoutes";
71
+
72
+ const MIME_TYPES: Record<string, string> = {
73
+ ".html": "text/html",
74
+ ".js": "application/javascript",
75
+ ".css": "text/css",
76
+ ".json": "application/json",
77
+ ".svg": "image/svg+xml",
78
+ ".png": "image/png",
79
+ ".ico": "image/x-icon",
80
+ ".woff": "font/woff",
81
+ ".woff2": "font/woff2",
82
+ ".ttf": "font/ttf",
83
+ ".otf": "font/otf",
84
+ };
85
+
86
+ type CreateApiRequestHandlerOptions = {
87
+ runtime: TerminalRuntime;
88
+ workspaceCwd: string;
89
+ projectStateDir: string;
90
+ promptsDir: string;
91
+ userPromptsDir: string;
92
+ webDistDir?: string | undefined;
93
+ getApiBaseUrl: () => string;
94
+ getApiPort: () => string;
95
+ readClaudeUsageSnapshot: () => Promise<ClaudeUsageSnapshot>;
96
+ readClaudeOauthUsageSnapshot: () => Promise<ClaudeUsageSnapshot>;
97
+ readClaudeCliUsageSnapshot: () => Promise<ClaudeUsageSnapshot>;
98
+ readCodexUsageSnapshot: () => Promise<CodexUsageSnapshot>;
99
+ readGithubRepoSummary: () => Promise<GitHubRepoSummarySnapshot>;
100
+ scanUsageHeatmap: (scope: "all" | "project") => Promise<UsageChartResponse>;
101
+ monitorService: MonitorService;
102
+ invalidateClaudeUsageCache: () => void;
103
+ codeIntelStore: CodeIntelStore;
104
+ allowRemoteAccess: boolean;
105
+ };
106
+
107
+ const API_ROUTE_MAP: ReadonlyMap<string, readonly ApiRouteHandler[]> = new Map([
108
+ ["channels", [handleChannelMessagesRoute]],
109
+ ["hooks", [handleHookRoute]],
110
+ ["prompts", [handlePromptsCollectionRoute, handlePromptItemRoute]],
111
+ [
112
+ "deck",
113
+ [
114
+ handleDeckSkillsRoute,
115
+ handleDeckTentaclesRoute,
116
+ handleDeckTentacleItemRoute,
117
+ handleDeckTentacleSkillsRoute,
118
+ handleDeckTodoSolveRoute,
119
+ handleDeckTentacleSwarmRoute,
120
+ handleDeckTodoToggleRoute,
121
+ handleDeckTodoEditRoute,
122
+ handleDeckTodoAddRoute,
123
+ handleDeckTodoDeleteRoute,
124
+ handleDeckVaultFileRoute,
125
+ ],
126
+ ],
127
+ ["terminal-snapshots", [handleTerminalSnapshotsRoute]],
128
+ ["codex", [handleCodexUsageRoute]],
129
+ ["claude", [handleClaudeUsageRoute]],
130
+ ["analytics", [handleUsageHeatmapRoute]],
131
+ ["github", [handleGithubSummaryRoute]],
132
+ ["setup", [handleWorkspaceSetupRoute]],
133
+ ["ui-state", [handleUiStateRoute]],
134
+ ["monitor", [handleMonitorConfigRoute, handleMonitorFeedRoute, handleMonitorRefreshRoute]],
135
+ [
136
+ "conversations",
137
+ [
138
+ handleConversationsCollectionRoute,
139
+ handleConversationSearchRoute,
140
+ handleConversationExportRoute,
141
+ handleConversationItemRoute,
142
+ ],
143
+ ],
144
+ ["terminals", [handleTerminalsCollectionRoute, handleTerminalItemRoute]],
145
+ ["tentacles", [handleTentacleGitRoute, handleTentacleGitPullRequestRoute]],
146
+ ["code-intel", [handleCodeIntelEventsRoute]],
147
+ ]);
148
+
149
+ const extractRoutePrefix = (pathname: string): string | null => {
150
+ const segments = pathname.split("/");
151
+ if (segments.length < 3 || segments[1] !== "api") {
152
+ return null;
153
+ }
154
+ return segments[2] ?? null;
155
+ };
156
+
157
+ const logRequest = (method: string, path: string, status: number, startTime: number) => {
158
+ logVerbose(`[API] ${method} ${path} ${status} ${Date.now() - startTime}ms`);
159
+ };
160
+
161
+ const serveStaticFile = async (
162
+ response: ServerResponse,
163
+ webDistDir: string,
164
+ pathname: string,
165
+ ): Promise<boolean> => {
166
+ // Prevent path traversal.
167
+ const safePath = pathname.replace(/\.\./g, "").replace(/\/+/g, "/");
168
+ const filePath = join(webDistDir, safePath === "/" ? "index.html" : safePath);
169
+
170
+ try {
171
+ const content = await readFile(filePath);
172
+ const ext = extname(filePath);
173
+ const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
174
+ response.writeHead(200, { "Content-Type": contentType });
175
+ response.end(content);
176
+ return true;
177
+ } catch (error) {
178
+ const code = typeof error === "object" && error && "code" in error ? String(error.code) : "";
179
+ if (code !== "ENOENT") {
180
+ console.error(
181
+ `[API] Static file error: ${filePath}`,
182
+ error instanceof Error ? error.message : error,
183
+ );
184
+ }
185
+ return false;
186
+ }
187
+ };
188
+
189
+ export const createApiRequestHandler = ({
190
+ runtime,
191
+ workspaceCwd,
192
+ projectStateDir,
193
+ promptsDir,
194
+ userPromptsDir,
195
+ webDistDir,
196
+ getApiBaseUrl,
197
+ getApiPort,
198
+ readClaudeUsageSnapshot,
199
+ readClaudeOauthUsageSnapshot,
200
+ readClaudeCliUsageSnapshot,
201
+ readCodexUsageSnapshot,
202
+ readGithubRepoSummary,
203
+ scanUsageHeatmap,
204
+ monitorService,
205
+ invalidateClaudeUsageCache,
206
+ codeIntelStore,
207
+ allowRemoteAccess,
208
+ }: CreateApiRequestHandlerOptions) => {
209
+ const resolvedWebDistDir = webDistDir && existsSync(webDistDir) ? webDistDir : null;
210
+
211
+ const routeDependencies: RouteHandlerDependencies = {
212
+ runtime,
213
+ workspaceCwd,
214
+ projectStateDir,
215
+ promptsDir,
216
+ userPromptsDir,
217
+ getApiBaseUrl,
218
+ getApiPort,
219
+ readClaudeUsageSnapshot,
220
+ readClaudeOauthUsageSnapshot,
221
+ readClaudeCliUsageSnapshot,
222
+ readCodexUsageSnapshot,
223
+ readGithubRepoSummary,
224
+ scanUsageHeatmap,
225
+ monitorService,
226
+ invalidateClaudeUsageCache,
227
+ codeIntelStore,
228
+ };
229
+
230
+ return async (request: IncomingMessage, response: ServerResponse) => {
231
+ const startTime = Date.now();
232
+ let statusCode = 0;
233
+ const originalWriteHead = response.writeHead.bind(response);
234
+ response.writeHead = ((...args: Parameters<typeof response.writeHead>) => {
235
+ statusCode = typeof args[0] === "number" ? args[0] : 0;
236
+ return originalWriteHead(...args);
237
+ }) as typeof response.writeHead;
238
+
239
+ const originHeader = readHeaderValue(request.headers.origin);
240
+ const hostHeader = readHeaderValue(request.headers.host);
241
+ const corsOrigin = getRequestCorsOrigin(originHeader, allowRemoteAccess);
242
+
243
+ if (!isAllowedHostHeader(hostHeader, allowRemoteAccess)) {
244
+ writeJson(response, 403, { error: "Host not allowed." }, null);
245
+ logRequest(request.method ?? "?", request.url ?? "/", 403, startTime);
246
+ return;
247
+ }
248
+
249
+ if (!isAllowedOriginHeader(originHeader, allowRemoteAccess)) {
250
+ writeJson(response, 403, { error: "Origin not allowed." }, null);
251
+ logRequest(request.method ?? "?", request.url ?? "/", 403, startTime);
252
+ return;
253
+ }
254
+
255
+ try {
256
+ const requestUrl = new URL(request.url ?? "/", "http://localhost");
257
+
258
+ if (request.method === "OPTIONS") {
259
+ writeNoContent(response, 204, corsOrigin);
260
+ logRequest(request.method ?? "OPTIONS", requestUrl.pathname, statusCode, startTime);
261
+ return;
262
+ }
263
+
264
+ const routeContext: RouteHandlerContext = {
265
+ request,
266
+ response,
267
+ requestUrl,
268
+ corsOrigin,
269
+ };
270
+
271
+ const prefix = extractRoutePrefix(requestUrl.pathname);
272
+ const handlers = prefix !== null ? API_ROUTE_MAP.get(prefix) : undefined;
273
+ if (handlers) {
274
+ for (const handleRoute of handlers) {
275
+ if (await handleRoute(routeContext, routeDependencies)) {
276
+ logRequest(request.method ?? "?", requestUrl.pathname, statusCode, startTime);
277
+ return;
278
+ }
279
+ }
280
+ }
281
+
282
+ // Serve static web frontend if available.
283
+ if (resolvedWebDistDir && request.method === "GET") {
284
+ const served =
285
+ (await serveStaticFile(response, resolvedWebDistDir, requestUrl.pathname)) ||
286
+ (await serveStaticFile(response, resolvedWebDistDir, "/"));
287
+ if (served) {
288
+ logRequest(request.method, requestUrl.pathname, 200, startTime);
289
+ return;
290
+ }
291
+ }
292
+
293
+ writeJson(response, 404, { error: "Not found" }, corsOrigin);
294
+ logRequest(request.method ?? "?", requestUrl.pathname, statusCode, startTime);
295
+ } catch (error) {
296
+ console.error(
297
+ `[API] Unhandled error: ${request.method ?? "?"} ${request.url ?? "/"}`,
298
+ error instanceof Error ? (error.stack ?? error.message) : error,
299
+ );
300
+ writeJson(
301
+ response,
302
+ 500,
303
+ {
304
+ error: "Internal server error",
305
+ },
306
+ corsOrigin,
307
+ );
308
+ logRequest(request.method ?? "?", request.url ?? "/", statusCode, startTime);
309
+ }
310
+ };
311
+ };
@@ -0,0 +1,25 @@
1
+ import type { IncomingMessage } from "node:http";
2
+
3
+ const MAX_JSON_BODY_BYTES = 1024 * 1024;
4
+
5
+ export class RequestBodyTooLargeError extends Error {}
6
+
7
+ export const readJsonBody = async (request: IncomingMessage): Promise<unknown> => {
8
+ let totalBytes = 0;
9
+ const chunks: Buffer[] = [];
10
+ for await (const chunk of request) {
11
+ const nextChunk = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
12
+ totalBytes += nextChunk.length;
13
+ if (totalBytes > MAX_JSON_BODY_BYTES) {
14
+ throw new RequestBodyTooLargeError("Request body too large.");
15
+ }
16
+ chunks.push(nextChunk);
17
+ }
18
+
19
+ const payload = Buffer.concat(chunks).toString("utf8").trim();
20
+ if (payload.length === 0) {
21
+ return null;
22
+ }
23
+
24
+ return JSON.parse(payload);
25
+ };
@@ -0,0 +1,97 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+
3
+ import type { UsageChartResponse } from "../claudeSessionScanner";
4
+ import type { ClaudeUsageSnapshot } from "../claudeUsage";
5
+ import type { CodeIntelStore } from "../codeIntelStore";
6
+ import type { CodexUsageSnapshot } from "../codexUsage";
7
+ import type { GitHubRepoSummarySnapshot } from "../githubRepoSummary";
8
+ import type { MonitorService } from "../monitor";
9
+ import { RequestBodyTooLargeError, readJsonBody } from "./requestParsers";
10
+ import { withCors } from "./security";
11
+
12
+ export type TerminalRuntime = ReturnType<typeof import("../terminalRuntime").createTerminalRuntime>;
13
+
14
+ export type RouteHandlerDependencies = {
15
+ runtime: TerminalRuntime;
16
+ workspaceCwd: string;
17
+ projectStateDir: string;
18
+ promptsDir: string;
19
+ userPromptsDir: string;
20
+ getApiBaseUrl: () => string;
21
+ getApiPort: () => string;
22
+ readClaudeUsageSnapshot: () => Promise<ClaudeUsageSnapshot>;
23
+ readClaudeOauthUsageSnapshot: () => Promise<ClaudeUsageSnapshot>;
24
+ readClaudeCliUsageSnapshot: () => Promise<ClaudeUsageSnapshot>;
25
+ readCodexUsageSnapshot: () => Promise<CodexUsageSnapshot>;
26
+ readGithubRepoSummary: () => Promise<GitHubRepoSummarySnapshot>;
27
+ scanUsageHeatmap: (scope: "all" | "project") => Promise<UsageChartResponse>;
28
+ monitorService: MonitorService;
29
+ invalidateClaudeUsageCache: () => void;
30
+ codeIntelStore: CodeIntelStore;
31
+ };
32
+
33
+ export type RouteHandlerContext = {
34
+ request: IncomingMessage;
35
+ response: ServerResponse;
36
+ requestUrl: URL;
37
+ corsOrigin: string | null;
38
+ };
39
+
40
+ type JsonBodyReadResult = { ok: true; payload: unknown } | { ok: false };
41
+ export type ApiRouteHandler = (
42
+ context: RouteHandlerContext,
43
+ dependencies: RouteHandlerDependencies,
44
+ ) => Promise<boolean>;
45
+
46
+ export const writeJson = (
47
+ response: ServerResponse,
48
+ status: number,
49
+ payload: unknown,
50
+ corsOrigin: string | null,
51
+ ) => {
52
+ response.writeHead(status, withCors({ "Content-Type": "application/json" }, corsOrigin));
53
+ response.end(JSON.stringify(payload));
54
+ };
55
+
56
+ export const writeText = (
57
+ response: ServerResponse,
58
+ status: number,
59
+ payload: string,
60
+ contentType: string,
61
+ corsOrigin: string | null,
62
+ ) => {
63
+ response.writeHead(status, withCors({ "Content-Type": contentType }, corsOrigin));
64
+ response.end(payload);
65
+ };
66
+
67
+ export const writeNoContent = (
68
+ response: ServerResponse,
69
+ status: number,
70
+ corsOrigin: string | null,
71
+ ) => {
72
+ response.writeHead(status, withCors({}, corsOrigin));
73
+ response.end();
74
+ };
75
+
76
+ export const writeMethodNotAllowed = (response: ServerResponse, corsOrigin: string | null) => {
77
+ writeJson(response, 405, { error: "Method not allowed" }, corsOrigin);
78
+ };
79
+
80
+ export const readJsonBodyOrWriteError = async (
81
+ request: IncomingMessage,
82
+ response: ServerResponse,
83
+ corsOrigin: string | null,
84
+ ): Promise<JsonBodyReadResult> => {
85
+ try {
86
+ const payload = await readJsonBody(request);
87
+ return { ok: true, payload };
88
+ } catch (error) {
89
+ if (error instanceof RequestBodyTooLargeError) {
90
+ writeJson(response, 413, { error: "Request body too large." }, corsOrigin);
91
+ return { ok: false };
92
+ }
93
+
94
+ writeJson(response, 400, { error: "Invalid JSON body." }, corsOrigin);
95
+ return { ok: false };
96
+ }
97
+ };