@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.
- package/README.md +1295 -0
- package/apps/octogent/.github/workflows/ci.yml +40 -0
- package/apps/octogent/.shims/claude +4 -0
- package/apps/octogent/AGENTS.md +71 -0
- package/apps/octogent/CONTRIBUTING.md +72 -0
- package/apps/octogent/LICENSE +21 -0
- package/apps/octogent/README.md +184 -0
- package/apps/octogent/apps/api/AGENTS.md +32 -0
- package/apps/octogent/apps/api/package.json +19 -0
- package/apps/octogent/apps/api/src/agentStateDetection.ts +181 -0
- package/apps/octogent/apps/api/src/claudeSessionScanner.ts +235 -0
- package/apps/octogent/apps/api/src/claudeSkills.ts +182 -0
- package/apps/octogent/apps/api/src/claudeUsage.ts +922 -0
- package/apps/octogent/apps/api/src/cli.ts +595 -0
- package/apps/octogent/apps/api/src/codeIntelStore.ts +46 -0
- package/apps/octogent/apps/api/src/codexUsage.ts +278 -0
- package/apps/octogent/apps/api/src/createApiServer/codeIntelRoutes.ts +60 -0
- package/apps/octogent/apps/api/src/createApiServer/conversationRoutes.ts +128 -0
- package/apps/octogent/apps/api/src/createApiServer/deckRoutes.ts +873 -0
- package/apps/octogent/apps/api/src/createApiServer/gitParsers.ts +140 -0
- package/apps/octogent/apps/api/src/createApiServer/gitRoutes.ts +214 -0
- package/apps/octogent/apps/api/src/createApiServer/miscRoutes.ts +316 -0
- package/apps/octogent/apps/api/src/createApiServer/monitorParsers.ts +137 -0
- package/apps/octogent/apps/api/src/createApiServer/monitorRoutes.ts +95 -0
- package/apps/octogent/apps/api/src/createApiServer/requestHandler.ts +311 -0
- package/apps/octogent/apps/api/src/createApiServer/requestParsers.ts +25 -0
- package/apps/octogent/apps/api/src/createApiServer/routeHelpers.ts +97 -0
- package/apps/octogent/apps/api/src/createApiServer/security.ts +70 -0
- package/apps/octogent/apps/api/src/createApiServer/terminalParsers.ts +167 -0
- package/apps/octogent/apps/api/src/createApiServer/terminalRoutes.ts +315 -0
- package/apps/octogent/apps/api/src/createApiServer/types.ts +24 -0
- package/apps/octogent/apps/api/src/createApiServer/uiStateParsers.ts +255 -0
- package/apps/octogent/apps/api/src/createApiServer/upgradeHandler.ts +38 -0
- package/apps/octogent/apps/api/src/createApiServer/usageRoutes.ts +84 -0
- package/apps/octogent/apps/api/src/createApiServer.ts +176 -0
- package/apps/octogent/apps/api/src/deck/readDeckTentacles.ts +595 -0
- package/apps/octogent/apps/api/src/githubRepoSummary.ts +397 -0
- package/apps/octogent/apps/api/src/logging.ts +9 -0
- package/apps/octogent/apps/api/src/monitor/defaults.ts +3 -0
- package/apps/octogent/apps/api/src/monitor/index.ts +8 -0
- package/apps/octogent/apps/api/src/monitor/repository.ts +303 -0
- package/apps/octogent/apps/api/src/monitor/service.ts +349 -0
- package/apps/octogent/apps/api/src/monitor/types.ts +120 -0
- package/apps/octogent/apps/api/src/monitor/xProvider.ts +587 -0
- package/apps/octogent/apps/api/src/projectPersistence.ts +377 -0
- package/apps/octogent/apps/api/src/prompts/index.ts +10 -0
- package/apps/octogent/apps/api/src/prompts/promptResolver.ts +145 -0
- package/apps/octogent/apps/api/src/runtimeMetadata.ts +69 -0
- package/apps/octogent/apps/api/src/server.ts +80 -0
- package/apps/octogent/apps/api/src/setupState.ts +80 -0
- package/apps/octogent/apps/api/src/setupStatus.ts +174 -0
- package/apps/octogent/apps/api/src/startupPrerequisites.ts +146 -0
- package/apps/octogent/apps/api/src/terminalRuntime/channelMessaging.ts +87 -0
- package/apps/octogent/apps/api/src/terminalRuntime/claudeTranscript.ts +279 -0
- package/apps/octogent/apps/api/src/terminalRuntime/constants.ts +15 -0
- package/apps/octogent/apps/api/src/terminalRuntime/conversations.ts +492 -0
- package/apps/octogent/apps/api/src/terminalRuntime/gitOperations.ts +341 -0
- package/apps/octogent/apps/api/src/terminalRuntime/hookProcessor.ts +405 -0
- package/apps/octogent/apps/api/src/terminalRuntime/protocol.ts +46 -0
- package/apps/octogent/apps/api/src/terminalRuntime/ptyEnvironment.ts +50 -0
- package/apps/octogent/apps/api/src/terminalRuntime/registry.ts +423 -0
- package/apps/octogent/apps/api/src/terminalRuntime/sessionRuntime.ts +671 -0
- package/apps/octogent/apps/api/src/terminalRuntime/systemClients.ts +432 -0
- package/apps/octogent/apps/api/src/terminalRuntime/types.ts +157 -0
- package/apps/octogent/apps/api/src/terminalRuntime/worktreeManager.ts +135 -0
- package/apps/octogent/apps/api/src/terminalRuntime.ts +567 -0
- package/apps/octogent/apps/api/src/usageUtils.ts +16 -0
- package/apps/octogent/apps/api/src/ws-shim.d.ts +28 -0
- package/apps/octogent/apps/api/tests/agentStateDetection.test.ts +67 -0
- package/apps/octogent/apps/api/tests/claudeUsage.test.ts +583 -0
- package/apps/octogent/apps/api/tests/codexUsage.test.ts +107 -0
- package/apps/octogent/apps/api/tests/createApiServer.test.ts +3207 -0
- package/apps/octogent/apps/api/tests/githubRepoSummary.test.ts +100 -0
- package/apps/octogent/apps/api/tests/logging.test.ts +33 -0
- package/apps/octogent/apps/api/tests/monitorApi.test.ts +467 -0
- package/apps/octogent/apps/api/tests/monitorCore.test.ts +104 -0
- package/apps/octogent/apps/api/tests/promptResolver.test.ts +109 -0
- package/apps/octogent/apps/api/tests/protocol.test.ts +14 -0
- package/apps/octogent/apps/api/tests/sessionRuntime.test.ts +608 -0
- package/apps/octogent/apps/api/tests/startupPrerequisites.test.ts +70 -0
- package/apps/octogent/apps/api/tests/upgradeHandler.test.ts +40 -0
- package/apps/octogent/apps/api/tests/xMonitorProvider.test.ts +109 -0
- package/apps/octogent/apps/api/tsconfig.json +7 -0
- package/apps/octogent/apps/api/vitest.config.ts +7 -0
- package/apps/octogent/apps/web/AGENTS.md +38 -0
- package/apps/octogent/apps/web/index.html +13 -0
- package/apps/octogent/apps/web/package.json +32 -0
- package/apps/octogent/apps/web/public/octopus-favicon.svg +26 -0
- package/apps/octogent/apps/web/src/App.tsx +646 -0
- package/apps/octogent/apps/web/src/app/canvas/types.ts +34 -0
- package/apps/octogent/apps/web/src/app/codeIntelAggregation.ts +278 -0
- package/apps/octogent/apps/web/src/app/constants.ts +28 -0
- package/apps/octogent/apps/web/src/app/conversationNormalizers.ts +135 -0
- package/apps/octogent/apps/web/src/app/formatTimestamp.ts +18 -0
- package/apps/octogent/apps/web/src/app/githubMetrics.ts +76 -0
- package/apps/octogent/apps/web/src/app/githubNormalizers.ts +91 -0
- package/apps/octogent/apps/web/src/app/hooks/useAgentRuntimeStates.ts +18 -0
- package/apps/octogent/apps/web/src/app/hooks/useBackendLivenessPolling.ts +53 -0
- package/apps/octogent/apps/web/src/app/hooks/useCanvasGraphData.ts +449 -0
- package/apps/octogent/apps/web/src/app/hooks/useCanvasTransform.ts +260 -0
- package/apps/octogent/apps/web/src/app/hooks/useClaudeUsagePolling.ts +40 -0
- package/apps/octogent/apps/web/src/app/hooks/useClickOutside.ts +30 -0
- package/apps/octogent/apps/web/src/app/hooks/useCodeIntelRuntime.ts +83 -0
- package/apps/octogent/apps/web/src/app/hooks/useCodexUsagePolling.ts +35 -0
- package/apps/octogent/apps/web/src/app/hooks/useConsoleKeyboardShortcuts.ts +31 -0
- package/apps/octogent/apps/web/src/app/hooks/useConversationsRuntime.ts +377 -0
- package/apps/octogent/apps/web/src/app/hooks/useForceSimulation.ts +319 -0
- package/apps/octogent/apps/web/src/app/hooks/useGitHubPrimaryViewModel.ts +143 -0
- package/apps/octogent/apps/web/src/app/hooks/useGithubSummaryPolling.ts +28 -0
- package/apps/octogent/apps/web/src/app/hooks/useInitialColumnsHydration.ts +64 -0
- package/apps/octogent/apps/web/src/app/hooks/useMonitorRuntime.ts +220 -0
- package/apps/octogent/apps/web/src/app/hooks/usePersistedUiState.ts +536 -0
- package/apps/octogent/apps/web/src/app/hooks/usePollingData.ts +79 -0
- package/apps/octogent/apps/web/src/app/hooks/usePromptLibrary.ts +185 -0
- package/apps/octogent/apps/web/src/app/hooks/useTentacleGitLifecycle.ts +530 -0
- package/apps/octogent/apps/web/src/app/hooks/useTerminalCompletionNotification.ts +94 -0
- package/apps/octogent/apps/web/src/app/hooks/useTerminalMutations.ts +266 -0
- package/apps/octogent/apps/web/src/app/hooks/useTerminalStateReconciliation.ts +23 -0
- package/apps/octogent/apps/web/src/app/hooks/useUsageHeatmapPolling.ts +43 -0
- package/apps/octogent/apps/web/src/app/hooks/useWorkspaceSetup.ts +80 -0
- package/apps/octogent/apps/web/src/app/hotkeys.ts +31 -0
- package/apps/octogent/apps/web/src/app/monitorNormalizers.ts +145 -0
- package/apps/octogent/apps/web/src/app/notificationSounds.ts +164 -0
- package/apps/octogent/apps/web/src/app/terminalRuntimeStateStore.ts +261 -0
- package/apps/octogent/apps/web/src/app/terminalState.ts +21 -0
- package/apps/octogent/apps/web/src/app/types.ts +42 -0
- package/apps/octogent/apps/web/src/app/uiStateNormalizers.ts +113 -0
- package/apps/octogent/apps/web/src/app/usageNormalizers.ts +58 -0
- package/apps/octogent/apps/web/src/components/ActiveAgentsSidebar.tsx +60 -0
- package/apps/octogent/apps/web/src/components/ActivityPrimaryView.tsx +21 -0
- package/apps/octogent/apps/web/src/components/AgentStateBadge.tsx +47 -0
- package/apps/octogent/apps/web/src/components/CanvasPrimaryView.tsx +1532 -0
- package/apps/octogent/apps/web/src/components/ClearAllConversationsDialog.tsx +33 -0
- package/apps/octogent/apps/web/src/components/CodeIntelArcDiagram.tsx +245 -0
- package/apps/octogent/apps/web/src/components/CodeIntelPrimaryView.tsx +104 -0
- package/apps/octogent/apps/web/src/components/CodeIntelTreemap.tsx +138 -0
- package/apps/octogent/apps/web/src/components/ConsolePrimaryNav.tsx +31 -0
- package/apps/octogent/apps/web/src/components/ConversationsPrimaryView.tsx +243 -0
- package/apps/octogent/apps/web/src/components/DeckPrimaryView.tsx +613 -0
- package/apps/octogent/apps/web/src/components/DeleteTentacleDialog.tsx +91 -0
- package/apps/octogent/apps/web/src/components/EmptyOctopus.tsx +715 -0
- package/apps/octogent/apps/web/src/components/GitHubPrimaryView.tsx +494 -0
- package/apps/octogent/apps/web/src/components/MonitorPrimaryView.tsx +475 -0
- package/apps/octogent/apps/web/src/components/PrimaryViewRouter.tsx +99 -0
- package/apps/octogent/apps/web/src/components/PromptsPrimaryView.tsx +243 -0
- package/apps/octogent/apps/web/src/components/RuntimeStatusStrip.tsx +273 -0
- package/apps/octogent/apps/web/src/components/SettingsPrimaryView.tsx +92 -0
- package/apps/octogent/apps/web/src/components/SidebarActionPanel.tsx +124 -0
- package/apps/octogent/apps/web/src/components/SidebarConversationsList.tsx +279 -0
- package/apps/octogent/apps/web/src/components/SidebarPromptsList.tsx +116 -0
- package/apps/octogent/apps/web/src/components/TelemetryTape.tsx +106 -0
- package/apps/octogent/apps/web/src/components/TentacleGitActionsDialog.tsx +341 -0
- package/apps/octogent/apps/web/src/components/Terminal.tsx +524 -0
- package/apps/octogent/apps/web/src/components/TerminalPromptPicker.tsx +140 -0
- package/apps/octogent/apps/web/src/components/UsageHeatmap.tsx +702 -0
- package/apps/octogent/apps/web/src/components/canvas/CanvasTentaclePanel.tsx +485 -0
- package/apps/octogent/apps/web/src/components/canvas/CanvasTerminalColumn.tsx +89 -0
- package/apps/octogent/apps/web/src/components/canvas/DeleteAllTerminalsDialog.tsx +221 -0
- package/apps/octogent/apps/web/src/components/canvas/OctopusNode.tsx +307 -0
- package/apps/octogent/apps/web/src/components/canvas/SessionNode.tsx +185 -0
- package/apps/octogent/apps/web/src/components/deck/ActionCards.tsx +118 -0
- package/apps/octogent/apps/web/src/components/deck/AddTentacleForm.tsx +269 -0
- package/apps/octogent/apps/web/src/components/deck/DeckBottomActions.tsx +56 -0
- package/apps/octogent/apps/web/src/components/deck/TentaclePod.tsx +334 -0
- package/apps/octogent/apps/web/src/components/deck/WorkspaceSetupCard.tsx +105 -0
- package/apps/octogent/apps/web/src/components/deck/octopusVisuals.ts +72 -0
- package/apps/octogent/apps/web/src/components/terminalReplay.ts +62 -0
- package/apps/octogent/apps/web/src/components/terminalWheel.ts +54 -0
- package/apps/octogent/apps/web/src/components/ui/ActionButton.tsx +34 -0
- package/apps/octogent/apps/web/src/components/ui/ConfirmationDialog.tsx +86 -0
- package/apps/octogent/apps/web/src/components/ui/MarkdownContent.tsx +43 -0
- package/apps/octogent/apps/web/src/components/ui/SettingsToggle.tsx +34 -0
- package/apps/octogent/apps/web/src/components/ui/StatusBadge.tsx +24 -0
- package/apps/octogent/apps/web/src/main.tsx +17 -0
- package/apps/octogent/apps/web/src/runtime/HttpTerminalSnapshotReader.ts +87 -0
- package/apps/octogent/apps/web/src/runtime/runtimeEndpoints.ts +412 -0
- package/apps/octogent/apps/web/src/styles/chrome-and-buttons.css +272 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-activity.css +358 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-canvas.css +1843 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-code-intel.css +227 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-conversations.css +705 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-deck.css +1524 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-github.css +541 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-monitor.css +595 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-pixpack.css +81 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-prompts.css +474 -0
- package/apps/octogent/apps/web/src/styles/console-canvas-settings.css +207 -0
- package/apps/octogent/apps/web/src/styles/console-chrome-status-nav.css +441 -0
- package/apps/octogent/apps/web/src/styles/console-overrides-telemetry.css +320 -0
- package/apps/octogent/apps/web/src/styles/console-theme-tokens.css +25 -0
- package/apps/octogent/apps/web/src/styles/cortex-theme.css +412 -0
- package/apps/octogent/apps/web/src/styles/foundation.css +100 -0
- package/apps/octogent/apps/web/src/styles/sidebar-and-scrollbars.css +447 -0
- package/apps/octogent/apps/web/src/styles/terminal-and-status.css +356 -0
- package/apps/octogent/apps/web/src/styles.css +25 -0
- package/apps/octogent/apps/web/src/types/ws.d.ts +23 -0
- package/apps/octogent/apps/web/tests/CanvasPrimaryView.test.tsx +347 -0
- package/apps/octogent/apps/web/tests/HttpTerminalSnapshotReader.test.tsx +54 -0
- package/apps/octogent/apps/web/tests/RuntimeStatusStrip.test.tsx +70 -0
- package/apps/octogent/apps/web/tests/Terminal.test.tsx +87 -0
- package/apps/octogent/apps/web/tests/add-tentacle-form.test.tsx +48 -0
- package/apps/octogent/apps/web/tests/app-github-runtime.test.tsx +162 -0
- package/apps/octogent/apps/web/tests/app-monitor-runtime.test.tsx +657 -0
- package/apps/octogent/apps/web/tests/app-shell-navigation.test.tsx +109 -0
- package/apps/octogent/apps/web/tests/app-swarm-refresh.test.tsx +268 -0
- package/apps/octogent/apps/web/tests/app-ui-state-persistence.test.tsx +116 -0
- package/apps/octogent/apps/web/tests/app-workspace-setup.test.tsx +217 -0
- package/apps/octogent/apps/web/tests/canvas-tentacle-panel.test.tsx +195 -0
- package/apps/octogent/apps/web/tests/delete-all-terminals-dialog.test.tsx +76 -0
- package/apps/octogent/apps/web/tests/githubMetrics.test.tsx +52 -0
- package/apps/octogent/apps/web/tests/hotkeys.test.tsx +44 -0
- package/apps/octogent/apps/web/tests/runtimeEndpoints.test.tsx +240 -0
- package/apps/octogent/apps/web/tests/setup.ts +39 -0
- package/apps/octogent/apps/web/tests/tentacle-pod.test.tsx +62 -0
- package/apps/octogent/apps/web/tests/terminalReplay.test.ts +71 -0
- package/apps/octogent/apps/web/tests/terminalState.test.tsx +49 -0
- package/apps/octogent/apps/web/tests/terminalWheel.test.tsx +51 -0
- package/apps/octogent/apps/web/tests/test-utils/appTestHarness.ts +48 -0
- package/apps/octogent/apps/web/tests/uiPrimitives.test.tsx +31 -0
- package/apps/octogent/apps/web/tests/useAgentRuntimeStates.test.tsx +47 -0
- package/apps/octogent/apps/web/tsconfig.json +8 -0
- package/apps/octogent/apps/web/vite.api.bundle.config.mts +32 -0
- package/apps/octogent/apps/web/vite.config.ts +22 -0
- package/apps/octogent/bin/octogent +3 -0
- package/apps/octogent/biome.json +21 -0
- package/apps/octogent/docs/concepts/mental-model.md +79 -0
- package/apps/octogent/docs/concepts/runtime-and-api.md +60 -0
- package/apps/octogent/docs/concepts/tentacles.md +85 -0
- package/apps/octogent/docs/getting-started/installation.md +54 -0
- package/apps/octogent/docs/getting-started/quickstart.md +79 -0
- package/apps/octogent/docs/guides/inter-agent-messaging.md +43 -0
- package/apps/octogent/docs/guides/orchestrating-child-agents.md +49 -0
- package/apps/octogent/docs/guides/working-with-todos.md +56 -0
- package/apps/octogent/docs/index.md +40 -0
- package/apps/octogent/docs/reference/api.md +103 -0
- package/apps/octogent/docs/reference/cli.md +71 -0
- package/apps/octogent/docs/reference/experimental-features.md +28 -0
- package/apps/octogent/docs/reference/filesystem-layout.md +62 -0
- package/apps/octogent/docs/reference/troubleshooting.md +49 -0
- package/apps/octogent/package.json +35 -0
- package/apps/octogent/packages/core/AGENTS.md +31 -0
- package/apps/octogent/packages/core/package.json +12 -0
- package/apps/octogent/packages/core/src/adapters/InMemoryTerminalSnapshotReader.ts +10 -0
- package/apps/octogent/packages/core/src/application/buildTerminalList.ts +13 -0
- package/apps/octogent/packages/core/src/domain/agentRuntime.ts +18 -0
- package/apps/octogent/packages/core/src/domain/channel.ts +8 -0
- package/apps/octogent/packages/core/src/domain/completionSound.ts +14 -0
- package/apps/octogent/packages/core/src/domain/conversation.ts +48 -0
- package/apps/octogent/packages/core/src/domain/deck.ts +33 -0
- package/apps/octogent/packages/core/src/domain/git.ts +32 -0
- package/apps/octogent/packages/core/src/domain/monitor.ts +62 -0
- package/apps/octogent/packages/core/src/domain/setup.ts +27 -0
- package/apps/octogent/packages/core/src/domain/terminal.ts +17 -0
- package/apps/octogent/packages/core/src/domain/uiState.ts +22 -0
- package/apps/octogent/packages/core/src/domain/usage.ts +60 -0
- package/apps/octogent/packages/core/src/index.ts +15 -0
- package/apps/octogent/packages/core/src/ports/TerminalSnapshotReader.ts +5 -0
- package/apps/octogent/packages/core/src/util/typeCoercion.ts +20 -0
- package/apps/octogent/packages/core/tests/buildTerminalList.test.ts +75 -0
- package/apps/octogent/packages/core/tsconfig.json +7 -0
- package/apps/octogent/packages/core/tsconfig.tsbuildinfo +1 -0
- package/apps/octogent/packages/core/vitest.config.ts +7 -0
- package/apps/octogent/pnpm-lock.yaml +3212 -0
- package/apps/octogent/pnpm-workspace.yaml +3 -0
- package/apps/octogent/prompts/meta-prompt-generator.md +223 -0
- package/apps/octogent/prompts/octoboss-clean-contexts.md +30 -0
- package/apps/octogent/prompts/octoboss-reorganize-tentacles.md +29 -0
- package/apps/octogent/prompts/octoboss-reorganize-todos.md +27 -0
- package/apps/octogent/prompts/sandbox-init.md +3 -0
- package/apps/octogent/prompts/swarm-parent.md +83 -0
- package/apps/octogent/prompts/swarm-worker.md +50 -0
- package/apps/octogent/prompts/tentacle-context-init.md +1 -0
- package/apps/octogent/prompts/tentacle-planner.md +110 -0
- package/apps/octogent/prompts/tentacle-reorganize-todos.md +20 -0
- package/apps/octogent/prompts/tentacle-update-tentacle.md +18 -0
- package/apps/octogent/scripts/build-package.mjs +23 -0
- package/apps/octogent/scripts/dev.mjs +158 -0
- package/apps/octogent/scripts/smoke-public-install.mjs +271 -0
- package/apps/octogent/static/images/octogent-header.png +0 -0
- package/apps/octogent/static/images/preview_1.jpg +0 -0
- package/apps/octogent/static/images/preview_2.jpg +0 -0
- package/apps/octogent/static/images/preview_3.jpg +0 -0
- package/apps/octogent/static/images/preview_4.jpg +0 -0
- package/apps/octogent/static/images/preview_5.jpg +0 -0
- package/apps/octogent/static/images/preview_6.jpg +0 -0
- package/apps/octogent/tsconfig.base.json +16 -0
- package/bin/AGI +3 -0
- package/bin/AGI-install-app +71 -0
- package/bin/AGI-ui +16 -0
- package/bin/AGI-voice +15 -0
- package/bin/AGI-web +16 -0
- package/bin/cortex +109 -0
- package/bin/cortex-octogent +99 -0
- package/bin/import-specifier.mjs +13 -0
- package/bin/import-specifier.test.mjs +13 -0
- package/bin/octo +150 -0
- package/dist/cli.mjs +555650 -0
- package/package.json +157 -0
- package/scripts/setup-wizard.ts +390 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export const parseTentacleCommitMessage = (
|
|
2
|
+
payload: unknown,
|
|
3
|
+
): { message: string | null; error: string | null } => {
|
|
4
|
+
if (payload === null || payload === undefined || typeof payload !== "object") {
|
|
5
|
+
return {
|
|
6
|
+
message: null,
|
|
7
|
+
error: "Expected a JSON object body.",
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const rawMessage = (payload as Record<string, unknown>).message;
|
|
12
|
+
if (typeof rawMessage !== "string") {
|
|
13
|
+
return {
|
|
14
|
+
message: null,
|
|
15
|
+
error: "Commit message must be a string.",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const trimmed = rawMessage.trim();
|
|
20
|
+
if (trimmed.length === 0) {
|
|
21
|
+
return {
|
|
22
|
+
message: null,
|
|
23
|
+
error: "Commit message cannot be empty.",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
message: trimmed,
|
|
29
|
+
error: null,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const parseTentacleSyncBaseRef = (
|
|
34
|
+
payload: unknown,
|
|
35
|
+
): { baseRef: string | null; error: string | null } => {
|
|
36
|
+
if (payload === null || payload === undefined) {
|
|
37
|
+
return {
|
|
38
|
+
baseRef: null,
|
|
39
|
+
error: null,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof payload !== "object") {
|
|
44
|
+
return {
|
|
45
|
+
baseRef: null,
|
|
46
|
+
error: "Expected a JSON object body.",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const rawBaseRef = (payload as Record<string, unknown>).baseRef;
|
|
51
|
+
if (rawBaseRef === undefined) {
|
|
52
|
+
return {
|
|
53
|
+
baseRef: null,
|
|
54
|
+
error: null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (typeof rawBaseRef !== "string") {
|
|
59
|
+
return {
|
|
60
|
+
baseRef: null,
|
|
61
|
+
error: "baseRef must be a string.",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const trimmed = rawBaseRef.trim();
|
|
66
|
+
if (trimmed.length === 0) {
|
|
67
|
+
return {
|
|
68
|
+
baseRef: null,
|
|
69
|
+
error: "baseRef cannot be empty.",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
baseRef: trimmed,
|
|
75
|
+
error: null,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const parseTentaclePullRequestCreateInput = (
|
|
80
|
+
payload: unknown,
|
|
81
|
+
): {
|
|
82
|
+
title: string | null;
|
|
83
|
+
body: string;
|
|
84
|
+
baseRef: string | null;
|
|
85
|
+
error: string | null;
|
|
86
|
+
} => {
|
|
87
|
+
if (payload === null || payload === undefined || typeof payload !== "object") {
|
|
88
|
+
return {
|
|
89
|
+
title: null,
|
|
90
|
+
body: "",
|
|
91
|
+
baseRef: null,
|
|
92
|
+
error: "Expected a JSON object body.",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const record = payload as Record<string, unknown>;
|
|
97
|
+
if (typeof record.title !== "string" || record.title.trim().length === 0) {
|
|
98
|
+
return {
|
|
99
|
+
title: null,
|
|
100
|
+
body: "",
|
|
101
|
+
baseRef: null,
|
|
102
|
+
error: "Pull request title cannot be empty.",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (record.body !== undefined && typeof record.body !== "string") {
|
|
107
|
+
return {
|
|
108
|
+
title: null,
|
|
109
|
+
body: "",
|
|
110
|
+
baseRef: null,
|
|
111
|
+
error: "Pull request body must be a string.",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (record.baseRef !== undefined && typeof record.baseRef !== "string") {
|
|
116
|
+
return {
|
|
117
|
+
title: null,
|
|
118
|
+
body: "",
|
|
119
|
+
baseRef: null,
|
|
120
|
+
error: "Pull request baseRef must be a string.",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const normalizedBaseRef = typeof record.baseRef === "string" ? record.baseRef.trim() : "";
|
|
125
|
+
if (record.baseRef !== undefined && normalizedBaseRef.length === 0) {
|
|
126
|
+
return {
|
|
127
|
+
title: null,
|
|
128
|
+
body: "",
|
|
129
|
+
baseRef: null,
|
|
130
|
+
error: "Pull request baseRef cannot be empty.",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
title: record.title.trim(),
|
|
136
|
+
body: typeof record.body === "string" ? record.body : "",
|
|
137
|
+
baseRef: normalizedBaseRef.length > 0 ? normalizedBaseRef : null,
|
|
138
|
+
error: null,
|
|
139
|
+
};
|
|
140
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { RuntimeInputError } from "../terminalRuntime";
|
|
2
|
+
import {
|
|
3
|
+
parseTentacleCommitMessage,
|
|
4
|
+
parseTentaclePullRequestCreateInput,
|
|
5
|
+
parseTentacleSyncBaseRef,
|
|
6
|
+
} from "./gitParsers";
|
|
7
|
+
import type { ApiRouteHandler } from "./routeHelpers";
|
|
8
|
+
import { readJsonBodyOrWriteError, writeJson, writeMethodNotAllowed } from "./routeHelpers";
|
|
9
|
+
|
|
10
|
+
const TENTACLE_GIT_ACTION_PATH_PATTERN =
|
|
11
|
+
/^\/api\/tentacles\/([^/]+)\/git\/(status|commit|push|sync)$/;
|
|
12
|
+
const TENTACLE_GIT_PULL_REQUEST_PATH_PATTERN = /^\/api\/tentacles\/([^/]+)\/git\/pr$/;
|
|
13
|
+
const TENTACLE_GIT_PULL_REQUEST_MERGE_PATH_PATTERN = /^\/api\/tentacles\/([^/]+)\/git\/pr\/merge$/;
|
|
14
|
+
|
|
15
|
+
export const handleTentacleGitRoute: ApiRouteHandler = async (
|
|
16
|
+
{ request, response, requestUrl, corsOrigin },
|
|
17
|
+
{ runtime },
|
|
18
|
+
) => {
|
|
19
|
+
const gitMatch = requestUrl.pathname.match(TENTACLE_GIT_ACTION_PATH_PATTERN);
|
|
20
|
+
if (!gitMatch) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const tentacleId = decodeURIComponent(gitMatch[1] ?? "");
|
|
25
|
+
const action = gitMatch[2];
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
if (action === "status") {
|
|
29
|
+
if (request.method !== "GET") {
|
|
30
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const payload = runtime.readTentacleGitStatus(tentacleId);
|
|
35
|
+
if (!payload) {
|
|
36
|
+
writeJson(response, 404, { error: "Tentacle not found." }, corsOrigin);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (action === "commit") {
|
|
45
|
+
if (request.method !== "POST") {
|
|
46
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
51
|
+
if (!bodyReadResult.ok) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const commitMessageResult = parseTentacleCommitMessage(bodyReadResult.payload);
|
|
56
|
+
if (commitMessageResult.error || !commitMessageResult.message) {
|
|
57
|
+
writeJson(
|
|
58
|
+
response,
|
|
59
|
+
400,
|
|
60
|
+
{ error: commitMessageResult.error ?? "Commit message cannot be empty." },
|
|
61
|
+
corsOrigin,
|
|
62
|
+
);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const payload = runtime.commitTentacleWorktree(tentacleId, commitMessageResult.message);
|
|
67
|
+
if (!payload) {
|
|
68
|
+
writeJson(response, 404, { error: "Tentacle not found." }, corsOrigin);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (action === "push") {
|
|
77
|
+
if (request.method !== "POST") {
|
|
78
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const payload = runtime.pushTentacleWorktree(tentacleId);
|
|
83
|
+
if (!payload) {
|
|
84
|
+
writeJson(response, 404, { error: "Tentacle not found." }, corsOrigin);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (request.method !== "POST") {
|
|
93
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
98
|
+
if (!bodyReadResult.ok) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const baseRefResult = parseTentacleSyncBaseRef(bodyReadResult.payload);
|
|
103
|
+
if (baseRefResult.error) {
|
|
104
|
+
writeJson(response, 400, { error: baseRefResult.error }, corsOrigin);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const payload = runtime.syncTentacleWorktree(tentacleId, baseRefResult.baseRef ?? undefined);
|
|
109
|
+
if (!payload) {
|
|
110
|
+
writeJson(response, 404, { error: "Tentacle not found." }, corsOrigin);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
115
|
+
return true;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error instanceof RuntimeInputError) {
|
|
118
|
+
writeJson(response, 409, { error: error.message }, corsOrigin);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const handleTentacleGitPullRequestRoute: ApiRouteHandler = async (
|
|
126
|
+
{ request, response, requestUrl, corsOrigin },
|
|
127
|
+
{ runtime },
|
|
128
|
+
) => {
|
|
129
|
+
const mergeMatch = requestUrl.pathname.match(TENTACLE_GIT_PULL_REQUEST_MERGE_PATH_PATTERN);
|
|
130
|
+
if (mergeMatch) {
|
|
131
|
+
if (request.method !== "POST") {
|
|
132
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const tentacleId = decodeURIComponent(mergeMatch[1] ?? "");
|
|
137
|
+
try {
|
|
138
|
+
const payload = runtime.mergeTentaclePullRequest(tentacleId);
|
|
139
|
+
if (!payload) {
|
|
140
|
+
writeJson(response, 404, { error: "Tentacle not found." }, corsOrigin);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
145
|
+
return true;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof RuntimeInputError) {
|
|
148
|
+
writeJson(response, 409, { error: error.message }, corsOrigin);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const prMatch = requestUrl.pathname.match(TENTACLE_GIT_PULL_REQUEST_PATH_PATTERN);
|
|
156
|
+
if (!prMatch) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const tentacleId = decodeURIComponent(prMatch[1] ?? "");
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
if (request.method === "GET") {
|
|
164
|
+
const payload = runtime.readTentaclePullRequest(tentacleId);
|
|
165
|
+
if (!payload) {
|
|
166
|
+
writeJson(response, 404, { error: "Tentacle not found." }, corsOrigin);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (request.method !== "POST") {
|
|
175
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
180
|
+
if (!bodyReadResult.ok) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const pullRequestInput = parseTentaclePullRequestCreateInput(bodyReadResult.payload);
|
|
185
|
+
if (pullRequestInput.error || !pullRequestInput.title) {
|
|
186
|
+
writeJson(
|
|
187
|
+
response,
|
|
188
|
+
400,
|
|
189
|
+
{ error: pullRequestInput.error ?? "Pull request title cannot be empty." },
|
|
190
|
+
corsOrigin,
|
|
191
|
+
);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const payload = runtime.createTentaclePullRequest(tentacleId, {
|
|
196
|
+
title: pullRequestInput.title,
|
|
197
|
+
...(pullRequestInput.body.length > 0 ? { body: pullRequestInput.body } : {}),
|
|
198
|
+
...(pullRequestInput.baseRef !== null ? { baseRef: pullRequestInput.baseRef } : {}),
|
|
199
|
+
});
|
|
200
|
+
if (!payload) {
|
|
201
|
+
writeJson(response, 404, { error: "Tentacle not found." }, corsOrigin);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
206
|
+
return true;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error instanceof RuntimeInputError) {
|
|
209
|
+
writeJson(response, 409, { error: error.message }, corsOrigin);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import type { WorkspaceSetupStepId } from "@octogent/core";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteUserPrompt,
|
|
5
|
+
listAllPrompts,
|
|
6
|
+
readPromptFromDirs,
|
|
7
|
+
resolvePrompt,
|
|
8
|
+
writeUserPrompt,
|
|
9
|
+
} from "../prompts";
|
|
10
|
+
import { markSetupStepVerified } from "../setupState";
|
|
11
|
+
import {
|
|
12
|
+
ensureWorkspaceGitignore,
|
|
13
|
+
initializeWorkspaceFiles,
|
|
14
|
+
readWorkspaceSetupSnapshot,
|
|
15
|
+
} from "../setupStatus";
|
|
16
|
+
import type { ApiRouteHandler } from "./routeHelpers";
|
|
17
|
+
import {
|
|
18
|
+
readJsonBodyOrWriteError,
|
|
19
|
+
writeJson,
|
|
20
|
+
writeMethodNotAllowed,
|
|
21
|
+
writeNoContent,
|
|
22
|
+
} from "./routeHelpers";
|
|
23
|
+
import { parseUiStatePatch } from "./uiStateParsers";
|
|
24
|
+
|
|
25
|
+
const WORKSPACE_SETUP_PATH = "/api/setup";
|
|
26
|
+
const WORKSPACE_SETUP_STEP_PATH_PATTERN = /^\/api\/setup\/steps\/([^/]+)$/;
|
|
27
|
+
|
|
28
|
+
const isWorkspaceSetupStepId = (value: string): value is WorkspaceSetupStepId =>
|
|
29
|
+
value === "initialize-workspace" ||
|
|
30
|
+
value === "ensure-gitignore" ||
|
|
31
|
+
value === "check-claude" ||
|
|
32
|
+
value === "check-git" ||
|
|
33
|
+
value === "check-curl" ||
|
|
34
|
+
value === "create-tentacles";
|
|
35
|
+
|
|
36
|
+
export const handleWorkspaceSetupRoute: ApiRouteHandler = async (
|
|
37
|
+
{ request, response, requestUrl, corsOrigin },
|
|
38
|
+
{ workspaceCwd, projectStateDir },
|
|
39
|
+
) => {
|
|
40
|
+
if (requestUrl.pathname === WORKSPACE_SETUP_PATH) {
|
|
41
|
+
if (request.method !== "GET") {
|
|
42
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
writeJson(response, 200, readWorkspaceSetupSnapshot(workspaceCwd, projectStateDir), corsOrigin);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const match = requestUrl.pathname.match(WORKSPACE_SETUP_STEP_PATH_PATTERN);
|
|
51
|
+
if (!match) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const stepId = decodeURIComponent(match[1] ?? "");
|
|
56
|
+
if (!isWorkspaceSetupStepId(stepId)) {
|
|
57
|
+
writeJson(response, 404, { error: "Setup step not found." }, corsOrigin);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (request.method !== "POST") {
|
|
62
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (stepId === "initialize-workspace") {
|
|
67
|
+
initializeWorkspaceFiles(workspaceCwd, projectStateDir);
|
|
68
|
+
} else if (stepId === "ensure-gitignore") {
|
|
69
|
+
ensureWorkspaceGitignore(workspaceCwd);
|
|
70
|
+
} else if (stepId === "check-claude" || stepId === "check-git" || stepId === "check-curl") {
|
|
71
|
+
markSetupStepVerified(projectStateDir, stepId);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
writeJson(response, 200, readWorkspaceSetupSnapshot(workspaceCwd, projectStateDir), corsOrigin);
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const handleUiStateRoute: ApiRouteHandler = async (
|
|
79
|
+
{ request, response, requestUrl, corsOrigin },
|
|
80
|
+
{ runtime },
|
|
81
|
+
) => {
|
|
82
|
+
if (requestUrl.pathname !== "/api/ui-state") {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (request.method === "GET") {
|
|
87
|
+
const payload = runtime.readUiState();
|
|
88
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (request.method !== "PATCH") {
|
|
93
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
98
|
+
if (!bodyReadResult.ok) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const uiStatePatch = parseUiStatePatch(bodyReadResult.payload);
|
|
103
|
+
if (uiStatePatch.error || !uiStatePatch.patch) {
|
|
104
|
+
writeJson(
|
|
105
|
+
response,
|
|
106
|
+
400,
|
|
107
|
+
{ error: uiStatePatch.error ?? "Invalid UI state patch." },
|
|
108
|
+
corsOrigin,
|
|
109
|
+
);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const payload = runtime.patchUiState(uiStatePatch.patch);
|
|
114
|
+
writeJson(response, 200, payload, corsOrigin);
|
|
115
|
+
return true;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const HOOK_PATH_PATTERN =
|
|
119
|
+
/^\/api\/hooks\/(session-start|user-prompt-submit|pre-tool-use|notification|stop)$/;
|
|
120
|
+
|
|
121
|
+
export const handleHookRoute: ApiRouteHandler = async (
|
|
122
|
+
{ request, response, requestUrl, corsOrigin },
|
|
123
|
+
{ runtime, invalidateClaudeUsageCache, readClaudeUsageSnapshot },
|
|
124
|
+
) => {
|
|
125
|
+
const match = requestUrl.pathname.match(HOOK_PATH_PATTERN);
|
|
126
|
+
if (!match) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (request.method !== "POST") {
|
|
131
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const body = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
136
|
+
if (!body.ok) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const hookName = match[1] ?? "";
|
|
141
|
+
// HTTP hooks pass the session ID via header; command hooks via query param.
|
|
142
|
+
const octogentSessionId =
|
|
143
|
+
(typeof request.headers["x-octogent-session"] === "string"
|
|
144
|
+
? request.headers["x-octogent-session"]
|
|
145
|
+
: undefined) ??
|
|
146
|
+
requestUrl.searchParams.get("octogent_session") ??
|
|
147
|
+
undefined;
|
|
148
|
+
const result = runtime.handleHook(hookName, body.payload, octogentSessionId);
|
|
149
|
+
|
|
150
|
+
if (hookName === "session-start" || hookName === "stop") {
|
|
151
|
+
invalidateClaudeUsageCache();
|
|
152
|
+
void readClaudeUsageSnapshot();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
writeJson(response, 200, result, corsOrigin);
|
|
156
|
+
return true;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const PROMPT_ITEM_PATH_PATTERN = /^\/api\/prompts\/([^/]+)$/;
|
|
160
|
+
|
|
161
|
+
export const handlePromptsCollectionRoute: ApiRouteHandler = async (
|
|
162
|
+
{ request, response, requestUrl, corsOrigin },
|
|
163
|
+
{ promptsDir, userPromptsDir },
|
|
164
|
+
) => {
|
|
165
|
+
if (requestUrl.pathname !== "/api/prompts") {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (request.method === "GET") {
|
|
170
|
+
const prompts = await listAllPrompts(promptsDir, userPromptsDir);
|
|
171
|
+
writeJson(response, 200, { prompts }, corsOrigin);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (request.method === "POST") {
|
|
176
|
+
const bodyResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
177
|
+
if (!bodyResult.ok) return true;
|
|
178
|
+
|
|
179
|
+
const body = bodyResult.payload as Record<string, unknown> | null;
|
|
180
|
+
const name = body && typeof body.name === "string" ? body.name.trim() : "";
|
|
181
|
+
const content = body && typeof body.content === "string" ? body.content : "";
|
|
182
|
+
|
|
183
|
+
if (name.length === 0) {
|
|
184
|
+
writeJson(response, 400, { error: "Prompt name is required." }, corsOrigin);
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const ok = await writeUserPrompt(userPromptsDir, name, content);
|
|
189
|
+
if (!ok) {
|
|
190
|
+
writeJson(response, 400, { error: "Invalid prompt name." }, corsOrigin);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
writeJson(response, 201, { name, source: "user" }, corsOrigin);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
199
|
+
return true;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export const handlePromptItemRoute: ApiRouteHandler = async (
|
|
203
|
+
{ request, response, requestUrl, corsOrigin },
|
|
204
|
+
{ promptsDir, userPromptsDir },
|
|
205
|
+
) => {
|
|
206
|
+
const match = requestUrl.pathname.match(PROMPT_ITEM_PATH_PATTERN);
|
|
207
|
+
if (!match) return false;
|
|
208
|
+
|
|
209
|
+
const name = decodeURIComponent(match[1] as string);
|
|
210
|
+
|
|
211
|
+
if (request.method === "GET") {
|
|
212
|
+
// Resolve variables from query params (e.g. ?tentacleId=sandbox).
|
|
213
|
+
const variables: Record<string, string> = {};
|
|
214
|
+
for (const [key, value] of requestUrl.searchParams.entries()) {
|
|
215
|
+
variables[key] = value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const hasVariables = Object.keys(variables).length > 0;
|
|
219
|
+
if (hasVariables) {
|
|
220
|
+
const resolved = await resolvePrompt(promptsDir, name, variables);
|
|
221
|
+
if (resolved === undefined) {
|
|
222
|
+
writeJson(response, 404, { error: "Prompt template not found" }, corsOrigin);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
writeJson(response, 200, { name, prompt: resolved }, corsOrigin);
|
|
226
|
+
} else {
|
|
227
|
+
const result = await readPromptFromDirs(promptsDir, userPromptsDir, name);
|
|
228
|
+
if (result === undefined) {
|
|
229
|
+
writeJson(response, 404, { error: "Prompt template not found" }, corsOrigin);
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
writeJson(response, 200, result, corsOrigin);
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (request.method === "PUT") {
|
|
238
|
+
const bodyResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
239
|
+
if (!bodyResult.ok) return true;
|
|
240
|
+
|
|
241
|
+
const body = bodyResult.payload as Record<string, unknown> | null;
|
|
242
|
+
const content = body && typeof body.content === "string" ? body.content : "";
|
|
243
|
+
|
|
244
|
+
const ok = await writeUserPrompt(userPromptsDir, name, content);
|
|
245
|
+
if (!ok) {
|
|
246
|
+
writeJson(response, 400, { error: "Invalid prompt name." }, corsOrigin);
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
writeJson(response, 200, { name, source: "user", content }, corsOrigin);
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (request.method === "DELETE") {
|
|
254
|
+
const ok = await deleteUserPrompt(userPromptsDir, name);
|
|
255
|
+
if (!ok) {
|
|
256
|
+
writeJson(response, 404, { error: "Prompt not found or cannot be deleted." }, corsOrigin);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
writeNoContent(response, 204, corsOrigin);
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
264
|
+
return true;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// ─── Channel routes ───────────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
const CHANNEL_MESSAGES_PATH_PATTERN = /^\/api\/channels\/([^/]+)\/messages$/;
|
|
270
|
+
|
|
271
|
+
export const handleChannelMessagesRoute: ApiRouteHandler = async (
|
|
272
|
+
{ request, response, requestUrl, corsOrigin },
|
|
273
|
+
{ runtime },
|
|
274
|
+
) => {
|
|
275
|
+
const match = requestUrl.pathname.match(CHANNEL_MESSAGES_PATH_PATTERN);
|
|
276
|
+
if (!match) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const terminalId = decodeURIComponent(match[1] ?? "");
|
|
281
|
+
|
|
282
|
+
if (request.method === "GET") {
|
|
283
|
+
const messages = runtime.listChannelMessages(terminalId);
|
|
284
|
+
writeJson(response, 200, { terminalId, messages }, corsOrigin);
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (request.method !== "POST") {
|
|
289
|
+
writeMethodNotAllowed(response, corsOrigin);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const bodyReadResult = await readJsonBodyOrWriteError(request, response, corsOrigin);
|
|
294
|
+
if (!bodyReadResult.ok) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const body = bodyReadResult.payload as Record<string, unknown> | null;
|
|
299
|
+
const fromTerminalId =
|
|
300
|
+
body && typeof body.fromTerminalId === "string" ? body.fromTerminalId.trim() : "";
|
|
301
|
+
const content = body && typeof body.content === "string" ? body.content.trim() : "";
|
|
302
|
+
|
|
303
|
+
if (content.length === 0) {
|
|
304
|
+
writeJson(response, 400, { error: "Message content cannot be empty." }, corsOrigin);
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const message = runtime.sendChannelMessage(terminalId, fromTerminalId, content);
|
|
309
|
+
if (!message) {
|
|
310
|
+
writeJson(response, 404, { error: "Target terminal not found." }, corsOrigin);
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
writeJson(response, 201, message, corsOrigin);
|
|
315
|
+
return true;
|
|
316
|
+
};
|