specmem-hardwicksoftware 3.5.99
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/CHANGELOG.md +299 -0
- package/LICENSE.md +6406 -0
- package/README.md +539 -0
- package/bin/AegisTheme.cjs +1022 -0
- package/bin/AnsiRenderer.cjs +1055 -0
- package/bin/BoxRenderer.cjs +605 -0
- package/bin/ClaudeLiveScreen.cjs +1299 -0
- package/bin/DashboardModules.cjs +733 -0
- package/bin/LiveScreenCapture.cjs +1012 -0
- package/bin/MemoryBrowserScreen.cjs +1595 -0
- package/bin/TabManager.cjs +1414 -0
- package/bin/checkAgentStatus-fix.patch +30 -0
- package/bin/mcp-socket-client.cjs +462 -0
- package/bin/screen-utils.cjs +106 -0
- package/bin/specmem-autoclaude.cjs +663 -0
- package/bin/specmem-cleanup.cjs +421 -0
- package/bin/specmem-cli.cjs +794 -0
- package/bin/specmem-console-teamcomms-class.cjs +428 -0
- package/bin/specmem-console.cjs +8104 -0
- package/bin/specmem-statusbar.cjs +530 -0
- package/bootstrap.cjs +5065 -0
- package/claude-hooks/agent-chooser-hook.js +179 -0
- package/claude-hooks/agent-chooser-inject.js +121 -0
- package/claude-hooks/agent-loading-hook.js +990 -0
- package/claude-hooks/agent-output-fader.cjs +542 -0
- package/claude-hooks/agent-output-interceptor.js +193 -0
- package/claude-hooks/agent-type-matcher.js +419 -0
- package/claude-hooks/auto-bypass.py +74 -0
- package/claude-hooks/background-completion-silencer.js +134 -0
- package/claude-hooks/bash-auto-background.js +182 -0
- package/claude-hooks/build-cedict-dictionary.mjs +167 -0
- package/claude-hooks/bullshit-radar.cjs +323 -0
- package/claude-hooks/cedict-codes.json +270 -0
- package/claude-hooks/cedict-extracted.json +22632 -0
- package/claude-hooks/claude-watchdog.sh +401 -0
- package/claude-hooks/context-dedup.cjs +144 -0
- package/claude-hooks/context-yeeter.cjs +244 -0
- package/claude-hooks/debug-suffix.cjs +15 -0
- package/claude-hooks/debug2.cjs +7 -0
- package/claude-hooks/drilldown-enforcer.js +242 -0
- package/claude-hooks/english-morphology-standalone.cjs +149 -0
- package/claude-hooks/english-morphology.cjs +152 -0
- package/claude-hooks/extract-translations.mjs +193 -0
- package/claude-hooks/file-claim-enforcer.cjs +293 -0
- package/claude-hooks/file-claim-enforcer.js +293 -0
- package/claude-hooks/find-collisions.cjs +39 -0
- package/claude-hooks/fix-abbreviations.cjs +60 -0
- package/claude-hooks/fix-collisions.cjs +60 -0
- package/claude-hooks/fix-decompressor.cjs +79 -0
- package/claude-hooks/fix-suffixes.cjs +66 -0
- package/claude-hooks/grammar-engine.cjs +159 -0
- package/claude-hooks/input-aware-improver.js +231 -0
- package/claude-hooks/is-agent.cjs +64 -0
- package/claude-hooks/mega-test.cjs +213 -0
- package/claude-hooks/merge-dictionaries.mjs +207 -0
- package/claude-hooks/merged-codes.cjs +22675 -0
- package/claude-hooks/merged-codes.json +22676 -0
- package/claude-hooks/output-cleaner.cjs +388 -0
- package/claude-hooks/post-write-memory-hook.cjs +430 -0
- package/claude-hooks/quick-test.cjs +24 -0
- package/claude-hooks/quick-test2.cjs +24 -0
- package/claude-hooks/remove-bad-codes.cjs +23 -0
- package/claude-hooks/search-reminder-hook.js +90 -0
- package/claude-hooks/semantic-test.cjs +93 -0
- package/claude-hooks/settings.json +445 -0
- package/claude-hooks/smart-context-hook.cjs +547 -0
- package/claude-hooks/smart-context-hook.js +539 -0
- package/claude-hooks/smart-search-interceptor.js +364 -0
- package/claude-hooks/socket-connect-helper.cjs +235 -0
- package/claude-hooks/specmem/sockets/session-start.lock +1 -0
- package/claude-hooks/specmem-context-hook.cjs +357 -0
- package/claude-hooks/specmem-context-hook.js +357 -0
- package/claude-hooks/specmem-drilldown-hook.cjs +480 -0
- package/claude-hooks/specmem-drilldown-hook.js +480 -0
- package/claude-hooks/specmem-drilldown-setter.js +210 -0
- package/claude-hooks/specmem-paths.cjs +213 -0
- package/claude-hooks/specmem-precompact.js +183 -0
- package/claude-hooks/specmem-session-init.sh +33 -0
- package/claude-hooks/specmem-session-start.cjs +498 -0
- package/claude-hooks/specmem-stop-hook.cjs +73 -0
- package/claude-hooks/specmem-stop-hook.js +5 -0
- package/claude-hooks/specmem-team-comms.cjs +434 -0
- package/claude-hooks/specmem-team-member-inject.js +271 -0
- package/claude-hooks/specmem-unified-hook.py +670 -0
- package/claude-hooks/subagent-loading-hook.js +194 -0
- package/claude-hooks/sysprompt-squisher.cjs +167 -0
- package/claude-hooks/task-progress-hook.js +204 -0
- package/claude-hooks/team-comms-enforcer.cjs +585 -0
- package/claude-hooks/test-accuracy.cjs +27 -0
- package/claude-hooks/test-big.cjs +28 -0
- package/claude-hooks/test-inflectors.cjs +39 -0
- package/claude-hooks/test-pluralize.cjs +37 -0
- package/claude-hooks/test-quick.cjs +8 -0
- package/claude-hooks/test-wink.cjs +20 -0
- package/claude-hooks/token-compressor.cjs +940 -0
- package/claude-hooks/use-code-pointers.cjs +279 -0
- package/commands/COMMAND_TOOL_MAP.md +299 -0
- package/commands/specmem-agents.md +412 -0
- package/commands/specmem-autoclaude.md +295 -0
- package/commands/specmem-changes.md +247 -0
- package/commands/specmem-code.md +103 -0
- package/commands/specmem-configteammembercomms.md +322 -0
- package/commands/specmem-drilldown.md +208 -0
- package/commands/specmem-find.md +195 -0
- package/commands/specmem-getdashboard.md +243 -0
- package/commands/specmem-hooks.md +219 -0
- package/commands/specmem-pointers.md +149 -0
- package/commands/specmem-progress.md +287 -0
- package/commands/specmem-remember.md +123 -0
- package/commands/specmem-service.md +349 -0
- package/commands/specmem-stats.md +189 -0
- package/commands/specmem-team-member.md +409 -0
- package/commands/specmem-webdev.md +583 -0
- package/commands/specmem.md +363 -0
- package/dist/autoStart/index.d.ts +214 -0
- package/dist/autoStart/index.d.ts.map +1 -0
- package/dist/autoStart/index.js +883 -0
- package/dist/autoStart/index.js.map +1 -0
- package/dist/claude-sessions/contextRestorationParser.d.ts +74 -0
- package/dist/claude-sessions/contextRestorationParser.d.ts.map +1 -0
- package/dist/claude-sessions/contextRestorationParser.js +570 -0
- package/dist/claude-sessions/contextRestorationParser.js.map +1 -0
- package/dist/claude-sessions/index.d.ts +13 -0
- package/dist/claude-sessions/index.d.ts.map +1 -0
- package/dist/claude-sessions/index.js +11 -0
- package/dist/claude-sessions/index.js.map +1 -0
- package/dist/claude-sessions/sessionIntegration.d.ts +48 -0
- package/dist/claude-sessions/sessionIntegration.d.ts.map +1 -0
- package/dist/claude-sessions/sessionIntegration.js +146 -0
- package/dist/claude-sessions/sessionIntegration.js.map +1 -0
- package/dist/claude-sessions/sessionParser.d.ts +293 -0
- package/dist/claude-sessions/sessionParser.d.ts.map +1 -0
- package/dist/claude-sessions/sessionParser.js +1028 -0
- package/dist/claude-sessions/sessionParser.js.map +1 -0
- package/dist/claude-sessions/sessionWatcher.d.ts +139 -0
- package/dist/claude-sessions/sessionWatcher.d.ts.map +1 -0
- package/dist/claude-sessions/sessionWatcher.js +722 -0
- package/dist/claude-sessions/sessionWatcher.js.map +1 -0
- package/dist/cli/deploy-to-claude.d.ts +56 -0
- package/dist/cli/deploy-to-claude.d.ts.map +1 -0
- package/dist/cli/deploy-to-claude.js +576 -0
- package/dist/cli/deploy-to-claude.js.map +1 -0
- package/dist/code-explanations/explainCode.d.ts +86 -0
- package/dist/code-explanations/explainCode.d.ts.map +1 -0
- package/dist/code-explanations/explainCode.js +286 -0
- package/dist/code-explanations/explainCode.js.map +1 -0
- package/dist/code-explanations/feedback.d.ts +87 -0
- package/dist/code-explanations/feedback.d.ts.map +1 -0
- package/dist/code-explanations/feedback.js +212 -0
- package/dist/code-explanations/feedback.js.map +1 -0
- package/dist/code-explanations/getRelatedCode.d.ts +80 -0
- package/dist/code-explanations/getRelatedCode.d.ts.map +1 -0
- package/dist/code-explanations/getRelatedCode.js +262 -0
- package/dist/code-explanations/getRelatedCode.js.map +1 -0
- package/dist/code-explanations/index.d.ts +284 -0
- package/dist/code-explanations/index.d.ts.map +1 -0
- package/dist/code-explanations/index.js +249 -0
- package/dist/code-explanations/index.js.map +1 -0
- package/dist/code-explanations/linkCodeToPrompt.d.ts +79 -0
- package/dist/code-explanations/linkCodeToPrompt.d.ts.map +1 -0
- package/dist/code-explanations/linkCodeToPrompt.js +213 -0
- package/dist/code-explanations/linkCodeToPrompt.js.map +1 -0
- package/dist/code-explanations/recallExplanation.d.ts +88 -0
- package/dist/code-explanations/recallExplanation.d.ts.map +1 -0
- package/dist/code-explanations/recallExplanation.js +218 -0
- package/dist/code-explanations/recallExplanation.js.map +1 -0
- package/dist/code-explanations/schema.d.ts +32 -0
- package/dist/code-explanations/schema.d.ts.map +1 -0
- package/dist/code-explanations/schema.js +221 -0
- package/dist/code-explanations/schema.js.map +1 -0
- package/dist/code-explanations/semanticSearch.d.ts +75 -0
- package/dist/code-explanations/semanticSearch.d.ts.map +1 -0
- package/dist/code-explanations/semanticSearch.js +203 -0
- package/dist/code-explanations/semanticSearch.js.map +1 -0
- package/dist/code-explanations/types.d.ts +328 -0
- package/dist/code-explanations/types.d.ts.map +1 -0
- package/dist/code-explanations/types.js +122 -0
- package/dist/code-explanations/types.js.map +1 -0
- package/dist/codebase/codeAnalyzer.d.ts +272 -0
- package/dist/codebase/codeAnalyzer.d.ts.map +1 -0
- package/dist/codebase/codeAnalyzer.js +1353 -0
- package/dist/codebase/codeAnalyzer.js.map +1 -0
- package/dist/codebase/codebaseIndexer.d.ts +360 -0
- package/dist/codebase/codebaseIndexer.d.ts.map +1 -0
- package/dist/codebase/codebaseIndexer.js +1735 -0
- package/dist/codebase/codebaseIndexer.js.map +1 -0
- package/dist/codebase/codebaseTools.d.ts +853 -0
- package/dist/codebase/codebaseTools.d.ts.map +1 -0
- package/dist/codebase/codebaseTools.js +1279 -0
- package/dist/codebase/codebaseTools.js.map +1 -0
- package/dist/codebase/exclusions.d.ts +111 -0
- package/dist/codebase/exclusions.d.ts.map +1 -0
- package/dist/codebase/exclusions.js +771 -0
- package/dist/codebase/exclusions.js.map +1 -0
- package/dist/codebase/fileWatcher.d.ts +135 -0
- package/dist/codebase/fileWatcher.d.ts.map +1 -0
- package/dist/codebase/fileWatcher.js +309 -0
- package/dist/codebase/fileWatcher.js.map +1 -0
- package/dist/codebase/index.d.ts +33 -0
- package/dist/codebase/index.d.ts.map +1 -0
- package/dist/codebase/index.js +77 -0
- package/dist/codebase/index.js.map +1 -0
- package/dist/codebase/ingestion.d.ts +177 -0
- package/dist/codebase/ingestion.d.ts.map +1 -0
- package/dist/codebase/ingestion.js +690 -0
- package/dist/codebase/ingestion.js.map +1 -0
- package/dist/codebase/languageDetection.d.ts +75 -0
- package/dist/codebase/languageDetection.d.ts.map +1 -0
- package/dist/codebase/languageDetection.js +768 -0
- package/dist/codebase/languageDetection.js.map +1 -0
- package/dist/commands/codebaseCommands.d.ts +101 -0
- package/dist/commands/codebaseCommands.d.ts.map +1 -0
- package/dist/commands/codebaseCommands.js +911 -0
- package/dist/commands/codebaseCommands.js.map +1 -0
- package/dist/commands/commandHandler.d.ts +126 -0
- package/dist/commands/commandHandler.d.ts.map +1 -0
- package/dist/commands/commandHandler.js +296 -0
- package/dist/commands/commandHandler.js.map +1 -0
- package/dist/commands/commandLoader.d.ts +103 -0
- package/dist/commands/commandLoader.d.ts.map +1 -0
- package/dist/commands/commandLoader.js +223 -0
- package/dist/commands/commandLoader.js.map +1 -0
- package/dist/commands/contextCommands.d.ts +83 -0
- package/dist/commands/contextCommands.d.ts.map +1 -0
- package/dist/commands/contextCommands.js +512 -0
- package/dist/commands/contextCommands.js.map +1 -0
- package/dist/commands/index.d.ts +24 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +28 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/mcpResources.d.ts +50 -0
- package/dist/commands/mcpResources.d.ts.map +1 -0
- package/dist/commands/mcpResources.js +372 -0
- package/dist/commands/mcpResources.js.map +1 -0
- package/dist/commands/memoryCommands.d.ts +74 -0
- package/dist/commands/memoryCommands.d.ts.map +1 -0
- package/dist/commands/memoryCommands.js +609 -0
- package/dist/commands/memoryCommands.js.map +1 -0
- package/dist/commands/promptCommands.d.ts +91 -0
- package/dist/commands/promptCommands.d.ts.map +1 -0
- package/dist/commands/promptCommands.js +801 -0
- package/dist/commands/promptCommands.js.map +1 -0
- package/dist/commands/teamMemberCommands.d.ts +21 -0
- package/dist/commands/teamMemberCommands.d.ts.map +1 -0
- package/dist/commands/teamMemberCommands.js +137 -0
- package/dist/commands/teamMemberCommands.js.map +1 -0
- package/dist/comms/fileCommsTransport.d.ts +91 -0
- package/dist/comms/fileCommsTransport.d.ts.map +1 -0
- package/dist/comms/fileCommsTransport.js +244 -0
- package/dist/comms/fileCommsTransport.js.map +1 -0
- package/dist/comms/index.d.ts +7 -0
- package/dist/comms/index.d.ts.map +1 -0
- package/dist/comms/index.js +7 -0
- package/dist/comms/index.js.map +1 -0
- package/dist/config/apiKeyDetection.d.ts +41 -0
- package/dist/config/apiKeyDetection.d.ts.map +1 -0
- package/dist/config/apiKeyDetection.js +211 -0
- package/dist/config/apiKeyDetection.js.map +1 -0
- package/dist/config/autoConfig.d.ts +188 -0
- package/dist/config/autoConfig.d.ts.map +1 -0
- package/dist/config/autoConfig.js +850 -0
- package/dist/config/autoConfig.js.map +1 -0
- package/dist/config/configSync.d.ts +119 -0
- package/dist/config/configSync.d.ts.map +1 -0
- package/dist/config/configSync.js +878 -0
- package/dist/config/configSync.js.map +1 -0
- package/dist/config/embeddingTimeouts.d.ts +145 -0
- package/dist/config/embeddingTimeouts.d.ts.map +1 -0
- package/dist/config/embeddingTimeouts.js +255 -0
- package/dist/config/embeddingTimeouts.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +7 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/languageConfig.d.ts +68 -0
- package/dist/config/languageConfig.d.ts.map +1 -0
- package/dist/config/languageConfig.js +473 -0
- package/dist/config/languageConfig.js.map +1 -0
- package/dist/config/password.d.ts +145 -0
- package/dist/config/password.d.ts.map +1 -0
- package/dist/config/password.js +428 -0
- package/dist/config/password.js.map +1 -0
- package/dist/config.d.ts +338 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +1177 -0
- package/dist/config.js.map +1 -0
- package/dist/consolidation.d.ts +44 -0
- package/dist/consolidation.d.ts.map +1 -0
- package/dist/consolidation.js +447 -0
- package/dist/consolidation.js.map +1 -0
- package/dist/constants.d.ts +371 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +552 -0
- package/dist/constants.js.map +1 -0
- package/dist/coordination/TeamMemberRegistry.d.ts +192 -0
- package/dist/coordination/TeamMemberRegistry.d.ts.map +1 -0
- package/dist/coordination/TeamMemberRegistry.js +415 -0
- package/dist/coordination/TeamMemberRegistry.js.map +1 -0
- package/dist/coordination/events.d.ts +369 -0
- package/dist/coordination/events.d.ts.map +1 -0
- package/dist/coordination/events.js +232 -0
- package/dist/coordination/events.js.map +1 -0
- package/dist/coordination/handlers.d.ts +116 -0
- package/dist/coordination/handlers.d.ts.map +1 -0
- package/dist/coordination/handlers.js +400 -0
- package/dist/coordination/handlers.js.map +1 -0
- package/dist/coordination/index.d.ts +14 -0
- package/dist/coordination/index.d.ts.map +1 -0
- package/dist/coordination/index.js +31 -0
- package/dist/coordination/index.js.map +1 -0
- package/dist/coordination/integration.d.ts +260 -0
- package/dist/coordination/integration.d.ts.map +1 -0
- package/dist/coordination/integration.js +472 -0
- package/dist/coordination/integration.js.map +1 -0
- package/dist/coordination/server.d.ts +266 -0
- package/dist/coordination/server.d.ts.map +1 -0
- package/dist/coordination/server.js +995 -0
- package/dist/coordination/server.js.map +1 -0
- package/dist/coordination/serviceProvider.d.ts +70 -0
- package/dist/coordination/serviceProvider.d.ts.map +1 -0
- package/dist/coordination/serviceProvider.js +273 -0
- package/dist/coordination/serviceProvider.js.map +1 -0
- package/dist/dashboard/api/claudeControl.d.ts +44 -0
- package/dist/dashboard/api/claudeControl.d.ts.map +1 -0
- package/dist/dashboard/api/claudeControl.js +650 -0
- package/dist/dashboard/api/claudeControl.js.map +1 -0
- package/dist/dashboard/api/claudeHistory.d.ts +4 -0
- package/dist/dashboard/api/claudeHistory.d.ts.map +1 -0
- package/dist/dashboard/api/claudeHistory.js +319 -0
- package/dist/dashboard/api/claudeHistory.js.map +1 -0
- package/dist/dashboard/api/dataExport.d.ts +23 -0
- package/dist/dashboard/api/dataExport.d.ts.map +1 -0
- package/dist/dashboard/api/dataExport.js +509 -0
- package/dist/dashboard/api/dataExport.js.map +1 -0
- package/dist/dashboard/api/fileManager.d.ts +39 -0
- package/dist/dashboard/api/fileManager.d.ts.map +1 -0
- package/dist/dashboard/api/fileManager.js +814 -0
- package/dist/dashboard/api/fileManager.js.map +1 -0
- package/dist/dashboard/api/hooks.d.ts +16 -0
- package/dist/dashboard/api/hooks.d.ts.map +1 -0
- package/dist/dashboard/api/hooks.js +342 -0
- package/dist/dashboard/api/hooks.js.map +1 -0
- package/dist/dashboard/api/hotReload.d.ts +14 -0
- package/dist/dashboard/api/hotReload.d.ts.map +1 -0
- package/dist/dashboard/api/hotReload.js +219 -0
- package/dist/dashboard/api/hotReload.js.map +1 -0
- package/dist/dashboard/api/liveSessionStream.d.ts +19 -0
- package/dist/dashboard/api/liveSessionStream.d.ts.map +1 -0
- package/dist/dashboard/api/liveSessionStream.js +430 -0
- package/dist/dashboard/api/liveSessionStream.js.map +1 -0
- package/dist/dashboard/api/memoryRecall.d.ts +20 -0
- package/dist/dashboard/api/memoryRecall.d.ts.map +1 -0
- package/dist/dashboard/api/memoryRecall.js +524 -0
- package/dist/dashboard/api/memoryRecall.js.map +1 -0
- package/dist/dashboard/api/promptSend.d.ts +33 -0
- package/dist/dashboard/api/promptSend.d.ts.map +1 -0
- package/dist/dashboard/api/promptSend.js +544 -0
- package/dist/dashboard/api/promptSend.js.map +1 -0
- package/dist/dashboard/api/settings.d.ts +10 -0
- package/dist/dashboard/api/settings.d.ts.map +1 -0
- package/dist/dashboard/api/settings.js +656 -0
- package/dist/dashboard/api/settings.js.map +1 -0
- package/dist/dashboard/api/setup.d.ts +21 -0
- package/dist/dashboard/api/setup.d.ts.map +1 -0
- package/dist/dashboard/api/setup.js +663 -0
- package/dist/dashboard/api/setup.js.map +1 -0
- package/dist/dashboard/api/specmemTools.d.ts +14 -0
- package/dist/dashboard/api/specmemTools.d.ts.map +1 -0
- package/dist/dashboard/api/specmemTools.js +1059 -0
- package/dist/dashboard/api/specmemTools.js.map +1 -0
- package/dist/dashboard/api/taskTeamMembers.d.ts +8 -0
- package/dist/dashboard/api/taskTeamMembers.d.ts.map +1 -0
- package/dist/dashboard/api/taskTeamMembers.js +136 -0
- package/dist/dashboard/api/taskTeamMembers.js.map +1 -0
- package/dist/dashboard/api/teamMemberDeploy.d.ts +15 -0
- package/dist/dashboard/api/teamMemberDeploy.d.ts.map +1 -0
- package/dist/dashboard/api/teamMemberDeploy.js +421 -0
- package/dist/dashboard/api/teamMemberDeploy.js.map +1 -0
- package/dist/dashboard/api/teamMemberHistory.d.ts +38 -0
- package/dist/dashboard/api/teamMemberHistory.d.ts.map +1 -0
- package/dist/dashboard/api/teamMemberHistory.js +583 -0
- package/dist/dashboard/api/teamMemberHistory.js.map +1 -0
- package/dist/dashboard/api/terminal.d.ts +12 -0
- package/dist/dashboard/api/terminal.d.ts.map +1 -0
- package/dist/dashboard/api/terminal.js +344 -0
- package/dist/dashboard/api/terminal.js.map +1 -0
- package/dist/dashboard/api/terminalInject.d.ts +17 -0
- package/dist/dashboard/api/terminalInject.d.ts.map +1 -0
- package/dist/dashboard/api/terminalInject.js +322 -0
- package/dist/dashboard/api/terminalInject.js.map +1 -0
- package/dist/dashboard/api/terminalStream.d.ts +12 -0
- package/dist/dashboard/api/terminalStream.d.ts.map +1 -0
- package/dist/dashboard/api/terminalStream.js +482 -0
- package/dist/dashboard/api/terminalStream.js.map +1 -0
- package/dist/dashboard/index.d.ts +7 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +7 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/ptyStreamer.d.ts +173 -0
- package/dist/dashboard/ptyStreamer.d.ts.map +1 -0
- package/dist/dashboard/ptyStreamer.js +661 -0
- package/dist/dashboard/ptyStreamer.js.map +1 -0
- package/dist/dashboard/public/DASHBOARD-README.md +378 -0
- package/dist/dashboard/public/INTEGRATION-GUIDE.md +395 -0
- package/dist/dashboard/public/codebase-config.html +1247 -0
- package/dist/dashboard/public/dashboard-v2.html +1942 -0
- package/dist/dashboard/public/data-export.html +819 -0
- package/dist/dashboard/public/example-page.html +164 -0
- package/dist/dashboard/public/file-explorer.html +1023 -0
- package/dist/dashboard/public/hooks.html +1103 -0
- package/dist/dashboard/public/index-improvements.css +499 -0
- package/dist/dashboard/public/index.html +5534 -0
- package/dist/dashboard/public/memory-controls.html +1959 -0
- package/dist/dashboard/public/memory-recall.html +1495 -0
- package/dist/dashboard/public/previews/skeleton-memory-graph.html +361 -0
- package/dist/dashboard/public/previews/skeleton-memory-list.html +366 -0
- package/dist/dashboard/public/previews/skeleton-search-results.html +609 -0
- package/dist/dashboard/public/previews/skeleton-stats-dashboard.html +556 -0
- package/dist/dashboard/public/prompt-console.html +2763 -0
- package/dist/dashboard/public/react-dist/assets/index-CkjobT5B.js +871 -0
- package/dist/dashboard/public/react-dist/assets/index-iRclxMst.css +1 -0
- package/dist/dashboard/public/react-dist/index.html +16 -0
- package/dist/dashboard/public/shared-header.js +325 -0
- package/dist/dashboard/public/shared-language-selector.js +626 -0
- package/dist/dashboard/public/shared-logger.js +66 -0
- package/dist/dashboard/public/shared-nav.js +325 -0
- package/dist/dashboard/public/shared-theme-blue.css +331 -0
- package/dist/dashboard/public/shared-theme.css +813 -0
- package/dist/dashboard/public/shared-toast.js +415 -0
- package/dist/dashboard/public/team-member-history.html +1291 -0
- package/dist/dashboard/public/team-member-spy.html +1199 -0
- package/dist/dashboard/public/team-members.html +3756 -0
- package/dist/dashboard/public/terminal-output.html +1013 -0
- package/dist/dashboard/public/terminal.html +372 -0
- package/dist/dashboard/sessionStore.d.ts +86 -0
- package/dist/dashboard/sessionStore.d.ts.map +1 -0
- package/dist/dashboard/sessionStore.js +262 -0
- package/dist/dashboard/sessionStore.js.map +1 -0
- package/dist/dashboard/standalone.d.ts +27 -0
- package/dist/dashboard/standalone.d.ts.map +1 -0
- package/dist/dashboard/standalone.js +380 -0
- package/dist/dashboard/standalone.js.map +1 -0
- package/dist/dashboard/webServer.d.ts +390 -0
- package/dist/dashboard/webServer.d.ts.map +1 -0
- package/dist/dashboard/webServer.js +4297 -0
- package/dist/dashboard/webServer.js.map +1 -0
- package/dist/dashboard/websocket/teamMemberStream.d.ts +87 -0
- package/dist/dashboard/websocket/teamMemberStream.d.ts.map +1 -0
- package/dist/dashboard/websocket/teamMemberStream.js +366 -0
- package/dist/dashboard/websocket/teamMemberStream.js.map +1 -0
- package/dist/dashboard/websocket/terminalStream.d.ts +130 -0
- package/dist/dashboard/websocket/terminalStream.d.ts.map +1 -0
- package/dist/dashboard/websocket/terminalStream.js +456 -0
- package/dist/dashboard/websocket/terminalStream.js.map +1 -0
- package/dist/database/embeddedPostgres.d.ts +187 -0
- package/dist/database/embeddedPostgres.d.ts.map +1 -0
- package/dist/database/embeddedPostgres.js +763 -0
- package/dist/database/embeddedPostgres.js.map +1 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +20 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/initEmbeddedPostgres.d.ts +124 -0
- package/dist/database/initEmbeddedPostgres.d.ts.map +1 -0
- package/dist/database/initEmbeddedPostgres.js +855 -0
- package/dist/database/initEmbeddedPostgres.js.map +1 -0
- package/dist/database.d.ts +256 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +1209 -0
- package/dist/database.js.map +1 -0
- package/dist/db/apiDataManager.d.ts +334 -0
- package/dist/db/apiDataManager.d.ts.map +1 -0
- package/dist/db/apiDataManager.js +631 -0
- package/dist/db/apiDataManager.js.map +1 -0
- package/dist/db/batchOperations.d.ts +154 -0
- package/dist/db/batchOperations.d.ts.map +1 -0
- package/dist/db/batchOperations.js +564 -0
- package/dist/db/batchOperations.js.map +1 -0
- package/dist/db/bigBrainMigrations.d.ts +48 -0
- package/dist/db/bigBrainMigrations.d.ts.map +1 -0
- package/dist/db/bigBrainMigrations.js +4266 -0
- package/dist/db/bigBrainMigrations.js.map +1 -0
- package/dist/db/connectionPoolGoBrrr.d.ts +94 -0
- package/dist/db/connectionPoolGoBrrr.d.ts.map +1 -0
- package/dist/db/connectionPoolGoBrrr.js +548 -0
- package/dist/db/connectionPoolGoBrrr.js.map +1 -0
- package/dist/db/dashboardQueries.d.ts +182 -0
- package/dist/db/dashboardQueries.d.ts.map +1 -0
- package/dist/db/dashboardQueries.js +821 -0
- package/dist/db/dashboardQueries.js.map +1 -0
- package/dist/db/deploymentBootstrap.d.ts +43 -0
- package/dist/db/deploymentBootstrap.d.ts.map +1 -0
- package/dist/db/deploymentBootstrap.js +329 -0
- package/dist/db/deploymentBootstrap.js.map +1 -0
- package/dist/db/dimensionService.d.ts +140 -0
- package/dist/db/dimensionService.d.ts.map +1 -0
- package/dist/db/dimensionService.js +261 -0
- package/dist/db/dimensionService.js.map +1 -0
- package/dist/db/embeddingOverflow.d.ts +69 -0
- package/dist/db/embeddingOverflow.d.ts.map +1 -0
- package/dist/db/embeddingOverflow.js +332 -0
- package/dist/db/embeddingOverflow.js.map +1 -0
- package/dist/db/embeddingOverflow.sql +221 -0
- package/dist/db/findThatShit.d.ts +145 -0
- package/dist/db/findThatShit.d.ts.map +1 -0
- package/dist/db/findThatShit.js +782 -0
- package/dist/db/findThatShit.js.map +1 -0
- package/dist/db/hotPathManager.d.ts +187 -0
- package/dist/db/hotPathManager.d.ts.map +1 -0
- package/dist/db/hotPathManager.js +504 -0
- package/dist/db/hotPathManager.js.map +1 -0
- package/dist/db/index.d.ts +85 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +219 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/memoryDrilldown.sql +99 -0
- package/dist/db/migrate.d.ts +3 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +97 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/migrateJsonToPostgres.d.ts +43 -0
- package/dist/db/migrateJsonToPostgres.d.ts.map +1 -0
- package/dist/db/migrateJsonToPostgres.js +465 -0
- package/dist/db/migrateJsonToPostgres.js.map +1 -0
- package/dist/db/nukeFromOrbit.d.ts +63 -0
- package/dist/db/nukeFromOrbit.d.ts.map +1 -0
- package/dist/db/nukeFromOrbit.js +499 -0
- package/dist/db/nukeFromOrbit.js.map +1 -0
- package/dist/db/processedTraining.sql +60 -0
- package/dist/db/projectNamespacing.d.ts +258 -0
- package/dist/db/projectNamespacing.d.ts.map +1 -0
- package/dist/db/projectNamespacing.js +920 -0
- package/dist/db/projectNamespacing.js.map +1 -0
- package/dist/db/projectNamespacing.sql +374 -0
- package/dist/db/projectSchemaInit.sql +271 -0
- package/dist/db/spatialMemory.d.ts +296 -0
- package/dist/db/spatialMemory.d.ts.map +1 -0
- package/dist/db/spatialMemory.js +818 -0
- package/dist/db/spatialMemory.js.map +1 -0
- package/dist/db/streamingQuery.d.ts +143 -0
- package/dist/db/streamingQuery.d.ts.map +1 -0
- package/dist/db/streamingQuery.js +350 -0
- package/dist/db/streamingQuery.js.map +1 -0
- package/dist/db/teamComms.sql +224 -0
- package/dist/db/yeetStuffInDb.d.ts +72 -0
- package/dist/db/yeetStuffInDb.d.ts.map +1 -0
- package/dist/db/yeetStuffInDb.js +473 -0
- package/dist/db/yeetStuffInDb.js.map +1 -0
- package/dist/embedding-providers/index.d.ts +10 -0
- package/dist/embedding-providers/index.d.ts.map +1 -0
- package/dist/embedding-providers/index.js +12 -0
- package/dist/embedding-providers/index.js.map +1 -0
- package/dist/embeddings/projectionLayer.d.ts +114 -0
- package/dist/embeddings/projectionLayer.d.ts.map +1 -0
- package/dist/embeddings/projectionLayer.js +345 -0
- package/dist/embeddings/projectionLayer.js.map +1 -0
- package/dist/events/Publisher.d.ts +193 -0
- package/dist/events/Publisher.d.ts.map +1 -0
- package/dist/events/Publisher.js +439 -0
- package/dist/events/Publisher.js.map +1 -0
- package/dist/events/config.d.ts +139 -0
- package/dist/events/config.d.ts.map +1 -0
- package/dist/events/config.js +266 -0
- package/dist/events/config.js.map +1 -0
- package/dist/events/index.d.ts +19 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +31 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/integration.d.ts +206 -0
- package/dist/events/integration.d.ts.map +1 -0
- package/dist/events/integration.js +348 -0
- package/dist/events/integration.js.map +1 -0
- package/dist/events/metrics.d.ts +147 -0
- package/dist/events/metrics.d.ts.map +1 -0
- package/dist/events/metrics.js +343 -0
- package/dist/events/metrics.js.map +1 -0
- package/dist/hooks/cli.d.ts +28 -0
- package/dist/hooks/cli.d.ts.map +1 -0
- package/dist/hooks/cli.js +118 -0
- package/dist/hooks/cli.js.map +1 -0
- package/dist/hooks/contextInjectionHook.d.ts +60 -0
- package/dist/hooks/contextInjectionHook.d.ts.map +1 -0
- package/dist/hooks/contextInjectionHook.js +294 -0
- package/dist/hooks/contextInjectionHook.js.map +1 -0
- package/dist/hooks/drilldownHook.d.ts +125 -0
- package/dist/hooks/drilldownHook.d.ts.map +1 -0
- package/dist/hooks/drilldownHook.js +181 -0
- package/dist/hooks/drilldownHook.js.map +1 -0
- package/dist/hooks/hookManager.d.ts +180 -0
- package/dist/hooks/hookManager.d.ts.map +1 -0
- package/dist/hooks/hookManager.js +782 -0
- package/dist/hooks/hookManager.js.map +1 -0
- package/dist/hooks/index.d.ts +62 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +66 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/lowContextHook.d.ts +71 -0
- package/dist/hooks/lowContextHook.d.ts.map +1 -0
- package/dist/hooks/lowContextHook.js +258 -0
- package/dist/hooks/lowContextHook.js.map +1 -0
- package/dist/hooks/simpleContextHook.d.ts +65 -0
- package/dist/hooks/simpleContextHook.d.ts.map +1 -0
- package/dist/hooks/simpleContextHook.js +194 -0
- package/dist/hooks/simpleContextHook.js.map +1 -0
- package/dist/hooks/teamFramingCli.d.ts +56 -0
- package/dist/hooks/teamFramingCli.d.ts.map +1 -0
- package/dist/hooks/teamFramingCli.js +264 -0
- package/dist/hooks/teamFramingCli.js.map +1 -0
- package/dist/hooks/teamMemberPrepromptHook.d.ts +150 -0
- package/dist/hooks/teamMemberPrepromptHook.d.ts.map +1 -0
- package/dist/hooks/teamMemberPrepromptHook.js +308 -0
- package/dist/hooks/teamMemberPrepromptHook.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4433 -0
- package/dist/index.js.map +1 -0
- package/dist/init/claudeConfigInjector.d.ts +116 -0
- package/dist/init/claudeConfigInjector.d.ts.map +1 -0
- package/dist/init/claudeConfigInjector.js +1154 -0
- package/dist/init/claudeConfigInjector.js.map +1 -0
- package/dist/installer/autoInstall.d.ts +72 -0
- package/dist/installer/autoInstall.d.ts.map +1 -0
- package/dist/installer/autoInstall.js +617 -0
- package/dist/installer/autoInstall.js.map +1 -0
- package/dist/installer/dbSetup.d.ts +84 -0
- package/dist/installer/dbSetup.d.ts.map +1 -0
- package/dist/installer/dbSetup.js +350 -0
- package/dist/installer/dbSetup.js.map +1 -0
- package/dist/installer/firstRun.d.ts +49 -0
- package/dist/installer/firstRun.d.ts.map +1 -0
- package/dist/installer/firstRun.js +207 -0
- package/dist/installer/firstRun.js.map +1 -0
- package/dist/installer/index.d.ts +10 -0
- package/dist/installer/index.d.ts.map +1 -0
- package/dist/installer/index.js +10 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/silentAutoInstall.d.ts +99 -0
- package/dist/installer/silentAutoInstall.d.ts.map +1 -0
- package/dist/installer/silentAutoInstall.js +491 -0
- package/dist/installer/silentAutoInstall.js.map +1 -0
- package/dist/installer/systemDeps.d.ts +54 -0
- package/dist/installer/systemDeps.d.ts.map +1 -0
- package/dist/installer/systemDeps.js +322 -0
- package/dist/installer/systemDeps.js.map +1 -0
- package/dist/mcp/cliNotifications.d.ts +133 -0
- package/dist/mcp/cliNotifications.d.ts.map +1 -0
- package/dist/mcp/cliNotifications.js +289 -0
- package/dist/mcp/cliNotifications.js.map +1 -0
- package/dist/mcp/embeddingServerManager.d.ts +307 -0
- package/dist/mcp/embeddingServerManager.d.ts.map +1 -0
- package/dist/mcp/embeddingServerManager.js +2081 -0
- package/dist/mcp/embeddingServerManager.js.map +1 -0
- package/dist/mcp/healthMonitor.d.ts +196 -0
- package/dist/mcp/healthMonitor.d.ts.map +1 -0
- package/dist/mcp/healthMonitor.js +685 -0
- package/dist/mcp/healthMonitor.js.map +1 -0
- package/dist/mcp/hotReloadManager.d.ts +101 -0
- package/dist/mcp/hotReloadManager.d.ts.map +1 -0
- package/dist/mcp/hotReloadManager.js +251 -0
- package/dist/mcp/hotReloadManager.js.map +1 -0
- package/dist/mcp/index.d.ts +16 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +22 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcpProtocolHandler.d.ts +64 -0
- package/dist/mcp/mcpProtocolHandler.d.ts.map +1 -0
- package/dist/mcp/mcpProtocolHandler.js +253 -0
- package/dist/mcp/mcpProtocolHandler.js.map +1 -0
- package/dist/mcp/miniCOTServerManager.d.ts +336 -0
- package/dist/mcp/miniCOTServerManager.d.ts.map +1 -0
- package/dist/mcp/miniCOTServerManager.js +1384 -0
- package/dist/mcp/miniCOTServerManager.js.map +1 -0
- package/dist/mcp/promptExecutor.d.ts +188 -0
- package/dist/mcp/promptExecutor.d.ts.map +1 -0
- package/dist/mcp/promptExecutor.js +469 -0
- package/dist/mcp/promptExecutor.js.map +1 -0
- package/dist/mcp/reloadBroadcast.d.ts +127 -0
- package/dist/mcp/reloadBroadcast.d.ts.map +1 -0
- package/dist/mcp/reloadBroadcast.js +275 -0
- package/dist/mcp/reloadBroadcast.js.map +1 -0
- package/dist/mcp/resilientTransport.d.ts +249 -0
- package/dist/mcp/resilientTransport.d.ts.map +1 -0
- package/dist/mcp/resilientTransport.js +931 -0
- package/dist/mcp/resilientTransport.js.map +1 -0
- package/dist/mcp/samplingHandler.d.ts +129 -0
- package/dist/mcp/samplingHandler.d.ts.map +1 -0
- package/dist/mcp/samplingHandler.js +276 -0
- package/dist/mcp/samplingHandler.js.map +1 -0
- package/dist/mcp/specMemServer.d.ts +305 -0
- package/dist/mcp/specMemServer.d.ts.map +1 -0
- package/dist/mcp/specMemServer.js +2048 -0
- package/dist/mcp/specMemServer.js.map +1 -0
- package/dist/mcp/toolRegistry.d.ts +122 -0
- package/dist/mcp/toolRegistry.d.ts.map +1 -0
- package/dist/mcp/toolRegistry.js +609 -0
- package/dist/mcp/toolRegistry.js.map +1 -0
- package/dist/mcp/tools/embeddingControl.d.ts +114 -0
- package/dist/mcp/tools/embeddingControl.d.ts.map +1 -0
- package/dist/mcp/tools/embeddingControl.js +222 -0
- package/dist/mcp/tools/embeddingControl.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +10 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +17 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/teamComms.d.ts +444 -0
- package/dist/mcp/tools/teamComms.d.ts.map +1 -0
- package/dist/mcp/tools/teamComms.js +1953 -0
- package/dist/mcp/tools/teamComms.js.map +1 -0
- package/dist/mcp/triggerSystem.d.ts +129 -0
- package/dist/mcp/triggerSystem.d.ts.map +1 -0
- package/dist/mcp/triggerSystem.js +363 -0
- package/dist/mcp/triggerSystem.js.map +1 -0
- package/dist/mcp/watcherIntegration.d.ts +77 -0
- package/dist/mcp/watcherIntegration.d.ts.map +1 -0
- package/dist/mcp/watcherIntegration.js +428 -0
- package/dist/mcp/watcherIntegration.js.map +1 -0
- package/dist/mcp/watcherToolWrappers.d.ts +89 -0
- package/dist/mcp/watcherToolWrappers.d.ts.map +1 -0
- package/dist/mcp/watcherToolWrappers.js +91 -0
- package/dist/mcp/watcherToolWrappers.js.map +1 -0
- package/dist/memorization/claudeCodeMigration.d.ts +34 -0
- package/dist/memorization/claudeCodeMigration.d.ts.map +1 -0
- package/dist/memorization/claudeCodeMigration.js +210 -0
- package/dist/memorization/claudeCodeMigration.js.map +1 -0
- package/dist/memorization/claudeCodeTracker.d.ts +147 -0
- package/dist/memorization/claudeCodeTracker.d.ts.map +1 -0
- package/dist/memorization/claudeCodeTracker.js +424 -0
- package/dist/memorization/claudeCodeTracker.js.map +1 -0
- package/dist/memorization/codeMemorizer.d.ts +158 -0
- package/dist/memorization/codeMemorizer.d.ts.map +1 -0
- package/dist/memorization/codeMemorizer.js +357 -0
- package/dist/memorization/codeMemorizer.js.map +1 -0
- package/dist/memorization/codeRecall.d.ts +156 -0
- package/dist/memorization/codeRecall.d.ts.map +1 -0
- package/dist/memorization/codeRecall.js +499 -0
- package/dist/memorization/codeRecall.js.map +1 -0
- package/dist/memorization/index.d.ts +55 -0
- package/dist/memorization/index.d.ts.map +1 -0
- package/dist/memorization/index.js +64 -0
- package/dist/memorization/index.js.map +1 -0
- package/dist/memorization/memorizationTools.d.ts +413 -0
- package/dist/memorization/memorizationTools.d.ts.map +1 -0
- package/dist/memorization/memorizationTools.js +513 -0
- package/dist/memorization/memorizationTools.js.map +1 -0
- package/dist/memorization/watcherIntegration.d.ts +100 -0
- package/dist/memorization/watcherIntegration.d.ts.map +1 -0
- package/dist/memorization/watcherIntegration.js +196 -0
- package/dist/memorization/watcherIntegration.js.map +1 -0
- package/dist/memory/humanLikeMemory.d.ts +206 -0
- package/dist/memory/humanLikeMemory.d.ts.map +1 -0
- package/dist/memory/humanLikeMemory.js +603 -0
- package/dist/memory/humanLikeMemory.js.map +1 -0
- package/dist/memory/index.d.ts +22 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +24 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memoryEvolutionMigration.d.ts +36 -0
- package/dist/memory/memoryEvolutionMigration.d.ts.map +1 -0
- package/dist/memory/memoryEvolutionMigration.js +371 -0
- package/dist/memory/memoryEvolutionMigration.js.map +1 -0
- package/dist/memory/quadrantSearch.d.ts +221 -0
- package/dist/memory/quadrantSearch.d.ts.map +1 -0
- package/dist/memory/quadrantSearch.js +897 -0
- package/dist/memory/quadrantSearch.js.map +1 -0
- package/dist/middleware/apiVersioning.d.ts +83 -0
- package/dist/middleware/apiVersioning.d.ts.map +1 -0
- package/dist/middleware/apiVersioning.js +152 -0
- package/dist/middleware/apiVersioning.js.map +1 -0
- package/dist/middleware/compression.d.ts +48 -0
- package/dist/middleware/compression.d.ts.map +1 -0
- package/dist/middleware/compression.js +240 -0
- package/dist/middleware/compression.js.map +1 -0
- package/dist/middleware/csrf.d.ts +118 -0
- package/dist/middleware/csrf.d.ts.map +1 -0
- package/dist/middleware/csrf.js +300 -0
- package/dist/middleware/csrf.js.map +1 -0
- package/dist/middleware/index.d.ts +13 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +17 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/wsRateLimiter.d.ts +129 -0
- package/dist/middleware/wsRateLimiter.d.ts.map +1 -0
- package/dist/middleware/wsRateLimiter.js +279 -0
- package/dist/middleware/wsRateLimiter.js.map +1 -0
- package/dist/migrations/run.d.ts +2 -0
- package/dist/migrations/run.d.ts.map +1 -0
- package/dist/migrations/run.js +25 -0
- package/dist/migrations/run.js.map +1 -0
- package/dist/migrations/syncDimensions.d.ts +24 -0
- package/dist/migrations/syncDimensions.d.ts.map +1 -0
- package/dist/migrations/syncDimensions.js +140 -0
- package/dist/migrations/syncDimensions.js.map +1 -0
- package/dist/migrations/teamComms.d.ts +16 -0
- package/dist/migrations/teamComms.d.ts.map +1 -0
- package/dist/migrations/teamComms.js +210 -0
- package/dist/migrations/teamComms.js.map +1 -0
- package/dist/openapi/index.d.ts +10 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +10 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/spec.d.ts +902 -0
- package/dist/openapi/spec.d.ts.map +1 -0
- package/dist/openapi/spec.js +733 -0
- package/dist/openapi/spec.js.map +1 -0
- package/dist/packages/dependencyHistory.d.ts +113 -0
- package/dist/packages/dependencyHistory.d.ts.map +1 -0
- package/dist/packages/dependencyHistory.js +360 -0
- package/dist/packages/dependencyHistory.js.map +1 -0
- package/dist/packages/index.d.ts +30 -0
- package/dist/packages/index.d.ts.map +1 -0
- package/dist/packages/index.js +65 -0
- package/dist/packages/index.js.map +1 -0
- package/dist/packages/packageTools.d.ts +255 -0
- package/dist/packages/packageTools.d.ts.map +1 -0
- package/dist/packages/packageTools.js +242 -0
- package/dist/packages/packageTools.js.map +1 -0
- package/dist/packages/packageTracker.d.ts +98 -0
- package/dist/packages/packageTracker.d.ts.map +1 -0
- package/dist/packages/packageTracker.js +268 -0
- package/dist/packages/packageTracker.js.map +1 -0
- package/dist/packages/packageWatcher.d.ts +62 -0
- package/dist/packages/packageWatcher.d.ts.map +1 -0
- package/dist/packages/packageWatcher.js +146 -0
- package/dist/packages/packageWatcher.js.map +1 -0
- package/dist/providers/MiniCOTProvider.d.ts +48 -0
- package/dist/providers/MiniCOTProvider.d.ts.map +1 -0
- package/dist/providers/MiniCOTProvider.js +98 -0
- package/dist/providers/MiniCOTProvider.js.map +1 -0
- package/dist/reminders/index.d.ts +5 -0
- package/dist/reminders/index.d.ts.map +1 -0
- package/dist/reminders/index.js +5 -0
- package/dist/reminders/index.js.map +1 -0
- package/dist/reminders/skillReminder.d.ts +131 -0
- package/dist/reminders/skillReminder.d.ts.map +1 -0
- package/dist/reminders/skillReminder.js +386 -0
- package/dist/reminders/skillReminder.js.map +1 -0
- package/dist/search.d.ts +35 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +574 -0
- package/dist/search.js.map +1 -0
- package/dist/security/localhostOnly.d.ts +36 -0
- package/dist/security/localhostOnly.d.ts.map +1 -0
- package/dist/security/localhostOnly.js +101 -0
- package/dist/security/localhostOnly.js.map +1 -0
- package/dist/services/CameraZoomSearch.d.ts +206 -0
- package/dist/services/CameraZoomSearch.d.ts.map +1 -0
- package/dist/services/CameraZoomSearch.js +669 -0
- package/dist/services/CameraZoomSearch.js.map +1 -0
- package/dist/services/DataFlowPipeline.d.ts +111 -0
- package/dist/services/DataFlowPipeline.d.ts.map +1 -0
- package/dist/services/DataFlowPipeline.js +379 -0
- package/dist/services/DataFlowPipeline.js.map +1 -0
- package/dist/services/DimensionAdapter.d.ts +194 -0
- package/dist/services/DimensionAdapter.d.ts.map +1 -0
- package/dist/services/DimensionAdapter.js +566 -0
- package/dist/services/DimensionAdapter.js.map +1 -0
- package/dist/services/DimensionService.d.ts +252 -0
- package/dist/services/DimensionService.d.ts.map +1 -0
- package/dist/services/DimensionService.js +564 -0
- package/dist/services/DimensionService.js.map +1 -0
- package/dist/services/EmbeddingQueue.d.ts +71 -0
- package/dist/services/EmbeddingQueue.d.ts.map +1 -0
- package/dist/services/EmbeddingQueue.js +258 -0
- package/dist/services/EmbeddingQueue.js.map +1 -0
- package/dist/services/MemoryDrilldown.d.ts +226 -0
- package/dist/services/MemoryDrilldown.d.ts.map +1 -0
- package/dist/services/MemoryDrilldown.js +479 -0
- package/dist/services/MemoryDrilldown.js.map +1 -0
- package/dist/services/MiniCOTScorer.d.ts +140 -0
- package/dist/services/MiniCOTScorer.d.ts.map +1 -0
- package/dist/services/MiniCOTScorer.js +292 -0
- package/dist/services/MiniCOTScorer.js.map +1 -0
- package/dist/services/ProjectContext.d.ts +342 -0
- package/dist/services/ProjectContext.d.ts.map +1 -0
- package/dist/services/ProjectContext.js +667 -0
- package/dist/services/ProjectContext.js.map +1 -0
- package/dist/services/ResponseCompactor.d.ts +135 -0
- package/dist/services/ResponseCompactor.d.ts.map +1 -0
- package/dist/services/ResponseCompactor.js +501 -0
- package/dist/services/ResponseCompactor.js.map +1 -0
- package/dist/services/TeamCommsDbService.d.ts +202 -0
- package/dist/services/TeamCommsDbService.d.ts.map +1 -0
- package/dist/services/TeamCommsDbService.js +526 -0
- package/dist/services/TeamCommsDbService.js.map +1 -0
- package/dist/services/UnifiedPasswordService.d.ts +166 -0
- package/dist/services/UnifiedPasswordService.d.ts.map +1 -0
- package/dist/services/UnifiedPasswordService.js +587 -0
- package/dist/services/UnifiedPasswordService.js.map +1 -0
- package/dist/services/adaptiveSearchConfig.d.ts +64 -0
- package/dist/services/adaptiveSearchConfig.d.ts.map +1 -0
- package/dist/services/adaptiveSearchConfig.js +187 -0
- package/dist/services/adaptiveSearchConfig.js.map +1 -0
- package/dist/skills/index.d.ts +8 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +8 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/skillScanner.d.ts +203 -0
- package/dist/skills/skillScanner.d.ts.map +1 -0
- package/dist/skills/skillScanner.js +559 -0
- package/dist/skills/skillScanner.js.map +1 -0
- package/dist/skills/skillsResource.d.ts +69 -0
- package/dist/skills/skillsResource.d.ts.map +1 -0
- package/dist/skills/skillsResource.js +257 -0
- package/dist/skills/skillsResource.js.map +1 -0
- package/dist/startup/index.d.ts +9 -0
- package/dist/startup/index.d.ts.map +1 -0
- package/dist/startup/index.js +12 -0
- package/dist/startup/index.js.map +1 -0
- package/dist/startup/startupIndexing.d.ts +80 -0
- package/dist/startup/startupIndexing.d.ts.map +1 -0
- package/dist/startup/startupIndexing.js +463 -0
- package/dist/startup/startupIndexing.js.map +1 -0
- package/dist/startup/validation.d.ts +89 -0
- package/dist/startup/validation.d.ts.map +1 -0
- package/dist/startup/validation.js +590 -0
- package/dist/startup/validation.js.map +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +4 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/overflowManager.d.ts +80 -0
- package/dist/storage/overflowManager.d.ts.map +1 -0
- package/dist/storage/overflowManager.js +317 -0
- package/dist/storage/overflowManager.js.map +1 -0
- package/dist/storage/overflowStorage.d.ts +69 -0
- package/dist/storage/overflowStorage.d.ts.map +1 -0
- package/dist/storage/overflowStorage.js +379 -0
- package/dist/storage/overflowStorage.js.map +1 -0
- package/dist/storage/toonFormat.d.ts +50 -0
- package/dist/storage/toonFormat.d.ts.map +1 -0
- package/dist/storage/toonFormat.js +224 -0
- package/dist/storage/toonFormat.js.map +1 -0
- package/dist/team-members/communication.d.ts +237 -0
- package/dist/team-members/communication.d.ts.map +1 -0
- package/dist/team-members/communication.js +650 -0
- package/dist/team-members/communication.js.map +1 -0
- package/dist/team-members/index.d.ts +14 -0
- package/dist/team-members/index.d.ts.map +1 -0
- package/dist/team-members/index.js +22 -0
- package/dist/team-members/index.js.map +1 -0
- package/dist/team-members/taskOrchestrator.d.ts +224 -0
- package/dist/team-members/taskOrchestrator.d.ts.map +1 -0
- package/dist/team-members/taskOrchestrator.js +574 -0
- package/dist/team-members/taskOrchestrator.js.map +1 -0
- package/dist/team-members/taskTeamMemberLogger.d.ts +157 -0
- package/dist/team-members/taskTeamMemberLogger.d.ts.map +1 -0
- package/dist/team-members/taskTeamMemberLogger.js +478 -0
- package/dist/team-members/taskTeamMemberLogger.js.map +1 -0
- package/dist/team-members/teamCommsService.d.ts +221 -0
- package/dist/team-members/teamCommsService.d.ts.map +1 -0
- package/dist/team-members/teamCommsService.js +628 -0
- package/dist/team-members/teamCommsService.js.map +1 -0
- package/dist/team-members/teamMemberChannels.d.ts +217 -0
- package/dist/team-members/teamMemberChannels.d.ts.map +1 -0
- package/dist/team-members/teamMemberChannels.js +687 -0
- package/dist/team-members/teamMemberChannels.js.map +1 -0
- package/dist/team-members/teamMemberDashboard.d.ts +222 -0
- package/dist/team-members/teamMemberDashboard.d.ts.map +1 -0
- package/dist/team-members/teamMemberDashboard.js +610 -0
- package/dist/team-members/teamMemberDashboard.js.map +1 -0
- package/dist/team-members/teamMemberDeployment.d.ts +60 -0
- package/dist/team-members/teamMemberDeployment.d.ts.map +1 -0
- package/dist/team-members/teamMemberDeployment.js +429 -0
- package/dist/team-members/teamMemberDeployment.js.map +1 -0
- package/dist/team-members/teamMemberDiscovery.d.ts +178 -0
- package/dist/team-members/teamMemberDiscovery.d.ts.map +1 -0
- package/dist/team-members/teamMemberDiscovery.js +446 -0
- package/dist/team-members/teamMemberDiscovery.js.map +1 -0
- package/dist/team-members/teamMemberHistory.d.ts +80 -0
- package/dist/team-members/teamMemberHistory.d.ts.map +1 -0
- package/dist/team-members/teamMemberHistory.js +426 -0
- package/dist/team-members/teamMemberHistory.js.map +1 -0
- package/dist/team-members/teamMemberLimits.d.ts +66 -0
- package/dist/team-members/teamMemberLimits.d.ts.map +1 -0
- package/dist/team-members/teamMemberLimits.js +259 -0
- package/dist/team-members/teamMemberLimits.js.map +1 -0
- package/dist/team-members/teamMemberRegistry.d.ts +199 -0
- package/dist/team-members/teamMemberRegistry.d.ts.map +1 -0
- package/dist/team-members/teamMemberRegistry.js +572 -0
- package/dist/team-members/teamMemberRegistry.js.map +1 -0
- package/dist/team-members/teamMemberTracker.d.ts +148 -0
- package/dist/team-members/teamMemberTracker.d.ts.map +1 -0
- package/dist/team-members/teamMemberTracker.js +828 -0
- package/dist/team-members/teamMemberTracker.js.map +1 -0
- package/dist/team-members/workers/aiWorker.d.ts +53 -0
- package/dist/team-members/workers/aiWorker.d.ts.map +1 -0
- package/dist/team-members/workers/aiWorker.js +322 -0
- package/dist/team-members/workers/aiWorker.js.map +1 -0
- package/dist/team-members/workers/baseWorker.d.ts +101 -0
- package/dist/team-members/workers/baseWorker.d.ts.map +1 -0
- package/dist/team-members/workers/baseWorker.js +179 -0
- package/dist/team-members/workers/baseWorker.js.map +1 -0
- package/dist/team-members/workers/codeReviewWorker.d.ts +3 -0
- package/dist/team-members/workers/codeReviewWorker.d.ts.map +1 -0
- package/dist/team-members/workers/codeReviewWorker.js +144 -0
- package/dist/team-members/workers/codeReviewWorker.js.map +1 -0
- package/dist/team-members/workers/index.d.ts +7 -0
- package/dist/team-members/workers/index.d.ts.map +1 -0
- package/dist/team-members/workers/index.js +7 -0
- package/dist/team-members/workers/index.js.map +1 -0
- package/dist/team-members/workers/repairWorker.d.ts +9 -0
- package/dist/team-members/workers/repairWorker.d.ts.map +1 -0
- package/dist/team-members/workers/repairWorker.js +102 -0
- package/dist/team-members/workers/repairWorker.js.map +1 -0
- package/dist/team-members/workers/sendToTeamMemberB.d.ts +9 -0
- package/dist/team-members/workers/sendToTeamMemberB.d.ts.map +1 -0
- package/dist/team-members/workers/sendToTeamMemberB.js +105 -0
- package/dist/team-members/workers/sendToTeamMemberB.js.map +1 -0
- package/dist/team-members/workers/specmemClient.d.ts +179 -0
- package/dist/team-members/workers/specmemClient.d.ts.map +1 -0
- package/dist/team-members/workers/specmemClient.js +421 -0
- package/dist/team-members/workers/specmemClient.js.map +1 -0
- package/dist/team-members/workers/testCommunication.d.ts +8 -0
- package/dist/team-members/workers/testCommunication.d.ts.map +1 -0
- package/dist/team-members/workers/testCommunication.js +136 -0
- package/dist/team-members/workers/testCommunication.js.map +1 -0
- package/dist/team-members/workers/testCommunicationSuite.d.ts +26 -0
- package/dist/team-members/workers/testCommunicationSuite.d.ts.map +1 -0
- package/dist/team-members/workers/testCommunicationSuite.js +415 -0
- package/dist/team-members/workers/testCommunicationSuite.js.map +1 -0
- package/dist/team-members/workers/testWorker.d.ts +9 -0
- package/dist/team-members/workers/testWorker.d.ts.map +1 -0
- package/dist/team-members/workers/testWorker.js +107 -0
- package/dist/team-members/workers/testWorker.js.map +1 -0
- package/dist/tools/agentDefinitions.d.ts +30 -0
- package/dist/tools/agentDefinitions.d.ts.map +1 -0
- package/dist/tools/agentDefinitions.js +166 -0
- package/dist/tools/agentDefinitions.js.map +1 -0
- package/dist/tools/goofy/checkSyncStatus.d.ts +68 -0
- package/dist/tools/goofy/checkSyncStatus.d.ts.map +1 -0
- package/dist/tools/goofy/checkSyncStatus.js +112 -0
- package/dist/tools/goofy/checkSyncStatus.js.map +1 -0
- package/dist/tools/goofy/codeMemoryLink.d.ts +82 -0
- package/dist/tools/goofy/codeMemoryLink.d.ts.map +1 -0
- package/dist/tools/goofy/codeMemoryLink.js +212 -0
- package/dist/tools/goofy/codeMemoryLink.js.map +1 -0
- package/dist/tools/goofy/compareInstanceMemory.d.ts +97 -0
- package/dist/tools/goofy/compareInstanceMemory.d.ts.map +1 -0
- package/dist/tools/goofy/compareInstanceMemory.js +218 -0
- package/dist/tools/goofy/compareInstanceMemory.js.map +1 -0
- package/dist/tools/goofy/createReasoningChain.d.ts +135 -0
- package/dist/tools/goofy/createReasoningChain.d.ts.map +1 -0
- package/dist/tools/goofy/createReasoningChain.js +257 -0
- package/dist/tools/goofy/createReasoningChain.js.map +1 -0
- package/dist/tools/goofy/deployTeamMember.d.ts +63 -0
- package/dist/tools/goofy/deployTeamMember.d.ts.map +1 -0
- package/dist/tools/goofy/deployTeamMember.js +103 -0
- package/dist/tools/goofy/deployTeamMember.js.map +1 -0
- package/dist/tools/goofy/drillDown.d.ts +143 -0
- package/dist/tools/goofy/drillDown.d.ts.map +1 -0
- package/dist/tools/goofy/drillDown.js +288 -0
- package/dist/tools/goofy/drillDown.js.map +1 -0
- package/dist/tools/goofy/extractClaudeSessions.d.ts +90 -0
- package/dist/tools/goofy/extractClaudeSessions.d.ts.map +1 -0
- package/dist/tools/goofy/extractClaudeSessions.js +277 -0
- package/dist/tools/goofy/extractClaudeSessions.js.map +1 -0
- package/dist/tools/goofy/extractContextRestorations.d.ts +70 -0
- package/dist/tools/goofy/extractContextRestorations.d.ts.map +1 -0
- package/dist/tools/goofy/extractContextRestorations.js +100 -0
- package/dist/tools/goofy/extractContextRestorations.js.map +1 -0
- package/dist/tools/goofy/findCodePointers.d.ts +364 -0
- package/dist/tools/goofy/findCodePointers.d.ts.map +1 -0
- package/dist/tools/goofy/findCodePointers.js +1764 -0
- package/dist/tools/goofy/findCodePointers.js.map +1 -0
- package/dist/tools/goofy/findMemoryGallery.d.ts +40 -0
- package/dist/tools/goofy/findMemoryGallery.d.ts.map +1 -0
- package/dist/tools/goofy/findMemoryGallery.js +66 -0
- package/dist/tools/goofy/findMemoryGallery.js.map +1 -0
- package/dist/tools/goofy/findWhatISaid.d.ts +300 -0
- package/dist/tools/goofy/findWhatISaid.d.ts.map +1 -0
- package/dist/tools/goofy/findWhatISaid.js +2547 -0
- package/dist/tools/goofy/findWhatISaid.js.map +1 -0
- package/dist/tools/goofy/forceResync.d.ts +57 -0
- package/dist/tools/goofy/forceResync.d.ts.map +1 -0
- package/dist/tools/goofy/forceResync.js +100 -0
- package/dist/tools/goofy/forceResync.js.map +1 -0
- package/dist/tools/goofy/getActiveTeamMembers.d.ts +48 -0
- package/dist/tools/goofy/getActiveTeamMembers.d.ts.map +1 -0
- package/dist/tools/goofy/getActiveTeamMembers.js +136 -0
- package/dist/tools/goofy/getActiveTeamMembers.js.map +1 -0
- package/dist/tools/goofy/getMemoryFull.d.ts +34 -0
- package/dist/tools/goofy/getMemoryFull.d.ts.map +1 -0
- package/dist/tools/goofy/getMemoryFull.js +58 -0
- package/dist/tools/goofy/getMemoryFull.js.map +1 -0
- package/dist/tools/goofy/getSessionWatcherStatus.d.ts +43 -0
- package/dist/tools/goofy/getSessionWatcherStatus.d.ts.map +1 -0
- package/dist/tools/goofy/getSessionWatcherStatus.js +92 -0
- package/dist/tools/goofy/getSessionWatcherStatus.js.map +1 -0
- package/dist/tools/goofy/getTeamMemberOutput.d.ts +35 -0
- package/dist/tools/goofy/getTeamMemberOutput.d.ts.map +1 -0
- package/dist/tools/goofy/getTeamMemberOutput.js +62 -0
- package/dist/tools/goofy/getTeamMemberOutput.js.map +1 -0
- package/dist/tools/goofy/getTeamMemberScreen.d.ts +28 -0
- package/dist/tools/goofy/getTeamMemberScreen.d.ts.map +1 -0
- package/dist/tools/goofy/getTeamMemberScreen.js +59 -0
- package/dist/tools/goofy/getTeamMemberScreen.js.map +1 -0
- package/dist/tools/goofy/getTeamMemberStatus.d.ts +33 -0
- package/dist/tools/goofy/getTeamMemberStatus.d.ts.map +1 -0
- package/dist/tools/goofy/getTeamMemberStatus.js +56 -0
- package/dist/tools/goofy/getTeamMemberStatus.js.map +1 -0
- package/dist/tools/goofy/index.d.ts +39 -0
- package/dist/tools/goofy/index.d.ts.map +1 -0
- package/dist/tools/goofy/index.js +51 -0
- package/dist/tools/goofy/index.js.map +1 -0
- package/dist/tools/goofy/interveneTeamMember.d.ts +33 -0
- package/dist/tools/goofy/interveneTeamMember.d.ts.map +1 -0
- package/dist/tools/goofy/interveneTeamMember.js +69 -0
- package/dist/tools/goofy/interveneTeamMember.js.map +1 -0
- package/dist/tools/goofy/killDeployedTeamMember.d.ts +29 -0
- package/dist/tools/goofy/killDeployedTeamMember.d.ts.map +1 -0
- package/dist/tools/goofy/killDeployedTeamMember.js +56 -0
- package/dist/tools/goofy/killDeployedTeamMember.js.map +1 -0
- package/dist/tools/goofy/linkTheVibes.d.ts +125 -0
- package/dist/tools/goofy/linkTheVibes.d.ts.map +1 -0
- package/dist/tools/goofy/linkTheVibes.js +354 -0
- package/dist/tools/goofy/linkTheVibes.js.map +1 -0
- package/dist/tools/goofy/listDeployedTeamMembers.d.ts +26 -0
- package/dist/tools/goofy/listDeployedTeamMembers.d.ts.map +1 -0
- package/dist/tools/goofy/listDeployedTeamMembers.js +52 -0
- package/dist/tools/goofy/listDeployedTeamMembers.js.map +1 -0
- package/dist/tools/goofy/listenForMessages.d.ts +56 -0
- package/dist/tools/goofy/listenForMessages.d.ts.map +1 -0
- package/dist/tools/goofy/listenForMessages.js +122 -0
- package/dist/tools/goofy/listenForMessages.js.map +1 -0
- package/dist/tools/goofy/memoryHealthCheck.d.ts +159 -0
- package/dist/tools/goofy/memoryHealthCheck.d.ts.map +1 -0
- package/dist/tools/goofy/memoryHealthCheck.js +443 -0
- package/dist/tools/goofy/memoryHealthCheck.js.map +1 -0
- package/dist/tools/goofy/rememberThisShit.d.ts +103 -0
- package/dist/tools/goofy/rememberThisShit.d.ts.map +1 -0
- package/dist/tools/goofy/rememberThisShit.js +291 -0
- package/dist/tools/goofy/rememberThisShit.js.map +1 -0
- package/dist/tools/goofy/sayToTeamMember.d.ts +55 -0
- package/dist/tools/goofy/sayToTeamMember.d.ts.map +1 -0
- package/dist/tools/goofy/sayToTeamMember.js +116 -0
- package/dist/tools/goofy/sayToTeamMember.js.map +1 -0
- package/dist/tools/goofy/selfMessage.d.ts +54 -0
- package/dist/tools/goofy/selfMessage.d.ts.map +1 -0
- package/dist/tools/goofy/selfMessage.js +111 -0
- package/dist/tools/goofy/selfMessage.js.map +1 -0
- package/dist/tools/goofy/sendHeartbeat.d.ts +53 -0
- package/dist/tools/goofy/sendHeartbeat.d.ts.map +1 -0
- package/dist/tools/goofy/sendHeartbeat.js +119 -0
- package/dist/tools/goofy/sendHeartbeat.js.map +1 -0
- package/dist/tools/goofy/showMeTheStats.d.ts +216 -0
- package/dist/tools/goofy/showMeTheStats.d.ts.map +1 -0
- package/dist/tools/goofy/showMeTheStats.js +535 -0
- package/dist/tools/goofy/showMeTheStats.js.map +1 -0
- package/dist/tools/goofy/smartRecall.d.ts +136 -0
- package/dist/tools/goofy/smartRecall.d.ts.map +1 -0
- package/dist/tools/goofy/smartRecall.js +286 -0
- package/dist/tools/goofy/smartRecall.js.map +1 -0
- package/dist/tools/goofy/smartSearch.d.ts +64 -0
- package/dist/tools/goofy/smartSearch.d.ts.map +1 -0
- package/dist/tools/goofy/smartSearch.js +89 -0
- package/dist/tools/goofy/smartSearch.js.map +1 -0
- package/dist/tools/goofy/smushMemoriesTogether.d.ts +128 -0
- package/dist/tools/goofy/smushMemoriesTogether.d.ts.map +1 -0
- package/dist/tools/goofy/smushMemoriesTogether.js +536 -0
- package/dist/tools/goofy/smushMemoriesTogether.js.map +1 -0
- package/dist/tools/goofy/spatialSearch.d.ts +198 -0
- package/dist/tools/goofy/spatialSearch.d.ts.map +1 -0
- package/dist/tools/goofy/spatialSearch.js +551 -0
- package/dist/tools/goofy/spatialSearch.js.map +1 -0
- package/dist/tools/goofy/spawnResearchTeamMember.d.ts +104 -0
- package/dist/tools/goofy/spawnResearchTeamMember.d.ts.map +1 -0
- package/dist/tools/goofy/spawnResearchTeamMember.js +290 -0
- package/dist/tools/goofy/spawnResearchTeamMember.js.map +1 -0
- package/dist/tools/goofy/spawnResearchTeamMemberTool.d.ts +121 -0
- package/dist/tools/goofy/spawnResearchTeamMemberTool.d.ts.map +1 -0
- package/dist/tools/goofy/spawnResearchTeamMemberTool.js +215 -0
- package/dist/tools/goofy/spawnResearchTeamMemberTool.js.map +1 -0
- package/dist/tools/goofy/startWatchingTheFiles.d.ts +81 -0
- package/dist/tools/goofy/startWatchingTheFiles.d.ts.map +1 -0
- package/dist/tools/goofy/startWatchingTheFiles.js +161 -0
- package/dist/tools/goofy/startWatchingTheFiles.js.map +1 -0
- package/dist/tools/goofy/stopWatchingTheFiles.d.ts +50 -0
- package/dist/tools/goofy/stopWatchingTheFiles.d.ts.map +1 -0
- package/dist/tools/goofy/stopWatchingTheFiles.js +81 -0
- package/dist/tools/goofy/stopWatchingTheFiles.js.map +1 -0
- package/dist/tools/goofy/whatDidIMean.d.ts +113 -0
- package/dist/tools/goofy/whatDidIMean.d.ts.map +1 -0
- package/dist/tools/goofy/whatDidIMean.js +401 -0
- package/dist/tools/goofy/whatDidIMean.js.map +1 -0
- package/dist/tools/goofy/yeahNahDeleteThat.d.ts +109 -0
- package/dist/tools/goofy/yeahNahDeleteThat.d.ts.map +1 -0
- package/dist/tools/goofy/yeahNahDeleteThat.js +319 -0
- package/dist/tools/goofy/yeahNahDeleteThat.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/teamMemberDeployer.d.ts +117 -0
- package/dist/tools/teamMemberDeployer.d.ts.map +1 -0
- package/dist/tools/teamMemberDeployer.js +613 -0
- package/dist/tools/teamMemberDeployer.js.map +1 -0
- package/dist/trace/index.d.ts +14 -0
- package/dist/trace/index.d.ts.map +1 -0
- package/dist/trace/index.js +16 -0
- package/dist/trace/index.js.map +1 -0
- package/dist/trace/tools/analyzeImpact.d.ts +90 -0
- package/dist/trace/tools/analyzeImpact.d.ts.map +1 -0
- package/dist/trace/tools/analyzeImpact.js +240 -0
- package/dist/trace/tools/analyzeImpact.js.map +1 -0
- package/dist/trace/tools/exploreDependencies.d.ts +81 -0
- package/dist/trace/tools/exploreDependencies.d.ts.map +1 -0
- package/dist/trace/tools/exploreDependencies.js +161 -0
- package/dist/trace/tools/exploreDependencies.js.map +1 -0
- package/dist/trace/tools/findSimilarBugs.d.ts +112 -0
- package/dist/trace/tools/findSimilarBugs.d.ts.map +1 -0
- package/dist/trace/tools/findSimilarBugs.js +216 -0
- package/dist/trace/tools/findSimilarBugs.js.map +1 -0
- package/dist/trace/tools/index.d.ts +22 -0
- package/dist/trace/tools/index.d.ts.map +1 -0
- package/dist/trace/tools/index.js +39 -0
- package/dist/trace/tools/index.js.map +1 -0
- package/dist/trace/tools/smartExplore.d.ts +126 -0
- package/dist/trace/tools/smartExplore.d.ts.map +1 -0
- package/dist/trace/tools/smartExplore.js +303 -0
- package/dist/trace/tools/smartExplore.js.map +1 -0
- package/dist/trace/tools/traceError.d.ts +101 -0
- package/dist/trace/tools/traceError.d.ts.map +1 -0
- package/dist/trace/tools/traceError.js +175 -0
- package/dist/trace/tools/traceError.js.map +1 -0
- package/dist/trace/traceExploreSystem.d.ts +271 -0
- package/dist/trace/traceExploreSystem.d.ts.map +1 -0
- package/dist/trace/traceExploreSystem.js +789 -0
- package/dist/trace/traceExploreSystem.js.map +1 -0
- package/dist/types/index.d.ts +421 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +118 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/circuitBreaker.d.ts +195 -0
- package/dist/utils/circuitBreaker.d.ts.map +1 -0
- package/dist/utils/circuitBreaker.js +374 -0
- package/dist/utils/circuitBreaker.js.map +1 -0
- package/dist/utils/cleanupHandler.d.ts +108 -0
- package/dist/utils/cleanupHandler.d.ts.map +1 -0
- package/dist/utils/cleanupHandler.js +203 -0
- package/dist/utils/cleanupHandler.js.map +1 -0
- package/dist/utils/compactXmlResponse.d.ts +60 -0
- package/dist/utils/compactXmlResponse.d.ts.map +1 -0
- package/dist/utils/compactXmlResponse.js +209 -0
- package/dist/utils/compactXmlResponse.js.map +1 -0
- package/dist/utils/cotBroadcast.d.ts +56 -0
- package/dist/utils/cotBroadcast.d.ts.map +1 -0
- package/dist/utils/cotBroadcast.js +157 -0
- package/dist/utils/cotBroadcast.js.map +1 -0
- package/dist/utils/debugLogger.d.ts +95 -0
- package/dist/utils/debugLogger.d.ts.map +1 -0
- package/dist/utils/debugLogger.js +610 -0
- package/dist/utils/debugLogger.js.map +1 -0
- package/dist/utils/fileProcessingQueue.d.ts +259 -0
- package/dist/utils/fileProcessingQueue.d.ts.map +1 -0
- package/dist/utils/fileProcessingQueue.js +714 -0
- package/dist/utils/fileProcessingQueue.js.map +1 -0
- package/dist/utils/humanReadableOutput.d.ts +124 -0
- package/dist/utils/humanReadableOutput.d.ts.map +1 -0
- package/dist/utils/humanReadableOutput.js +340 -0
- package/dist/utils/humanReadableOutput.js.map +1 -0
- package/dist/utils/index.d.ts +32 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +71 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/instanceManager.d.ts +530 -0
- package/dist/utils/instanceManager.d.ts.map +1 -0
- package/dist/utils/instanceManager.js +1784 -0
- package/dist/utils/instanceManager.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +49 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mapCleanup.d.ts +58 -0
- package/dist/utils/mapCleanup.d.ts.map +1 -0
- package/dist/utils/mapCleanup.js +150 -0
- package/dist/utils/mapCleanup.js.map +1 -0
- package/dist/utils/memoryManager.d.ts +349 -0
- package/dist/utils/memoryManager.d.ts.map +1 -0
- package/dist/utils/memoryManager.js +799 -0
- package/dist/utils/memoryManager.js.map +1 -0
- package/dist/utils/metrics.d.ts +160 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +558 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/pathValidator.d.ts +96 -0
- package/dist/utils/pathValidator.d.ts.map +1 -0
- package/dist/utils/pathValidator.js +320 -0
- package/dist/utils/pathValidator.js.map +1 -0
- package/dist/utils/portAllocator.d.ts +296 -0
- package/dist/utils/portAllocator.d.ts.map +1 -0
- package/dist/utils/portAllocator.js +768 -0
- package/dist/utils/portAllocator.js.map +1 -0
- package/dist/utils/portUtils.d.ts +97 -0
- package/dist/utils/portUtils.d.ts.map +1 -0
- package/dist/utils/portUtils.js +285 -0
- package/dist/utils/portUtils.js.map +1 -0
- package/dist/utils/postgresAutoSetup.d.ts +55 -0
- package/dist/utils/postgresAutoSetup.d.ts.map +1 -0
- package/dist/utils/postgresAutoSetup.js +406 -0
- package/dist/utils/postgresAutoSetup.js.map +1 -0
- package/dist/utils/processHealthCheck.d.ts +61 -0
- package/dist/utils/processHealthCheck.d.ts.map +1 -0
- package/dist/utils/processHealthCheck.js +313 -0
- package/dist/utils/processHealthCheck.js.map +1 -0
- package/dist/utils/progressReporter.d.ts +151 -0
- package/dist/utils/progressReporter.d.ts.map +1 -0
- package/dist/utils/progressReporter.js +345 -0
- package/dist/utils/progressReporter.js.map +1 -0
- package/dist/utils/projectEnv.d.ts +73 -0
- package/dist/utils/projectEnv.d.ts.map +1 -0
- package/dist/utils/projectEnv.js +137 -0
- package/dist/utils/projectEnv.js.map +1 -0
- package/dist/utils/qoms.d.ts +122 -0
- package/dist/utils/qoms.d.ts.map +1 -0
- package/dist/utils/qoms.js +650 -0
- package/dist/utils/qoms.js.map +1 -0
- package/dist/utils/retryHelper.d.ts +122 -0
- package/dist/utils/retryHelper.d.ts.map +1 -0
- package/dist/utils/retryHelper.js +272 -0
- package/dist/utils/retryHelper.js.map +1 -0
- package/dist/utils/safeProcessTermination.d.ts +206 -0
- package/dist/utils/safeProcessTermination.d.ts.map +1 -0
- package/dist/utils/safeProcessTermination.js +552 -0
- package/dist/utils/safeProcessTermination.js.map +1 -0
- package/dist/utils/sessionInjector.d.ts +68 -0
- package/dist/utils/sessionInjector.d.ts.map +1 -0
- package/dist/utils/sessionInjector.js +189 -0
- package/dist/utils/sessionInjector.js.map +1 -0
- package/dist/utils/statsCache.d.ts +134 -0
- package/dist/utils/statsCache.d.ts.map +1 -0
- package/dist/utils/statsCache.js +285 -0
- package/dist/utils/statsCache.js.map +1 -0
- package/dist/utils/timeoutMiddleware.d.ts +81 -0
- package/dist/utils/timeoutMiddleware.d.ts.map +1 -0
- package/dist/utils/timeoutMiddleware.js +155 -0
- package/dist/utils/timeoutMiddleware.js.map +1 -0
- package/dist/utils/timerRegistry.d.ts +91 -0
- package/dist/utils/timerRegistry.d.ts.map +1 -0
- package/dist/utils/timerRegistry.js +187 -0
- package/dist/utils/timerRegistry.js.map +1 -0
- package/dist/utils/tokenCompressor.d.ts +332 -0
- package/dist/utils/tokenCompressor.d.ts.map +1 -0
- package/dist/utils/tokenCompressor.js +1306 -0
- package/dist/utils/tokenCompressor.js.map +1 -0
- package/dist/utils/tracing.d.ts +236 -0
- package/dist/utils/tracing.d.ts.map +1 -0
- package/dist/utils/tracing.js +378 -0
- package/dist/utils/tracing.js.map +1 -0
- package/dist/watcher/changeHandler.d.ts +123 -0
- package/dist/watcher/changeHandler.d.ts.map +1 -0
- package/dist/watcher/changeHandler.js +623 -0
- package/dist/watcher/changeHandler.js.map +1 -0
- package/dist/watcher/changeQueue.d.ts +133 -0
- package/dist/watcher/changeQueue.d.ts.map +1 -0
- package/dist/watcher/changeQueue.js +355 -0
- package/dist/watcher/changeQueue.js.map +1 -0
- package/dist/watcher/fileWatcher.d.ts +121 -0
- package/dist/watcher/fileWatcher.d.ts.map +1 -0
- package/dist/watcher/fileWatcher.js +531 -0
- package/dist/watcher/fileWatcher.js.map +1 -0
- package/dist/watcher/index.d.ts +94 -0
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +235 -0
- package/dist/watcher/index.js.map +1 -0
- package/dist/watcher/syncChecker.d.ts +93 -0
- package/dist/watcher/syncChecker.d.ts.map +1 -0
- package/dist/watcher/syncChecker.js +401 -0
- package/dist/watcher/syncChecker.js.map +1 -0
- package/dist/watcher/tsCompiler.d.ts +88 -0
- package/dist/watcher/tsCompiler.d.ts.map +1 -0
- package/dist/watcher/tsCompiler.js +212 -0
- package/dist/watcher/tsCompiler.js.map +1 -0
- package/embedding-sandbox/Dockerfile +77 -0
- package/embedding-sandbox/Dockerfile.frankenstein +91 -0
- package/embedding-sandbox/README.md +193 -0
- package/embedding-sandbox/__pycache__/frankenstein-embeddings.cpython-312.pyc +0 -0
- package/embedding-sandbox/__pycache__/frankenstein-embeddings.cpython-313.pyc +0 -0
- package/embedding-sandbox/__pycache__/qqms_v2.cpython-312.pyc +0 -0
- package/embedding-sandbox/__pycache__/qqms_v2.cpython-313.pyc +0 -0
- package/embedding-sandbox/add_js_docs.py +684 -0
- package/embedding-sandbox/build_docs_db.py +239 -0
- package/embedding-sandbox/client.cjs +376 -0
- package/embedding-sandbox/client.ts +913 -0
- package/embedding-sandbox/deploy-frankenstein.sh +240 -0
- package/embedding-sandbox/docker-compose.yml +60 -0
- package/embedding-sandbox/docker-manager.py +325 -0
- package/embedding-sandbox/docs/python_docs.db +0 -0
- package/embedding-sandbox/download-model.mjs +79 -0
- package/embedding-sandbox/download-model.py +28 -0
- package/embedding-sandbox/embedding-supervisor.sh +164 -0
- package/embedding-sandbox/frankenstein-embeddings.py +3940 -0
- package/embedding-sandbox/manage-services.sh +354 -0
- package/embedding-sandbox/overflow_queue.py +345 -0
- package/embedding-sandbox/package.json +17 -0
- package/embedding-sandbox/project_isolation.py +292 -0
- package/embedding-sandbox/qqms_v2.py +967 -0
- package/embedding-sandbox/ram-manager.sh +311 -0
- package/embedding-sandbox/requirements-frankenstein.txt +7 -0
- package/embedding-sandbox/run_js_docs.py +59 -0
- package/embedding-sandbox/seed_docs.py +885 -0
- package/embedding-sandbox/server-batch.mjs +228 -0
- package/embedding-sandbox/server.mjs +389 -0
- package/embedding-sandbox/specmem/sockets/claude-input-state.json +1 -0
- package/embedding-sandbox/specmem/sockets/embedding-death-reason.txt +3 -0
- package/embedding-sandbox/specmem/sockets/seen-sessions.json +1 -0
- package/embedding-sandbox/specmem/sockets/session-start.lock +1 -0
- package/embedding-sandbox/specmem/sockets/session-stops.log +7 -0
- package/embedding-sandbox/start-frankenstein-throttled.sh +98 -0
- package/embedding-sandbox/start-on-demand.sh +116 -0
- package/embedding-sandbox/start-sandbox.sh +237 -0
- package/embedding-sandbox/start-supervised.sh +11 -0
- package/embedding-sandbox/stop-sandbox.sh +51 -0
- package/embedding-sandbox/test-socket.mjs +61 -0
- package/embedding-sandbox/warm-start.sh +353 -0
- package/embedding-sandbox/warm_start_feeder.py +660 -0
- package/legal/README.md +31 -0
- package/legal/anthropic-privacy-center-screenshot-2026-01-30.png +0 -0
- package/legal/anthropic-tos-screenshot-2026-01-30.png +0 -0
- package/lib/codebase-bridge.cjs +308 -0
- package/package.json +136 -0
- package/plugins/specmem-agents/agents/bug-hunter.md +79 -0
- package/plugins/specmem-agents/agents/memory-explorer.md +57 -0
- package/plugins/specmem-agents/agents/team-coordinator.md +82 -0
- package/scripts/auto-updater.cjs +399 -0
- package/scripts/backfill-code-definition-embeddings.ts +440 -0
- package/scripts/backfill-code-embeddings.ts +206 -0
- package/scripts/capture-tos-screenshots.cjs +94 -0
- package/scripts/check-global-install.cjs +67 -0
- package/scripts/cleanup-embedding-servers.sh +25 -0
- package/scripts/dashboard-standalone.sh +369 -0
- package/scripts/deploy-hooks.cjs +1451 -0
- package/scripts/deploy.sh +106 -0
- package/scripts/docker-project-down.sh +83 -0
- package/scripts/docker-project-list.sh +40 -0
- package/scripts/docker-project-up.sh +79 -0
- package/scripts/fast-backfill-embeddings.ts +173 -0
- package/scripts/fast-batch-embedder.cjs +334 -0
- package/scripts/first-run-model-setup.cjs +849 -0
- package/scripts/global-postinstall.cjs +1957 -0
- package/scripts/index-codebase.js +72 -0
- package/scripts/migrate-fix-embeddings.py +110 -0
- package/scripts/migrate-to-project-schemas.ts +525 -0
- package/scripts/optimize-embedding-model.py +324 -0
- package/scripts/optimize-instructions.cjs +530 -0
- package/scripts/pack-docker-images.sh +68 -0
- package/scripts/pack-for-testing.sh +130 -0
- package/scripts/postinstall.cjs +54 -0
- package/scripts/project-env.sh +51 -0
- package/scripts/reset-db.sh +30 -0
- package/scripts/run-indexer.ts +69 -0
- package/scripts/run-migrations.js +47 -0
- package/scripts/setup-db.sh +34 -0
- package/scripts/setup-minimal-schema.sql +143 -0
- package/scripts/skills/code-review.md +44 -0
- package/scripts/skills/debugging.md +56 -0
- package/scripts/skills/specmem-deployteam.md +239 -0
- package/scripts/skills/teammemberskills/EFFICIENT_GREP.md +171 -0
- package/scripts/skills/teammemberskills/task-planning.md +67 -0
- package/scripts/specmem/sockets/session-start.lock +1 -0
- package/scripts/specmem/sockets/session-stops.log +1 -0
- package/scripts/specmem-health.sh +382 -0
- package/scripts/specmem-init.cjs +6935 -0
- package/scripts/strip-debug-logs.cjs +43 -0
- package/scripts/test-mcp-standalone.sh +365 -0
- package/scripts/test-optimized-models.py +166 -0
- package/scripts/verify-embedding-fix.sh +148 -0
- package/skills/code-review.md +44 -0
- package/skills/debugging.md +56 -0
- package/skills/specmem-deployteam.md +239 -0
- package/skills/teammemberskills/EFFICIENT_GREP.md +171 -0
- package/skills/teammemberskills/task-planning.md +67 -0
- package/specmem-health.cjs +522 -0
- package/specmem.env +216 -0
|
@@ -0,0 +1,2081 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding Server Lifecycle Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the embedding server process lifecycle for the MCP server.
|
|
5
|
+
* Ensures embedding server is ALWAYS available when needs it.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* 1. On MCP server start: Check for stale processes, kill them, start fresh
|
|
9
|
+
* 2. On MCP server stop: Gracefully kill embedding server using PID file
|
|
10
|
+
* 3. Project-specific socket path: {PROJECT}/specmem/sockets/embeddings.sock
|
|
11
|
+
* 4. Health check that pings embedding server periodically
|
|
12
|
+
* 5. Auto-restart if embedding server dies
|
|
13
|
+
*
|
|
14
|
+
* @author hardwicksoftwareservices
|
|
15
|
+
*/
|
|
16
|
+
import { spawn, execSync } from 'child_process';
|
|
17
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync, closeSync, constants } from 'fs';
|
|
18
|
+
import { join, dirname } from 'path';
|
|
19
|
+
import { createConnection } from 'net';
|
|
20
|
+
import { EventEmitter } from 'events';
|
|
21
|
+
import { fileURLToPath } from 'url';
|
|
22
|
+
import { logger } from '../utils/logger.js';
|
|
23
|
+
import { getProjectPath, getEmbeddingSocketPath } from '../config.js';
|
|
24
|
+
import { checkProcessHealth } from '../utils/processHealthCheck.js';
|
|
25
|
+
import { getPythonPath, mergeWithProjectEnv } from '../utils/projectEnv.js';
|
|
26
|
+
import { getProjectSchema } from '../db/projectNamespacing.js';
|
|
27
|
+
import { ensureSocketDirAtomicSync } from '../utils/fileProcessingQueue.js';
|
|
28
|
+
import { getEmbeddingQueue, hasEmbeddingQueue } from '../services/EmbeddingQueue.js';
|
|
29
|
+
import { getDatabase } from '../database.js';
|
|
30
|
+
// ESM __dirname equivalent - replaces hardcoded paths
|
|
31
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
32
|
+
const __dirname = dirname(__filename);
|
|
33
|
+
const DEFAULT_CONFIG = {
|
|
34
|
+
healthCheckIntervalMs: parseInt(process.env['SPECMEM_EMBEDDING_HEALTH_INTERVAL'] || '30000', 10),
|
|
35
|
+
// MED-24 FIX: Increased from 3s to 10s to handle lazy-load scenarios
|
|
36
|
+
// The embedding server may need time to load the model on first request
|
|
37
|
+
healthCheckTimeoutMs: parseInt(process.env['SPECMEM_EMBEDDING_HEALTH_TIMEOUT'] || '10000', 10),
|
|
38
|
+
maxFailuresBeforeRestart: parseInt(process.env['SPECMEM_EMBEDDING_MAX_FAILURES'] || '2', 10),
|
|
39
|
+
restartCooldownMs: parseInt(process.env['SPECMEM_EMBEDDING_RESTART_COOLDOWN'] || '10000', 10),
|
|
40
|
+
// FIX: Increased from 15s to 45s - Python embedding server needs time to:
|
|
41
|
+
// 1. Start Python interpreter (~1-2s)
|
|
42
|
+
// 2. Import torch/sentence-transformers (~5-10s)
|
|
43
|
+
// 3. Load the ML model into memory (~10-20s)
|
|
44
|
+
// 4. Create the Unix socket (~100ms)
|
|
45
|
+
// Total: 15-30s typical, 45s gives headroom for slower machines
|
|
46
|
+
startupTimeoutMs: parseInt(process.env['SPECMEM_EMBEDDING_STARTUP_TIMEOUT'] || '45000', 10),
|
|
47
|
+
maxRestartAttempts: parseInt(process.env['SPECMEM_EMBEDDING_MAX_RESTARTS'] || '5', 10),
|
|
48
|
+
autoStart: process.env['SPECMEM_EMBEDDING_AUTO_START'] !== 'false',
|
|
49
|
+
killStaleOnStart: process.env['SPECMEM_EMBEDDING_KILL_STALE'] !== 'false',
|
|
50
|
+
maxProcessAgeHours: parseFloat(process.env['SPECMEM_EMBEDDING_MAX_AGE_HOURS'] || '1'),
|
|
51
|
+
};
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// EMBEDDING SERVER MANAGER
|
|
54
|
+
// ============================================================================
|
|
55
|
+
/**
|
|
56
|
+
* EmbeddingServerManager - Manages embedding server process lifecycle
|
|
57
|
+
*
|
|
58
|
+
* Events emitted:
|
|
59
|
+
* - 'started': { pid: number } - Server started successfully
|
|
60
|
+
* - 'stopped': { pid: number } - Server stopped
|
|
61
|
+
* - 'health': HealthCheckResult - Health check result
|
|
62
|
+
* - 'unhealthy': { failures: number } - Server unhealthy
|
|
63
|
+
* - 'restarting': { attempt: number } - Restarting server
|
|
64
|
+
* - 'restart_failed': { attempts: number } - All restart attempts failed
|
|
65
|
+
*/
|
|
66
|
+
export class EmbeddingServerManager extends EventEmitter {
|
|
67
|
+
config;
|
|
68
|
+
process = null;
|
|
69
|
+
healthCheckTimer = null;
|
|
70
|
+
isRunning = false;
|
|
71
|
+
consecutiveFailures = 0;
|
|
72
|
+
restartCount = 0;
|
|
73
|
+
lastRestartTime = 0;
|
|
74
|
+
startTime = null;
|
|
75
|
+
projectPath;
|
|
76
|
+
socketPath;
|
|
77
|
+
pidFilePath;
|
|
78
|
+
isShuttingDown = false;
|
|
79
|
+
// FIX: Prevent concurrent starts (race condition causing duplicate processes)
|
|
80
|
+
isStarting = false;
|
|
81
|
+
// FIX: Startup grace period - prevents health monitoring from reporting errors during initialization
|
|
82
|
+
startupGraceUntil = 0;
|
|
83
|
+
// FIX: File-based lock to prevent CROSS-PROCESS concurrent starts
|
|
84
|
+
// The in-memory isStarting flag only prevents within same process
|
|
85
|
+
// Multiple MCP servers (subagents, sessions) need file-based coordination
|
|
86
|
+
startLockPath;
|
|
87
|
+
START_LOCK_TIMEOUT_MS = 60000; // 60 second lock timeout
|
|
88
|
+
// Phase 4: Track if user manually stopped server (prevents auto-restart)
|
|
89
|
+
stoppedFlagPath;
|
|
90
|
+
// Phase 4: Track restart timestamps for loop detection
|
|
91
|
+
restartTimestamps = [];
|
|
92
|
+
// KYS (Keep Yourself Safe) heartbeat timer - sends heartbeat every 25s to embedding server
|
|
93
|
+
// If embedding server doesn't receive heartbeat within 90s, it commits suicide
|
|
94
|
+
// This prevents zombie embedding servers when MCP crashes (increased from 30s for startup tolerance)
|
|
95
|
+
kysHeartbeatTimer = null;
|
|
96
|
+
KYS_HEARTBEAT_INTERVAL_MS = 25000; // 25 seconds
|
|
97
|
+
constructor(config = {}) {
|
|
98
|
+
super();
|
|
99
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
100
|
+
// Get project-specific paths
|
|
101
|
+
this.projectPath = getProjectPath();
|
|
102
|
+
this.socketPath = getEmbeddingSocketPath();
|
|
103
|
+
this.pidFilePath = join(dirname(this.socketPath), 'embedding.pid');
|
|
104
|
+
// Phase 4: Stopped flag file path - prevents auto-restart when user manually stops
|
|
105
|
+
this.stoppedFlagPath = join(dirname(this.socketPath), 'embedding.stopped');
|
|
106
|
+
// FIX: File-based start lock path - prevents cross-process race condition
|
|
107
|
+
this.startLockPath = join(dirname(this.socketPath), 'embedding.starting');
|
|
108
|
+
logger.info({
|
|
109
|
+
projectPath: this.projectPath,
|
|
110
|
+
socketPath: this.socketPath,
|
|
111
|
+
pidFilePath: this.pidFilePath,
|
|
112
|
+
startLockPath: this.startLockPath,
|
|
113
|
+
config: this.config,
|
|
114
|
+
}, '[EmbeddingServerManager] Initialized');
|
|
115
|
+
}
|
|
116
|
+
// ==========================================================================
|
|
117
|
+
// PUBLIC METHODS
|
|
118
|
+
// ==========================================================================
|
|
119
|
+
/**
|
|
120
|
+
* Initialize and start the embedding server
|
|
121
|
+
* Should be called on MCP server startup
|
|
122
|
+
*/
|
|
123
|
+
async initialize() {
|
|
124
|
+
logger.info('[EmbeddingServerManager] Initializing...');
|
|
125
|
+
// Phase 4: Check if user manually stopped the server - respect their choice
|
|
126
|
+
if (this.isStoppedByUser()) {
|
|
127
|
+
logger.info('[EmbeddingServerManager] Server stopped by user, skipping auto-start. Use userStart() to restart.');
|
|
128
|
+
// Still start health monitoring so we can detect if Docker starts it externally
|
|
129
|
+
this.startHealthMonitoring();
|
|
130
|
+
// KYS heartbeat keeps server alive when externally started
|
|
131
|
+
this.startKysHeartbeat();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Step 1: Kill any stale processes
|
|
135
|
+
if (this.config.killStaleOnStart) {
|
|
136
|
+
await this.killStaleProcesses();
|
|
137
|
+
}
|
|
138
|
+
// Step 2: Start the embedding server if auto-start is enabled
|
|
139
|
+
if (this.config.autoStart) {
|
|
140
|
+
await this.start();
|
|
141
|
+
// NOTE: KYS heartbeat is started inside start() immediately after server starts
|
|
142
|
+
// This prevents watchdog timeout during long initialization operations
|
|
143
|
+
}
|
|
144
|
+
// Step 3: Start health monitoring
|
|
145
|
+
this.startHealthMonitoring();
|
|
146
|
+
// Step 4: Start KYS heartbeat if server wasn't auto-started (e.g., externally managed)
|
|
147
|
+
// Only start if not already running to avoid duplicate timers
|
|
148
|
+
if (!this.kysHeartbeatTimer) {
|
|
149
|
+
this.startKysHeartbeat();
|
|
150
|
+
logger.info('[EmbeddingServerManager] KYS heartbeat started (fallback path)');
|
|
151
|
+
}
|
|
152
|
+
logger.info('[EmbeddingServerManager] Initialization complete');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Start the embedding server
|
|
156
|
+
* FIX: First check if Docker container is already serving on this socket
|
|
157
|
+
* FIX: Uses file-based lock to prevent CROSS-PROCESS race conditions
|
|
158
|
+
*/
|
|
159
|
+
async start() {
|
|
160
|
+
if (this.isRunning) {
|
|
161
|
+
logger.debug('[EmbeddingServerManager] Server already running');
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
// FIX: Prevent concurrent starts (race condition causing duplicate processes)
|
|
165
|
+
if (this.isStarting) {
|
|
166
|
+
logger.debug('[EmbeddingServerManager] Start already in progress (in-memory lock), skipping duplicate');
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (this.isShuttingDown) {
|
|
170
|
+
logger.warn('[EmbeddingServerManager] Cannot start during shutdown');
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
// FIX 1B: Process deduplication check BEFORE acquiring lock
|
|
174
|
+
// Find all running embedding server processes and try to reuse one
|
|
175
|
+
const runningServers = this.findRunningEmbeddingServers();
|
|
176
|
+
if (runningServers.length > 0) {
|
|
177
|
+
logger.info({ count: runningServers.length, pids: runningServers.map(s => s.pid) },
|
|
178
|
+
'[EmbeddingServerManager] FIX 1B: Found existing embedding server processes, checking health');
|
|
179
|
+
// Test if any existing process is healthy via socket
|
|
180
|
+
if (existsSync(this.socketPath)) {
|
|
181
|
+
const healthResult = await this.healthCheck();
|
|
182
|
+
if (healthResult.success) {
|
|
183
|
+
logger.info({
|
|
184
|
+
pids: runningServers.map(s => s.pid),
|
|
185
|
+
responseTimeMs: healthResult.responseTimeMs,
|
|
186
|
+
}, '[EmbeddingServerManager] FIX 1B: Existing server is healthy - reusing instead of spawning new');
|
|
187
|
+
this.isRunning = true;
|
|
188
|
+
this.startTime = Date.now();
|
|
189
|
+
this.consecutiveFailures = 0;
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Existing processes found but unhealthy - kill them all before starting fresh
|
|
194
|
+
logger.warn({ count: runningServers.length },
|
|
195
|
+
'[EmbeddingServerManager] FIX 1B: Existing processes are unhealthy, killing all before fresh start');
|
|
196
|
+
for (const server of runningServers) {
|
|
197
|
+
try {
|
|
198
|
+
process.kill(server.pid, 'SIGTERM');
|
|
199
|
+
logger.info({ pid: server.pid }, '[EmbeddingServerManager] FIX 1B: Sent SIGTERM to unhealthy server');
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
logger.debug({ pid: server.pid, error: err.message }, '[EmbeddingServerManager] FIX 1B: Failed to SIGTERM process');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Wait 2 seconds for processes to terminate
|
|
206
|
+
await this.sleep(2000);
|
|
207
|
+
// Force kill any survivors
|
|
208
|
+
for (const server of runningServers) {
|
|
209
|
+
try {
|
|
210
|
+
process.kill(server.pid, 0); // Check if alive
|
|
211
|
+
process.kill(server.pid, 'SIGKILL');
|
|
212
|
+
logger.info({ pid: server.pid }, '[EmbeddingServerManager] FIX 1B: Force killed surviving process');
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Already dead - good
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Clean up stale socket
|
|
219
|
+
if (existsSync(this.socketPath)) {
|
|
220
|
+
try { unlinkSync(this.socketPath); } catch { /* ignore */ }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// FIX 1C: PID file validation before acquiring lock
|
|
224
|
+
// Check if PID file points to a live, healthy process we can reuse
|
|
225
|
+
const pidData = this.readPidFileWithTimestamp();
|
|
226
|
+
if (pidData && pidData.pid) {
|
|
227
|
+
try {
|
|
228
|
+
process.kill(pidData.pid, 0); // Check if process is alive
|
|
229
|
+
// Process is alive - check if it's healthy
|
|
230
|
+
if (existsSync(this.socketPath)) {
|
|
231
|
+
const pidHealthResult = await this.healthCheck();
|
|
232
|
+
if (pidHealthResult.success) {
|
|
233
|
+
logger.info({
|
|
234
|
+
pid: pidData.pid,
|
|
235
|
+
responseTimeMs: pidHealthResult.responseTimeMs,
|
|
236
|
+
}, '[EmbeddingServerManager] FIX 1C: PID file process is alive and healthy - reusing');
|
|
237
|
+
this.isRunning = true;
|
|
238
|
+
this.startTime = Date.now();
|
|
239
|
+
this.consecutiveFailures = 0;
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
logger.info({ pid: pidData.pid },
|
|
244
|
+
'[EmbeddingServerManager] FIX 1C: PID file process alive but not healthy, continuing with start');
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Process is dead - clean up PID file
|
|
248
|
+
logger.info({ pid: pidData.pid },
|
|
249
|
+
'[EmbeddingServerManager] FIX 1C: PID file process is dead, cleaning up');
|
|
250
|
+
this.removePidFile();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// FIX: ATOMIC CROSS-PROCESS file-based lock using O_CREAT | O_EXCL
|
|
254
|
+
// This prevents race condition where multiple processes check then write
|
|
255
|
+
// O_EXCL makes openSync fail if file exists - truly atomic lock acquisition
|
|
256
|
+
const lockAcquired = await this.acquireStartLockAtomic();
|
|
257
|
+
if (!lockAcquired) {
|
|
258
|
+
// Another process is starting - wait for them
|
|
259
|
+
logger.info('[EmbeddingServerManager] Lock held by another process, waiting for server...');
|
|
260
|
+
const waitStart = Date.now();
|
|
261
|
+
while (Date.now() - waitStart < 30000) { // 30s max wait
|
|
262
|
+
await this.sleep(1000);
|
|
263
|
+
if (existsSync(this.socketPath)) {
|
|
264
|
+
const healthResult = await this.healthCheck();
|
|
265
|
+
if (healthResult.success) {
|
|
266
|
+
logger.info('[EmbeddingServerManager] Another process started the server successfully');
|
|
267
|
+
this.isRunning = true;
|
|
268
|
+
this.startTime = Date.now();
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Check if lock released (we can try again)
|
|
273
|
+
if (!existsSync(this.startLockPath)) {
|
|
274
|
+
const retryLock = await this.acquireStartLockAtomic();
|
|
275
|
+
if (retryLock)
|
|
276
|
+
break; // Got lock on retry
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Timed out waiting
|
|
280
|
+
if (!this.acquireStartLockAtomic()) {
|
|
281
|
+
logger.warn('[EmbeddingServerManager] Timeout waiting for lock, giving up');
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Set starting flag immediately to prevent race conditions
|
|
286
|
+
this.isStarting = true;
|
|
287
|
+
// FIX: Set dynamic startup grace period based on configured startupTimeoutMs
|
|
288
|
+
this.startupGraceUntil = Date.now() + this.config.startupTimeoutMs;
|
|
289
|
+
logger.info({ graceMs: this.config.startupTimeoutMs }, '[EmbeddingServerManager] Starting embedding server (grace period active)...');
|
|
290
|
+
// Ensure socket directory exists
|
|
291
|
+
// Task #17 FIX: Use atomic mkdir to prevent race condition when multiple
|
|
292
|
+
// MCP servers try to create the socket directory simultaneously
|
|
293
|
+
const socketDir = dirname(this.socketPath);
|
|
294
|
+
try {
|
|
295
|
+
const created = ensureSocketDirAtomicSync(socketDir);
|
|
296
|
+
if (created) {
|
|
297
|
+
logger.debug({ socketDir }, '[EmbeddingServerManager] Created socket directory atomically');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
logger.error({ error: err }, '[EmbeddingServerManager] Failed to create socket directory');
|
|
302
|
+
}
|
|
303
|
+
// FIX: Check if socket already exists AND is responsive (Docker may have started it)
|
|
304
|
+
if (existsSync(this.socketPath)) {
|
|
305
|
+
const healthResult = await this.healthCheck();
|
|
306
|
+
if (healthResult.success) {
|
|
307
|
+
logger.info({
|
|
308
|
+
socketPath: this.socketPath,
|
|
309
|
+
responseTimeMs: healthResult.responseTimeMs
|
|
310
|
+
}, '[EmbeddingServerManager] Socket already responsive (Docker/external server) - using existing');
|
|
311
|
+
// Mark as running but with no managed process (external server)
|
|
312
|
+
this.isRunning = true;
|
|
313
|
+
this.startTime = Date.now();
|
|
314
|
+
this.consecutiveFailures = 0;
|
|
315
|
+
this.isStarting = false; // Reset starting flag
|
|
316
|
+
this.releaseStartLock(); // Release file-based lock
|
|
317
|
+
// CRITICAL FIX: Start KYS heartbeat for external server too
|
|
318
|
+
this.startKysHeartbeat();
|
|
319
|
+
logger.info('[EmbeddingServerManager] KYS heartbeat started for external server');
|
|
320
|
+
this.emit('started', { pid: null, external: true });
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
// Socket exists but not responding - clean it up
|
|
324
|
+
try {
|
|
325
|
+
unlinkSync(this.socketPath);
|
|
326
|
+
logger.debug('[EmbeddingServerManager] Removed non-responsive socket file');
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
logger.warn({ error: err }, '[EmbeddingServerManager] Failed to remove old socket');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Find the embedding script (prefers warm-start.sh Docker mode)
|
|
333
|
+
const scriptInfo = this.findEmbeddingScript();
|
|
334
|
+
if (!scriptInfo) {
|
|
335
|
+
logger.error('[EmbeddingServerManager] Could not find embedding script');
|
|
336
|
+
this.isStarting = false;
|
|
337
|
+
this.releaseStartLock();
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
const { script: embeddingScript, useWarmStart } = scriptInfo;
|
|
341
|
+
// Spawn the embedding server process
|
|
342
|
+
try {
|
|
343
|
+
// Read config from model-config.json (heavyOps + resource limits)
|
|
344
|
+
let configEnv = {};
|
|
345
|
+
try {
|
|
346
|
+
const configPath = join(this.projectPath, 'specmem', 'model-config.json');
|
|
347
|
+
if (existsSync(configPath)) {
|
|
348
|
+
const modelConfig = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
349
|
+
// Pass heavyOps settings
|
|
350
|
+
if (modelConfig.heavyOps?.enabled) {
|
|
351
|
+
configEnv.SPECMEM_HEAVY_OPS = '1';
|
|
352
|
+
configEnv.SPECMEM_HEAVY_OPS_BATCH_MULT = String(modelConfig.heavyOps.batchSizeMultiplier || 2);
|
|
353
|
+
configEnv.SPECMEM_HEAVY_OPS_THROTTLE_REDUCE = String(modelConfig.heavyOps.throttleReduction || 0.20);
|
|
354
|
+
logger.info({ heavyOps: modelConfig.heavyOps }, '[EmbeddingServerManager] Heavy Ops mode enabled');
|
|
355
|
+
}
|
|
356
|
+
// Pass batch size from config
|
|
357
|
+
if (modelConfig.embedding?.batchSize) {
|
|
358
|
+
configEnv.SPECMEM_EMBEDDING_BATCH_SIZE = String(modelConfig.embedding.batchSize);
|
|
359
|
+
}
|
|
360
|
+
// Pass resource limits (cpuMin, cpuMax, ramMin, ramMax)
|
|
361
|
+
if (modelConfig.resources) {
|
|
362
|
+
const r = modelConfig.resources;
|
|
363
|
+
if (r.cpuMin != null)
|
|
364
|
+
configEnv.SPECMEM_CPU_MIN = String(r.cpuMin);
|
|
365
|
+
if (r.cpuMax != null)
|
|
366
|
+
configEnv.SPECMEM_CPU_MAX = String(r.cpuMax);
|
|
367
|
+
if (r.ramMinMb != null)
|
|
368
|
+
configEnv.SPECMEM_RAM_MIN_MB = String(r.ramMinMb);
|
|
369
|
+
if (r.ramMaxMb != null)
|
|
370
|
+
configEnv.SPECMEM_RAM_MAX_MB = String(r.ramMaxMb);
|
|
371
|
+
logger.info({ resources: r }, '[EmbeddingServerManager] Resource limits configured');
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (configErr) {
|
|
376
|
+
logger.warn({ error: configErr }, '[EmbeddingServerManager] Could not read model-config.json');
|
|
377
|
+
}
|
|
378
|
+
// yooo ALWAYS use mergeWithProjectEnv for project isolation - spawned processes need the full context
|
|
379
|
+
// CRITICAL: Pass DB schema name so embedding server queries the right schema
|
|
380
|
+
const projectSchema = getProjectSchema(this.projectPath);
|
|
381
|
+
const env = mergeWithProjectEnv({
|
|
382
|
+
SPECMEM_SOCKET_DIR: socketDir,
|
|
383
|
+
SPECMEM_EMBEDDING_SOCKET: this.socketPath,
|
|
384
|
+
SPECMEM_EMBEDDING_IDLE_TIMEOUT: '0',
|
|
385
|
+
SPECMEM_DB_SCHEMA: projectSchema,
|
|
386
|
+
...configEnv,
|
|
387
|
+
});
|
|
388
|
+
if (useWarmStart) {
|
|
389
|
+
// Docker mode via warm-start.sh - handles container lifecycle
|
|
390
|
+
logger.info({ script: embeddingScript }, '[EmbeddingServerManager] Starting via warm-start.sh (Docker mode)');
|
|
391
|
+
this.process = spawn('bash', [embeddingScript], {
|
|
392
|
+
env,
|
|
393
|
+
cwd: dirname(embeddingScript),
|
|
394
|
+
detached: true,
|
|
395
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
// Direct Python mode - Task #22 fix: Use getPythonPath()
|
|
400
|
+
const pythonPath = getPythonPath();
|
|
401
|
+
logger.info({ pythonPath, script: embeddingScript }, '[EmbeddingServerManager] Starting via Python (direct mode)');
|
|
402
|
+
this.process = spawn(pythonPath, [embeddingScript, '--service'], {
|
|
403
|
+
env,
|
|
404
|
+
cwd: this.projectPath,
|
|
405
|
+
detached: true,
|
|
406
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
const pid = this.process.pid;
|
|
410
|
+
if (!pid) {
|
|
411
|
+
throw new Error('Failed to get process PID');
|
|
412
|
+
}
|
|
413
|
+
// Write PID file
|
|
414
|
+
this.writePidFile(pid);
|
|
415
|
+
// Handle process events
|
|
416
|
+
this.process.on('error', (err) => {
|
|
417
|
+
logger.error({ error: err }, '[EmbeddingServerManager] Process error');
|
|
418
|
+
this.handleProcessExit(-1, 'error');
|
|
419
|
+
});
|
|
420
|
+
this.process.on('exit', (code, signal) => {
|
|
421
|
+
logger.info({ code, signal }, '[EmbeddingServerManager] Process exited');
|
|
422
|
+
this.handleProcessExit(code ?? -1, signal ?? 'unknown');
|
|
423
|
+
});
|
|
424
|
+
// Log stderr for debugging
|
|
425
|
+
this.process.stderr?.on('data', (data) => {
|
|
426
|
+
const msg = data.toString().trim();
|
|
427
|
+
if (msg && (msg.includes('Socket') || msg.includes('READY') || msg.includes('Error') || msg.includes('FRANKENSTEIN'))) {
|
|
428
|
+
logger.debug({ msg }, '[EmbeddingServerManager] Server stderr');
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
// Wait for socket to appear
|
|
432
|
+
const socketReady = await this.waitForSocket();
|
|
433
|
+
if (!socketReady) {
|
|
434
|
+
logger.error('[EmbeddingServerManager] Socket did not appear within timeout');
|
|
435
|
+
this.isStarting = false; // Reset starting flag on timeout
|
|
436
|
+
this.releaseStartLock(); // Release file-based lock
|
|
437
|
+
await this.stop();
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
this.isRunning = true;
|
|
441
|
+
this.startTime = Date.now();
|
|
442
|
+
this.consecutiveFailures = 0;
|
|
443
|
+
// Verify the new process with health check
|
|
444
|
+
const verifyHealth = await this.healthCheck();
|
|
445
|
+
if (verifyHealth.success) {
|
|
446
|
+
logger.info({
|
|
447
|
+
pid,
|
|
448
|
+
socketPath: this.socketPath,
|
|
449
|
+
responseTimeMs: verifyHealth.responseTimeMs,
|
|
450
|
+
dimensions: verifyHealth.dimensions,
|
|
451
|
+
}, '[EmbeddingServerManager] Server started successfully and verified healthy');
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
logger.warn({
|
|
455
|
+
pid,
|
|
456
|
+
socketPath: this.socketPath,
|
|
457
|
+
error: verifyHealth.error,
|
|
458
|
+
}, '[EmbeddingServerManager] Server started but health check failed (may still be initializing)');
|
|
459
|
+
}
|
|
460
|
+
this.emit('started', { pid });
|
|
461
|
+
// CRITICAL FIX: Start KYS heartbeat IMMEDIATELY after server starts
|
|
462
|
+
// The heartbeat must begin BEFORE any long-running operations (like queue draining)
|
|
463
|
+
// to prevent the embedding server's watchdog from timing out
|
|
464
|
+
this.startKysHeartbeat();
|
|
465
|
+
logger.info('[EmbeddingServerManager] KYS heartbeat started immediately after server start');
|
|
466
|
+
// Drain any queued embedding requests now that server is up
|
|
467
|
+
this.drainEmbeddingQueueIfNeeded().catch(err => {
|
|
468
|
+
logger.warn({ error: err }, '[EmbeddingServerManager] Queue drain failed (non-fatal)');
|
|
469
|
+
});
|
|
470
|
+
this.isStarting = false; // Reset starting flag on success
|
|
471
|
+
// FIX: Clear grace period once server is verified running
|
|
472
|
+
if (verifyHealth.success) {
|
|
473
|
+
this.startupGraceUntil = 0;
|
|
474
|
+
}
|
|
475
|
+
this.releaseStartLock(); // Release file-based lock
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
catch (err) {
|
|
479
|
+
logger.error({ error: err }, '[EmbeddingServerManager] Failed to start server');
|
|
480
|
+
this.isStarting = false; // Reset starting flag on error
|
|
481
|
+
this.startupGraceUntil = 0; // Clear grace on failure too
|
|
482
|
+
this.releaseStartLock(); // Release file-based lock
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Atomically acquire the start lock using O_CREAT | O_EXCL
|
|
488
|
+
* This is truly atomic - the OS ensures only one process can create the file
|
|
489
|
+
* @returns true if lock acquired, false if another process has it
|
|
490
|
+
*/
|
|
491
|
+
async acquireStartLockAtomic() {
|
|
492
|
+
try {
|
|
493
|
+
// FIX 1A: Strengthened lock acquisition with PID validation and stale detection
|
|
494
|
+
// First check if lock exists and is stale
|
|
495
|
+
if (existsSync(this.startLockPath)) {
|
|
496
|
+
try {
|
|
497
|
+
const lockContent = readFileSync(this.startLockPath, 'utf8').trim();
|
|
498
|
+
const parts = lockContent.split(':');
|
|
499
|
+
const lockTime = parseInt(parts[0], 10);
|
|
500
|
+
const lockPid = parseInt(parts[1], 10);
|
|
501
|
+
const lockAge = Date.now() - lockTime;
|
|
502
|
+
// FIX 1A: Reduced stale threshold from 60s to 120s for robustness
|
|
503
|
+
const STALE_LOCK_THRESHOLD_MS = 120000; // 120 seconds
|
|
504
|
+
if (lockAge >= STALE_LOCK_THRESHOLD_MS) {
|
|
505
|
+
// Stale lock by time - remove it
|
|
506
|
+
logger.warn({ lockAge, lockPid, thresholdMs: STALE_LOCK_THRESHOLD_MS }, '[EmbeddingServerManager] Removing stale start lock (exceeded time threshold)');
|
|
507
|
+
unlinkSync(this.startLockPath);
|
|
508
|
+
}
|
|
509
|
+
else if (!isNaN(lockPid) && lockPid > 0) {
|
|
510
|
+
// FIX 1A: Lock is recent - check if the owning process is still alive
|
|
511
|
+
try {
|
|
512
|
+
process.kill(lockPid, 0); // Signal 0 = check existence
|
|
513
|
+
// Process is alive and lock is recent - respect it
|
|
514
|
+
logger.debug({ lockAge, lockPid }, '[EmbeddingServerManager] Lock held by alive process');
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
catch (killErr) {
|
|
518
|
+
// Process is dead - stale lock from a crashed process
|
|
519
|
+
logger.warn({ lockPid, lockAge }, '[EmbeddingServerManager] Lock owner process is dead, removing stale lock');
|
|
520
|
+
try {
|
|
521
|
+
unlinkSync(this.startLockPath);
|
|
522
|
+
}
|
|
523
|
+
catch { /* ignore */ }
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
// Recent lock but no valid PID - respect it
|
|
528
|
+
logger.debug({ lockAge, lockPid }, '[EmbeddingServerManager] Lock held by another process (no valid PID)');
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
catch (readErr) {
|
|
533
|
+
// Lock file corrupt or unreadable - try to remove it
|
|
534
|
+
try {
|
|
535
|
+
unlinkSync(this.startLockPath);
|
|
536
|
+
}
|
|
537
|
+
catch { /* ignore */ }
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// Try atomic create with O_CREAT | O_EXCL - fails if file exists
|
|
541
|
+
// This is the atomic part - only one process can succeed
|
|
542
|
+
const fd = openSync(this.startLockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
|
|
543
|
+
// FIX 1A: Write PID + timestamp to lock file for stale detection
|
|
544
|
+
const lockContent = `${Date.now()}:${process.pid}`;
|
|
545
|
+
writeFileSync(fd, lockContent, 'utf8');
|
|
546
|
+
closeSync(fd);
|
|
547
|
+
logger.debug({ pid: process.pid }, '[EmbeddingServerManager] Acquired start lock atomically');
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
catch (err) {
|
|
551
|
+
if (err.code === 'EEXIST') {
|
|
552
|
+
// FIX 1A: On EEXIST, read the lock content and check if owner is alive
|
|
553
|
+
try {
|
|
554
|
+
const lockContent = readFileSync(this.startLockPath, 'utf8').trim();
|
|
555
|
+
const parts = lockContent.split(':');
|
|
556
|
+
const lockTime = parseInt(parts[0], 10);
|
|
557
|
+
const lockPid = parseInt(parts[1], 10);
|
|
558
|
+
const lockAge = Date.now() - lockTime;
|
|
559
|
+
// Check if lock is stale (>120s) or owner process is dead
|
|
560
|
+
let isStale = lockAge > 120000;
|
|
561
|
+
if (!isStale && !isNaN(lockPid) && lockPid > 0) {
|
|
562
|
+
try {
|
|
563
|
+
process.kill(lockPid, 0);
|
|
564
|
+
// Owner alive, lock valid
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
// Owner dead, lock is stale
|
|
568
|
+
isStale = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (isStale) {
|
|
572
|
+
logger.warn({ lockPid, lockAge }, '[EmbeddingServerManager] EEXIST but lock is stale, removing and retrying');
|
|
573
|
+
try {
|
|
574
|
+
unlinkSync(this.startLockPath);
|
|
575
|
+
}
|
|
576
|
+
catch { /* ignore */ }
|
|
577
|
+
// Retry once after removing stale lock
|
|
578
|
+
try {
|
|
579
|
+
const fd = openSync(this.startLockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
|
|
580
|
+
const newLockContent = `${Date.now()}:${process.pid}`;
|
|
581
|
+
writeFileSync(fd, newLockContent, 'utf8');
|
|
582
|
+
closeSync(fd);
|
|
583
|
+
logger.debug({ pid: process.pid }, '[EmbeddingServerManager] Acquired start lock on retry after stale removal');
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
catch (retryErr) {
|
|
587
|
+
logger.debug('[EmbeddingServerManager] Retry lock acquisition failed - another process won the race');
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
catch (readErr) {
|
|
593
|
+
// Can't read lock file - just report as held
|
|
594
|
+
}
|
|
595
|
+
logger.debug('[EmbeddingServerManager] Lock already exists (EEXIST) - another process has it');
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
// Other error - log and fail
|
|
599
|
+
logger.warn({ error: err }, '[EmbeddingServerManager] Lock acquisition error');
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Release the file-based start lock
|
|
605
|
+
*/
|
|
606
|
+
releaseStartLock() {
|
|
607
|
+
try {
|
|
608
|
+
if (existsSync(this.startLockPath)) {
|
|
609
|
+
unlinkSync(this.startLockPath);
|
|
610
|
+
logger.debug('[EmbeddingServerManager] Released start lock');
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
catch (err) {
|
|
614
|
+
logger.debug({ error: err }, '[EmbeddingServerManager] Failed to release start lock');
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* FIX 1B: Find all running embedding server processes using ps
|
|
619
|
+
* Returns array of { pid, command } objects for each running frankenstein-embeddings.py process
|
|
620
|
+
*/
|
|
621
|
+
findRunningEmbeddingServers() {
|
|
622
|
+
try {
|
|
623
|
+
const result = execSync('ps aux | grep frankenstein-embeddings.py | grep -v grep', {
|
|
624
|
+
encoding: 'utf8',
|
|
625
|
+
timeout: 5000,
|
|
626
|
+
}).trim();
|
|
627
|
+
if (!result) return [];
|
|
628
|
+
const processes = [];
|
|
629
|
+
for (const line of result.split('\n')) {
|
|
630
|
+
if (!line.trim()) continue;
|
|
631
|
+
const parts = line.trim().split(/\s+/);
|
|
632
|
+
const pid = parseInt(parts[1], 10);
|
|
633
|
+
if (!isNaN(pid)) {
|
|
634
|
+
processes.push({ pid, command: parts.slice(10).join(' ') });
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return processes;
|
|
638
|
+
}
|
|
639
|
+
catch (err) {
|
|
640
|
+
// ps/grep returns exit code 1 when no matches found - that's normal
|
|
641
|
+
return [];
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Drain embedding queue after server starts
|
|
646
|
+
* Processes any pending embedding requests that were queued while server was down
|
|
647
|
+
*/
|
|
648
|
+
async drainEmbeddingQueueIfNeeded() {
|
|
649
|
+
try {
|
|
650
|
+
const db = getDatabase();
|
|
651
|
+
if (!db) {
|
|
652
|
+
logger.debug('[EmbeddingServerManager] No database available, skipping queue drain');
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const pool = db.getPool();
|
|
656
|
+
if (!pool) {
|
|
657
|
+
logger.debug('[EmbeddingServerManager] No pool available, skipping queue drain');
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (!hasEmbeddingQueue()) {
|
|
661
|
+
logger.debug('[EmbeddingServerManager] No embedding queue exists, skipping drain');
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
const queue = getEmbeddingQueue(pool);
|
|
665
|
+
const pendingCount = await queue.getPendingCount();
|
|
666
|
+
if (pendingCount === 0) {
|
|
667
|
+
logger.debug('[EmbeddingServerManager] No pending items in queue');
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
logger.info({ pendingCount }, '[EmbeddingServerManager] Draining embedding queue after server start');
|
|
671
|
+
const drained = await queue.drainQueue((text) => this.generateEmbeddingViaSocket(text));
|
|
672
|
+
logger.info({ drained }, '[EmbeddingServerManager] Queue drain complete');
|
|
673
|
+
}
|
|
674
|
+
catch (err) {
|
|
675
|
+
logger.warn({ error: err }, '[EmbeddingServerManager] Failed to drain embedding queue');
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Generate embedding using direct socket connection
|
|
680
|
+
* Used by drainQueue to process pending requests after server comes online
|
|
681
|
+
*/
|
|
682
|
+
async generateEmbeddingViaSocket(text) {
|
|
683
|
+
return new Promise((resolve, reject) => {
|
|
684
|
+
const socket = createConnection(this.socketPath);
|
|
685
|
+
let buffer = '';
|
|
686
|
+
let resolved = false;
|
|
687
|
+
const timeout = setTimeout(() => {
|
|
688
|
+
if (!resolved) {
|
|
689
|
+
resolved = true;
|
|
690
|
+
socket.destroy();
|
|
691
|
+
reject(new Error('Embedding generation timeout'));
|
|
692
|
+
}
|
|
693
|
+
}, 60000); // 60 second timeout for embedding generation
|
|
694
|
+
socket.on('connect', () => {
|
|
695
|
+
socket.write(JSON.stringify({ text }) + '\n');
|
|
696
|
+
});
|
|
697
|
+
socket.on('data', (data) => {
|
|
698
|
+
buffer += data.toString();
|
|
699
|
+
// Process all complete lines in the buffer
|
|
700
|
+
// The server may send multiple lines: {"status": "processing"} then {"embedding": [...]}
|
|
701
|
+
let newlineIndex;
|
|
702
|
+
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
703
|
+
if (resolved)
|
|
704
|
+
return;
|
|
705
|
+
const line = buffer.slice(0, newlineIndex);
|
|
706
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
707
|
+
try {
|
|
708
|
+
const response = JSON.parse(line);
|
|
709
|
+
// Handle error responses
|
|
710
|
+
if (response.error) {
|
|
711
|
+
clearTimeout(timeout);
|
|
712
|
+
resolved = true;
|
|
713
|
+
socket.end();
|
|
714
|
+
reject(new Error(response.error));
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
// Skip "processing" status messages - wait for actual embedding
|
|
718
|
+
if (response.status === 'processing') {
|
|
719
|
+
logger.debug({ textLength: response.text_length }, '[EmbeddingServerManager] Embedding request queued, waiting for result...');
|
|
720
|
+
continue; // Keep reading for the actual embedding
|
|
721
|
+
}
|
|
722
|
+
// Got the embedding!
|
|
723
|
+
if (response.embedding && Array.isArray(response.embedding)) {
|
|
724
|
+
clearTimeout(timeout);
|
|
725
|
+
resolved = true;
|
|
726
|
+
socket.end();
|
|
727
|
+
resolve(response.embedding);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
// Unknown response format - log but keep waiting
|
|
731
|
+
logger.warn({ response: JSON.stringify(response).slice(0, 100) }, '[EmbeddingServerManager] Unexpected response format, continuing to wait');
|
|
732
|
+
}
|
|
733
|
+
catch (parseErr) {
|
|
734
|
+
logger.warn({ line: line.slice(0, 100) }, '[EmbeddingServerManager] Failed to parse line, continuing to wait');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
socket.on('error', (err) => {
|
|
739
|
+
clearTimeout(timeout);
|
|
740
|
+
if (!resolved) {
|
|
741
|
+
resolved = true;
|
|
742
|
+
reject(err);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Stop the embedding server gracefully
|
|
749
|
+
*/
|
|
750
|
+
async stop() {
|
|
751
|
+
this.isShuttingDown = true;
|
|
752
|
+
this.isStarting = false; // Reset starting flag on stop
|
|
753
|
+
this.startupGraceUntil = 0; // Clear any active grace period
|
|
754
|
+
logger.info('[EmbeddingServerManager] Stopping embedding server...');
|
|
755
|
+
// Stop health monitoring
|
|
756
|
+
this.stopHealthMonitoring();
|
|
757
|
+
// Stop KYS heartbeat - server will suicide after 30s without heartbeat
|
|
758
|
+
this.stopKysHeartbeat();
|
|
759
|
+
// Kill the process
|
|
760
|
+
if (this.process && this.process.pid) {
|
|
761
|
+
try {
|
|
762
|
+
// Try graceful SIGTERM first
|
|
763
|
+
process.kill(this.process.pid, 'SIGTERM');
|
|
764
|
+
// Wait briefly for graceful shutdown
|
|
765
|
+
await this.sleep(1000);
|
|
766
|
+
// Force kill if still running
|
|
767
|
+
try {
|
|
768
|
+
process.kill(this.process.pid, 0); // Check if still running
|
|
769
|
+
process.kill(this.process.pid, 'SIGKILL');
|
|
770
|
+
logger.debug('[EmbeddingServerManager] Force killed process');
|
|
771
|
+
}
|
|
772
|
+
catch {
|
|
773
|
+
// Process already dead - good
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
catch (err) {
|
|
777
|
+
logger.debug({ error: err }, '[EmbeddingServerManager] Process already dead');
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
// Also try killing by PID file (in case process reference was lost)
|
|
781
|
+
await this.killByPidFile();
|
|
782
|
+
// FIX: Kill ALL running embedding server processes (orphans from previous sessions/subagents)
|
|
783
|
+
const allServers = this.findRunningEmbeddingServers();
|
|
784
|
+
if (allServers.length > 0) {
|
|
785
|
+
logger.info({ count: allServers.length, pids: allServers.map(s => s.pid) },
|
|
786
|
+
'[EmbeddingServerManager] Killing all remaining embedding server processes');
|
|
787
|
+
for (const server of allServers) {
|
|
788
|
+
try {
|
|
789
|
+
process.kill(server.pid, 'SIGTERM');
|
|
790
|
+
} catch { /* already dead */ }
|
|
791
|
+
}
|
|
792
|
+
await this.sleep(2000);
|
|
793
|
+
for (const server of allServers) {
|
|
794
|
+
try {
|
|
795
|
+
process.kill(server.pid, 0);
|
|
796
|
+
process.kill(server.pid, 'SIGKILL');
|
|
797
|
+
logger.info({ pid: server.pid }, '[EmbeddingServerManager] Force killed orphan embedding process');
|
|
798
|
+
} catch { /* already dead */ }
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// Clean up PID file
|
|
802
|
+
this.removePidFile();
|
|
803
|
+
// Clean up socket file
|
|
804
|
+
if (existsSync(this.socketPath)) {
|
|
805
|
+
try {
|
|
806
|
+
unlinkSync(this.socketPath);
|
|
807
|
+
}
|
|
808
|
+
catch (err) {
|
|
809
|
+
logger.debug({ error: err }, '[EmbeddingServerManager] Failed to remove socket');
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
this.process = null;
|
|
813
|
+
this.isRunning = false;
|
|
814
|
+
this.startTime = null;
|
|
815
|
+
logger.info('[EmbeddingServerManager] Server stopped');
|
|
816
|
+
this.emit('stopped', { pid: this.process?.pid });
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Perform a health check on the embedding server
|
|
820
|
+
*/
|
|
821
|
+
async healthCheck() {
|
|
822
|
+
const startTime = Date.now();
|
|
823
|
+
// Quick check: socket must exist
|
|
824
|
+
if (!existsSync(this.socketPath)) {
|
|
825
|
+
return {
|
|
826
|
+
success: false,
|
|
827
|
+
responseTimeMs: Date.now() - startTime,
|
|
828
|
+
error: 'Socket file does not exist',
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
// Ping the server
|
|
832
|
+
return new Promise((resolve) => {
|
|
833
|
+
const socket = createConnection(this.socketPath);
|
|
834
|
+
let buffer = '';
|
|
835
|
+
let resolved = false;
|
|
836
|
+
const timeout = setTimeout(() => {
|
|
837
|
+
if (!resolved) {
|
|
838
|
+
resolved = true;
|
|
839
|
+
socket.destroy();
|
|
840
|
+
resolve({
|
|
841
|
+
success: false,
|
|
842
|
+
responseTimeMs: Date.now() - startTime,
|
|
843
|
+
error: 'Health check timeout',
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}, this.config.healthCheckTimeoutMs);
|
|
847
|
+
socket.on('connect', () => {
|
|
848
|
+
// FIX: Docker server.mjs requires type field - use {"type":"health"} for health checks
|
|
849
|
+
// Python server (frankenstein-embeddings.py) accepts both {"stats":true} and {"type":"health"}
|
|
850
|
+
// Docker server.mjs ONLY accepts {"type":"health"} - so use that for compatibility with both
|
|
851
|
+
socket.write(JSON.stringify({ type: 'health' }) + '\n');
|
|
852
|
+
});
|
|
853
|
+
socket.on('data', (data) => {
|
|
854
|
+
buffer += data.toString();
|
|
855
|
+
const newlineIndex = buffer.indexOf('\n');
|
|
856
|
+
if (newlineIndex !== -1) {
|
|
857
|
+
clearTimeout(timeout);
|
|
858
|
+
if (resolved)
|
|
859
|
+
return;
|
|
860
|
+
resolved = true;
|
|
861
|
+
try {
|
|
862
|
+
const response = JSON.parse(buffer.slice(0, newlineIndex));
|
|
863
|
+
socket.end();
|
|
864
|
+
// Check for error response first
|
|
865
|
+
if (response.error) {
|
|
866
|
+
resolve({
|
|
867
|
+
success: false,
|
|
868
|
+
responseTimeMs: Date.now() - startTime,
|
|
869
|
+
error: `Server error: ${response.error}`,
|
|
870
|
+
});
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
// Handle both Python (capabilities) and Docker (native_dimensions) response formats
|
|
874
|
+
// Python frankenstein-embeddings.py: { capabilities: { native_dims, target_dims }, ... }
|
|
875
|
+
// Docker server.mjs: { status: "healthy", native_dimensions, target_dimensions, ... }
|
|
876
|
+
if (response.capabilities) {
|
|
877
|
+
// Python format
|
|
878
|
+
resolve({
|
|
879
|
+
success: true,
|
|
880
|
+
responseTimeMs: Date.now() - startTime,
|
|
881
|
+
dimensions: {
|
|
882
|
+
native: response.capabilities.native_dims,
|
|
883
|
+
target: response.capabilities.target_dims,
|
|
884
|
+
},
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
else if (response.native_dimensions !== undefined) {
|
|
888
|
+
// Docker format - check for healthy status
|
|
889
|
+
const isHealthy = response.status === 'healthy' || response.status === 'ok';
|
|
890
|
+
resolve({
|
|
891
|
+
success: isHealthy,
|
|
892
|
+
responseTimeMs: Date.now() - startTime,
|
|
893
|
+
dimensions: {
|
|
894
|
+
native: response.native_dimensions,
|
|
895
|
+
target: response.target_dimensions,
|
|
896
|
+
},
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
// Fallback for other response formats - assume healthy if no error
|
|
901
|
+
resolve({
|
|
902
|
+
success: true,
|
|
903
|
+
responseTimeMs: Date.now() - startTime,
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
catch (parseErr) {
|
|
908
|
+
socket.end();
|
|
909
|
+
resolve({
|
|
910
|
+
success: false,
|
|
911
|
+
responseTimeMs: Date.now() - startTime,
|
|
912
|
+
error: 'Invalid response from server',
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
socket.on('error', (err) => {
|
|
918
|
+
clearTimeout(timeout);
|
|
919
|
+
if (!resolved) {
|
|
920
|
+
resolved = true;
|
|
921
|
+
resolve({
|
|
922
|
+
success: false,
|
|
923
|
+
responseTimeMs: Date.now() - startTime,
|
|
924
|
+
error: `Socket error: ${err.message}`,
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Get current server status
|
|
932
|
+
*/
|
|
933
|
+
getStatus() {
|
|
934
|
+
const pid = this.readPidFile();
|
|
935
|
+
const socketExists = existsSync(this.socketPath);
|
|
936
|
+
return {
|
|
937
|
+
running: this.isRunning,
|
|
938
|
+
pid,
|
|
939
|
+
socketPath: this.socketPath,
|
|
940
|
+
socketExists,
|
|
941
|
+
healthy: this.isRunning && socketExists && this.consecutiveFailures === 0,
|
|
942
|
+
lastHealthCheck: this.healthCheckTimer ? Date.now() : null,
|
|
943
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
944
|
+
restartCount: this.restartCount,
|
|
945
|
+
startTime: this.startTime,
|
|
946
|
+
uptime: this.startTime ? Date.now() - this.startTime : null,
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Shutdown - called when MCP server is shutting down
|
|
951
|
+
*/
|
|
952
|
+
async shutdown() {
|
|
953
|
+
await this.stop();
|
|
954
|
+
this.removeAllListeners();
|
|
955
|
+
}
|
|
956
|
+
// ==========================================================================
|
|
957
|
+
// PHASE 4: USER-INITIATED START/STOP + RESTART LOOP DETECTION
|
|
958
|
+
// ==========================================================================
|
|
959
|
+
/**
|
|
960
|
+
* Check if user manually stopped the server
|
|
961
|
+
* Returns true if the stopped flag file exists
|
|
962
|
+
*/
|
|
963
|
+
isStoppedByUser() {
|
|
964
|
+
return existsSync(this.stoppedFlagPath);
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Check if the embedding server died due to KYS watchdog (no heartbeat from MCP)
|
|
968
|
+
* Returns true if death reason file exists and contains "kys"
|
|
969
|
+
*/
|
|
970
|
+
wasKilledByKYS() {
|
|
971
|
+
const deathReasonPath = join(dirname(this.socketPath), 'embedding-death-reason.txt');
|
|
972
|
+
if (!existsSync(deathReasonPath)) {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
try {
|
|
976
|
+
const content = readFileSync(deathReasonPath, 'utf8');
|
|
977
|
+
return content.startsWith('kys');
|
|
978
|
+
}
|
|
979
|
+
catch {
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Clear the KYS death reason file (called after successful respawn)
|
|
985
|
+
*/
|
|
986
|
+
clearKYSDeathReason() {
|
|
987
|
+
const deathReasonPath = join(dirname(this.socketPath), 'embedding-death-reason.txt');
|
|
988
|
+
if (existsSync(deathReasonPath)) {
|
|
989
|
+
try {
|
|
990
|
+
unlinkSync(deathReasonPath);
|
|
991
|
+
logger.info('[EmbeddingServerManager] Cleared KYS death reason file');
|
|
992
|
+
}
|
|
993
|
+
catch (err) {
|
|
994
|
+
logger.warn({ err }, '[EmbeddingServerManager] Failed to clear KYS death reason file');
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Auto-respawn if server was killed by KYS watchdog
|
|
1000
|
+
* This is called when a socket connection fails - if KYS was the cause,
|
|
1001
|
+
* we respawn the server and return true so caller can retry
|
|
1002
|
+
*/
|
|
1003
|
+
async autoRespawnIfKYSDeath() {
|
|
1004
|
+
if (!this.wasKilledByKYS()) {
|
|
1005
|
+
return false;
|
|
1006
|
+
}
|
|
1007
|
+
logger.info('[EmbeddingServerManager] Server was killed by KYS watchdog - auto-respawning');
|
|
1008
|
+
// Clear the death reason so we don't loop
|
|
1009
|
+
this.clearKYSDeathReason();
|
|
1010
|
+
// Clear stopped flag in case it was set
|
|
1011
|
+
this.setStoppedByUser(false);
|
|
1012
|
+
// Reset failure counters
|
|
1013
|
+
this.consecutiveFailures = 0;
|
|
1014
|
+
this.restartCount = 0;
|
|
1015
|
+
// Force a fresh start
|
|
1016
|
+
try {
|
|
1017
|
+
await this.start();
|
|
1018
|
+
logger.info('[EmbeddingServerManager] Auto-respawn complete after KYS death');
|
|
1019
|
+
return true;
|
|
1020
|
+
}
|
|
1021
|
+
catch (err) {
|
|
1022
|
+
logger.error({ err }, '[EmbeddingServerManager] Auto-respawn failed after KYS death');
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Set the stopped-by-user flag
|
|
1028
|
+
* When true, prevents auto-restart
|
|
1029
|
+
*/
|
|
1030
|
+
setStoppedByUser(stopped) {
|
|
1031
|
+
if (stopped) {
|
|
1032
|
+
writeFileSync(this.stoppedFlagPath, Date.now().toString(), 'utf8');
|
|
1033
|
+
logger.info('[EmbeddingServerManager] Set stopped-by-user flag');
|
|
1034
|
+
}
|
|
1035
|
+
else if (existsSync(this.stoppedFlagPath)) {
|
|
1036
|
+
unlinkSync(this.stoppedFlagPath);
|
|
1037
|
+
logger.info('[EmbeddingServerManager] Cleared stopped-by-user flag');
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* User-initiated stop - sets flag to prevent auto-restart
|
|
1042
|
+
* Use this when user explicitly wants to stop the embedding server
|
|
1043
|
+
*/
|
|
1044
|
+
async userStop() {
|
|
1045
|
+
logger.info('[EmbeddingServerManager] User-initiated stop');
|
|
1046
|
+
// Set flag BEFORE stopping to prevent health check restart race
|
|
1047
|
+
this.setStoppedByUser(true);
|
|
1048
|
+
await this.stop();
|
|
1049
|
+
return {
|
|
1050
|
+
success: true,
|
|
1051
|
+
message: 'Embedding server stopped. Auto-restart disabled. Use userStart() to restart.'
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* User-initiated start - clears stopped flag and does hard restart
|
|
1056
|
+
* Use this when user explicitly wants to (re)start the embedding server
|
|
1057
|
+
*/
|
|
1058
|
+
async userStart() {
|
|
1059
|
+
logger.info('[EmbeddingServerManager] User-initiated start');
|
|
1060
|
+
// Clear the stopped flag
|
|
1061
|
+
this.setStoppedByUser(false);
|
|
1062
|
+
// Reset restart counter for fresh start
|
|
1063
|
+
this.restartCount = 0;
|
|
1064
|
+
this.restartTimestamps = [];
|
|
1065
|
+
this.consecutiveFailures = 0;
|
|
1066
|
+
this.isShuttingDown = false;
|
|
1067
|
+
// Kill existing and start fresh
|
|
1068
|
+
await this.stop();
|
|
1069
|
+
// Clear the shutdown flag that stop() sets
|
|
1070
|
+
this.isShuttingDown = false;
|
|
1071
|
+
const success = await this.start();
|
|
1072
|
+
if (success) {
|
|
1073
|
+
// Start health monitoring if not already running
|
|
1074
|
+
this.startHealthMonitoring();
|
|
1075
|
+
// NOTE: KYS heartbeat already started inside start() method
|
|
1076
|
+
// No need to start it again here
|
|
1077
|
+
}
|
|
1078
|
+
return {
|
|
1079
|
+
success,
|
|
1080
|
+
message: success
|
|
1081
|
+
? 'Embedding server started successfully'
|
|
1082
|
+
: 'Failed to start embedding server - check logs for details'
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Get restart loop detection info
|
|
1087
|
+
* Detects if we're in a restart loop (>3 restarts in 60 seconds)
|
|
1088
|
+
*/
|
|
1089
|
+
getRestartLoopInfo() {
|
|
1090
|
+
const windowMs = 60000; // 60 seconds
|
|
1091
|
+
const loopThreshold = 3;
|
|
1092
|
+
const now = Date.now();
|
|
1093
|
+
// Count restarts in the last 60 seconds
|
|
1094
|
+
const recentRestarts = this.restartTimestamps.filter(ts => now - ts < windowMs).length;
|
|
1095
|
+
return {
|
|
1096
|
+
inLoop: recentRestarts >= loopThreshold,
|
|
1097
|
+
recentRestarts,
|
|
1098
|
+
maxAttempts: this.config.maxRestartAttempts,
|
|
1099
|
+
windowSeconds: 60,
|
|
1100
|
+
restartCount: this.restartCount
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Get extended status including stopped-by-user flag and restart loop info
|
|
1105
|
+
*/
|
|
1106
|
+
getExtendedStatus() {
|
|
1107
|
+
const graceActive = this.startupGraceUntil > 0 && Date.now() < this.startupGraceUntil;
|
|
1108
|
+
return {
|
|
1109
|
+
...this.getStatus(),
|
|
1110
|
+
stoppedByUser: this.isStoppedByUser(),
|
|
1111
|
+
restartLoop: this.getRestartLoopInfo(),
|
|
1112
|
+
startupGrace: graceActive ? {
|
|
1113
|
+
active: true,
|
|
1114
|
+
remainingMs: this.startupGraceUntil - Date.now(),
|
|
1115
|
+
totalMs: this.config.startupTimeoutMs
|
|
1116
|
+
} : { active: false }
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
// ==========================================================================
|
|
1120
|
+
// PRIVATE METHODS
|
|
1121
|
+
// ==========================================================================
|
|
1122
|
+
/**
|
|
1123
|
+
* Kill any stale embedding processes from previous runs
|
|
1124
|
+
* Uses robust process age checking to verify we're killing the right process
|
|
1125
|
+
*/
|
|
1126
|
+
async killStaleProcesses() {
|
|
1127
|
+
logger.info('[EmbeddingServerManager] Checking for stale processes...');
|
|
1128
|
+
// Step 0: KILL ANY ROGUE DOCKER EMBEDDING CONTAINERS
|
|
1129
|
+
// Docker containers crash-loop if they can't bind to the socket because
|
|
1130
|
+
// the native Python server already owns it. Clean them up FIRST.
|
|
1131
|
+
try {
|
|
1132
|
+
const projectDirName = require('path').basename(this.projectPath).toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
1133
|
+
const containerName = `specmem-embedding-${projectDirName}`;
|
|
1134
|
+
// Check if container exists (running or stopped)
|
|
1135
|
+
const checkCmd = `docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${containerName}$"`;
|
|
1136
|
+
try {
|
|
1137
|
+
execSync(checkCmd, { stdio: 'pipe' });
|
|
1138
|
+
// Container exists - remove it forcefully
|
|
1139
|
+
logger.info({ containerName }, '[EmbeddingServerManager] Removing rogue Docker embedding container');
|
|
1140
|
+
execSync(`docker rm -f ${containerName} 2>/dev/null`, { stdio: 'pipe' });
|
|
1141
|
+
logger.info({ containerName }, '[EmbeddingServerManager] Rogue Docker container removed');
|
|
1142
|
+
}
|
|
1143
|
+
catch {
|
|
1144
|
+
// Container doesn't exist - good
|
|
1145
|
+
logger.debug({ containerName }, '[EmbeddingServerManager] No rogue Docker container found');
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
catch (err) {
|
|
1149
|
+
logger.debug({ error: err?.message || err }, '[EmbeddingServerManager] Docker cleanup check failed (Docker may not be installed)');
|
|
1150
|
+
}
|
|
1151
|
+
// Step 1: Check PID file with health check
|
|
1152
|
+
const healthInfo = checkProcessHealth({
|
|
1153
|
+
pidFilePath: this.pidFilePath,
|
|
1154
|
+
maxAgeHours: this.config.maxProcessAgeHours,
|
|
1155
|
+
expectedProcessName: 'frankenstein-embeddings',
|
|
1156
|
+
projectPath: this.projectPath,
|
|
1157
|
+
});
|
|
1158
|
+
if (healthInfo) {
|
|
1159
|
+
logger.info({
|
|
1160
|
+
pid: healthInfo.pid,
|
|
1161
|
+
processExists: healthInfo.processExists,
|
|
1162
|
+
isEmbeddingServer: healthInfo.isEmbeddingServer,
|
|
1163
|
+
ageHours: healthInfo.processAgeHours?.toFixed(2) || 'unknown',
|
|
1164
|
+
pidFileAgeHours: healthInfo.pidFileAgeHours.toFixed(2),
|
|
1165
|
+
isStale: healthInfo.isStale,
|
|
1166
|
+
recommendedAction: healthInfo.recommendedAction,
|
|
1167
|
+
statusMessage: healthInfo.statusMessage,
|
|
1168
|
+
}, '[EmbeddingServerManager] Process health check result');
|
|
1169
|
+
// Take action based on health check
|
|
1170
|
+
// CRITICAL FIX: Only kill if process belongs to THIS project's socket path
|
|
1171
|
+
// Check process's SPECMEM_EMBEDDING_SOCKET env var (not cmdline - socket isn't in cmdline!)
|
|
1172
|
+
const processSocketPath = healthInfo.pid ? this.getProcessSocketPath(healthInfo.pid) : null;
|
|
1173
|
+
const belongsToThisProject = processSocketPath === this.socketPath;
|
|
1174
|
+
if (!belongsToThisProject && processSocketPath) {
|
|
1175
|
+
logger.info({
|
|
1176
|
+
pid: healthInfo.pid,
|
|
1177
|
+
thisProjectSocket: this.socketPath,
|
|
1178
|
+
processSocket: processSocketPath,
|
|
1179
|
+
}, '[EmbeddingServerManager] Process belongs to different project - skipping kill');
|
|
1180
|
+
// Don't kill processes from other projects - just clean up our PID file
|
|
1181
|
+
this.removePidFile();
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
if (healthInfo.recommendedAction === 'kill') {
|
|
1185
|
+
await this.killProcessWithHealthInfo(healthInfo);
|
|
1186
|
+
}
|
|
1187
|
+
else if (healthInfo.recommendedAction === 'investigate') {
|
|
1188
|
+
logger.warn({
|
|
1189
|
+
pid: healthInfo.pid,
|
|
1190
|
+
commandLine: healthInfo.commandLine,
|
|
1191
|
+
message: 'Process exists but may not be embedding server - killing to be safe',
|
|
1192
|
+
}, '[EmbeddingServerManager] Suspicious process found');
|
|
1193
|
+
await this.killProcessWithHealthInfo(healthInfo);
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
// CRITICAL FIX: Don't kill healthy servers - REUSE them!
|
|
1197
|
+
// This allows multiple MCP sessions to share the same embedding server
|
|
1198
|
+
// Killing a healthy server breaks other sessions that depend on it
|
|
1199
|
+
logger.info({
|
|
1200
|
+
pid: healthInfo.pid,
|
|
1201
|
+
ageHours: healthInfo.processAgeHours?.toFixed(2) || 'unknown',
|
|
1202
|
+
}, '[EmbeddingServerManager] Process is healthy - REUSING instead of killing (preserves other sessions)');
|
|
1203
|
+
// Mark as running so we don't try to start a new one
|
|
1204
|
+
this.isRunning = true;
|
|
1205
|
+
this.process = { pid: healthInfo.pid }; // Track the existing process
|
|
1206
|
+
return; // Don't kill, don't clean socket - just reuse
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
logger.debug('[EmbeddingServerManager] No PID file found, checking for orphaned processes');
|
|
1211
|
+
}
|
|
1212
|
+
// Step 2: Also check for orphaned processes (no PID file but process exists)
|
|
1213
|
+
await this.killOrphanedProcesses();
|
|
1214
|
+
// Step 3: Clean up old socket if exists
|
|
1215
|
+
if (existsSync(this.socketPath)) {
|
|
1216
|
+
try {
|
|
1217
|
+
unlinkSync(this.socketPath);
|
|
1218
|
+
logger.debug('[EmbeddingServerManager] Removed stale socket');
|
|
1219
|
+
}
|
|
1220
|
+
catch (err) {
|
|
1221
|
+
logger.debug({ error: err }, '[EmbeddingServerManager] Failed to remove stale socket');
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
// Step 4: Clean up PID file if exists
|
|
1225
|
+
this.removePidFile();
|
|
1226
|
+
logger.info('[EmbeddingServerManager] Stale process cleanup complete');
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Kill a process using health info metadata
|
|
1230
|
+
*/
|
|
1231
|
+
async killProcessWithHealthInfo(healthInfo) {
|
|
1232
|
+
const { pid, processAgeHours, pidFileAgeHours } = healthInfo;
|
|
1233
|
+
if (!healthInfo.processExists) {
|
|
1234
|
+
logger.debug({ pid }, '[EmbeddingServerManager] Process does not exist, nothing to kill');
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
try {
|
|
1238
|
+
logger.info({
|
|
1239
|
+
pid,
|
|
1240
|
+
processAge: processAgeHours?.toFixed(2) || 'unknown',
|
|
1241
|
+
pidFileAge: pidFileAgeHours.toFixed(2),
|
|
1242
|
+
commandLine: healthInfo.commandLine,
|
|
1243
|
+
}, '[EmbeddingServerManager] Killing process');
|
|
1244
|
+
// Try graceful SIGTERM first
|
|
1245
|
+
process.kill(pid, 'SIGTERM');
|
|
1246
|
+
await this.sleep(500);
|
|
1247
|
+
// Check if still running
|
|
1248
|
+
try {
|
|
1249
|
+
process.kill(pid, 0);
|
|
1250
|
+
// Still running - force kill
|
|
1251
|
+
process.kill(pid, 'SIGKILL');
|
|
1252
|
+
logger.debug({ pid }, '[EmbeddingServerManager] Force killed process with SIGKILL');
|
|
1253
|
+
}
|
|
1254
|
+
catch {
|
|
1255
|
+
// Already dead - good
|
|
1256
|
+
logger.debug({ pid }, '[EmbeddingServerManager] Process terminated gracefully');
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
catch (err) {
|
|
1260
|
+
logger.debug({ pid, error: err }, '[EmbeddingServerManager] Failed to kill process (may already be dead)');
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Find and kill orphaned embedding processes (no PID file)
|
|
1265
|
+
* Kills zombie frankenstein processes NOT tracked by any project's PID file
|
|
1266
|
+
* Preserves processes actively used by other SpecMem instances
|
|
1267
|
+
*
|
|
1268
|
+
* CRITICAL FIX: Socket path is passed via ENVIRONMENT VARIABLES, not command line!
|
|
1269
|
+
* So we find ALL frankenstein processes, then filter by checking /proc/PID/environ
|
|
1270
|
+
*/
|
|
1271
|
+
async killOrphanedProcesses() {
|
|
1272
|
+
try {
|
|
1273
|
+
// FIX: Find ALL frankenstein processes (socket path is in env, not cmdline)
|
|
1274
|
+
// Then filter by checking each process's SPECMEM_EMBEDDING_SOCKET env var
|
|
1275
|
+
const result = execSync(`pgrep -f "frankenstein-embeddings.py" 2>/dev/null || true`, { encoding: 'utf8' }).trim();
|
|
1276
|
+
// Get ALL known PID files from ALL possible locations
|
|
1277
|
+
// These are processes actively tracked by running instances
|
|
1278
|
+
const trackedPids = new Set();
|
|
1279
|
+
// Search common PID file locations more comprehensively
|
|
1280
|
+
try {
|
|
1281
|
+
const pidFiles = execSync(`find /tmp ~/.specmem /specmem -name "embedding.pid" 2>/dev/null | xargs cat 2>/dev/null || true`, { encoding: 'utf8' }).trim();
|
|
1282
|
+
// PID files format: {PID}:{TIMESTAMP}
|
|
1283
|
+
for (const line of pidFiles.split('\n')) {
|
|
1284
|
+
if (!line.trim())
|
|
1285
|
+
continue;
|
|
1286
|
+
const pid = parseInt(line.split(':')[0], 10);
|
|
1287
|
+
if (!isNaN(pid))
|
|
1288
|
+
trackedPids.add(pid);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
catch { /* no pid files - all are orphans */ }
|
|
1292
|
+
// CRITICAL FIX: Also search for PID files in ANY project path
|
|
1293
|
+
// This catches project-specific socket directories like /newServer/specmem/sockets/
|
|
1294
|
+
try {
|
|
1295
|
+
const allProjectPids = execSync(`find / -path "*/specmem/sockets/embedding.pid" -o -path "*/.specmem/*/sockets/embedding.pid" 2>/dev/null | xargs cat 2>/dev/null || true`, { encoding: 'utf8', timeout: 5000 }).trim();
|
|
1296
|
+
for (const line of allProjectPids.split('\n')) {
|
|
1297
|
+
if (!line.trim())
|
|
1298
|
+
continue;
|
|
1299
|
+
const pid = parseInt(line.split(':')[0], 10);
|
|
1300
|
+
if (!isNaN(pid))
|
|
1301
|
+
trackedPids.add(pid);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
catch { /* filesystem search may be slow or fail - that's ok */ }
|
|
1305
|
+
if (result) {
|
|
1306
|
+
const pids = result.split('\n').filter(p => p.trim());
|
|
1307
|
+
const orphanPids = pids.filter(p => !trackedPids.has(parseInt(p, 10)));
|
|
1308
|
+
if (orphanPids.length > 0) {
|
|
1309
|
+
logger.info({
|
|
1310
|
+
orphanPids,
|
|
1311
|
+
trackedCount: trackedPids.size,
|
|
1312
|
+
totalFound: pids.length,
|
|
1313
|
+
thisProjectSocket: this.socketPath,
|
|
1314
|
+
}, '[EmbeddingServerManager] Found orphaned processes for THIS project (not tracked by PID file)');
|
|
1315
|
+
}
|
|
1316
|
+
for (const pidStr of orphanPids) {
|
|
1317
|
+
const pid = parseInt(pidStr, 10);
|
|
1318
|
+
if (isNaN(pid))
|
|
1319
|
+
continue;
|
|
1320
|
+
// Skip if tracked by another project
|
|
1321
|
+
if (trackedPids.has(pid)) {
|
|
1322
|
+
logger.debug({ pid }, '[EmbeddingServerManager] Skipping - tracked by another project');
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
try {
|
|
1326
|
+
// Get process info before killing
|
|
1327
|
+
const healthInfo = this.getProcessInfoForOrphan(pid);
|
|
1328
|
+
const ageHours = healthInfo?.ageHours ?? null;
|
|
1329
|
+
const commandLine = healthInfo?.commandLine || '';
|
|
1330
|
+
// CRITICAL FIX: Double-check that process belongs to THIS project
|
|
1331
|
+
// First try reading socket path from process environment (most reliable)
|
|
1332
|
+
const processSocketPath = this.getProcessSocketPath(pid);
|
|
1333
|
+
if (processSocketPath && processSocketPath !== this.socketPath) {
|
|
1334
|
+
logger.warn({
|
|
1335
|
+
pid,
|
|
1336
|
+
processSocketPath,
|
|
1337
|
+
thisProjectSocket: this.socketPath,
|
|
1338
|
+
}, '[EmbeddingServerManager] SAFETY CHECK: Process socket path does not match this project - skipping kill');
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
// Fallback: verify socket path is in command line
|
|
1342
|
+
if (!processSocketPath && !commandLine.includes(this.socketPath)) {
|
|
1343
|
+
logger.warn({
|
|
1344
|
+
pid,
|
|
1345
|
+
commandLine,
|
|
1346
|
+
thisProjectSocket: this.socketPath,
|
|
1347
|
+
}, '[EmbeddingServerManager] SAFETY CHECK: Process command line does not match this project - skipping kill');
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
// Only kill if older than max age
|
|
1351
|
+
if (ageHours !== null && ageHours <= this.config.maxProcessAgeHours) {
|
|
1352
|
+
logger.info({
|
|
1353
|
+
pid,
|
|
1354
|
+
ageHours: ageHours.toFixed(2),
|
|
1355
|
+
maxAgeHours: this.config.maxProcessAgeHours,
|
|
1356
|
+
socketPath: this.socketPath,
|
|
1357
|
+
}, '[EmbeddingServerManager] Orphaned process is recent and belongs to this project, keeping it');
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
logger.info({
|
|
1361
|
+
pid,
|
|
1362
|
+
ageHours: ageHours?.toFixed(2) || 'unknown',
|
|
1363
|
+
maxAgeHours: this.config.maxProcessAgeHours,
|
|
1364
|
+
commandLine: healthInfo?.commandLine || 'unknown',
|
|
1365
|
+
socketPath: this.socketPath,
|
|
1366
|
+
}, '[EmbeddingServerManager] Killing stale orphaned process for THIS project (zombie)');
|
|
1367
|
+
process.kill(pid, 'SIGTERM');
|
|
1368
|
+
await this.sleep(200);
|
|
1369
|
+
// Force kill if still running
|
|
1370
|
+
try {
|
|
1371
|
+
process.kill(pid, 0);
|
|
1372
|
+
process.kill(pid, 'SIGKILL');
|
|
1373
|
+
}
|
|
1374
|
+
catch {
|
|
1375
|
+
// Already dead
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
catch (err) {
|
|
1379
|
+
logger.debug({ pid, error: err }, '[EmbeddingServerManager] Failed to kill orphaned process');
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
catch (err) {
|
|
1385
|
+
// pgrep may not exist or may fail - that's OK
|
|
1386
|
+
logger.debug({ error: err }, '[EmbeddingServerManager] pgrep failed (may not exist)');
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Get the SPECMEM_EMBEDDING_SOCKET env var from a running process
|
|
1391
|
+
* Returns the socket path this process is bound to, or null if not found
|
|
1392
|
+
*/
|
|
1393
|
+
getProcessSocketPath(pid) {
|
|
1394
|
+
try {
|
|
1395
|
+
const environPath = `/proc/${pid}/environ`;
|
|
1396
|
+
if (!existsSync(environPath)) {
|
|
1397
|
+
return null;
|
|
1398
|
+
}
|
|
1399
|
+
const environ = readFileSync(environPath, 'utf8');
|
|
1400
|
+
// Environment variables are null-separated
|
|
1401
|
+
const envVars = environ.split('\0');
|
|
1402
|
+
for (const envVar of envVars) {
|
|
1403
|
+
if (envVar.startsWith('SPECMEM_EMBEDDING_SOCKET=')) {
|
|
1404
|
+
return envVar.split('=')[1];
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
return null;
|
|
1408
|
+
}
|
|
1409
|
+
catch {
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Get process info for an orphaned process (no PID file)
|
|
1415
|
+
*/
|
|
1416
|
+
getProcessInfoForOrphan(pid) {
|
|
1417
|
+
try {
|
|
1418
|
+
// Try to read /proc/[pid]/stat for start time
|
|
1419
|
+
const statPath = `/proc/${pid}/stat`;
|
|
1420
|
+
if (!existsSync(statPath)) {
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
const statContent = readFileSync(statPath, 'utf8');
|
|
1424
|
+
const match = statContent.match(/\(.*?\)\s+(.*)$/);
|
|
1425
|
+
if (!match) {
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
const fields = match[1].split(/\s+/);
|
|
1429
|
+
const startTimeJiffies = parseInt(fields[19], 10);
|
|
1430
|
+
if (!isNaN(startTimeJiffies)) {
|
|
1431
|
+
// Simple age calculation (not fully accurate but good enough)
|
|
1432
|
+
const uptimeContent = readFileSync('/proc/uptime', 'utf8');
|
|
1433
|
+
const uptimeSeconds = parseFloat(uptimeContent.split(/\s+/)[0]);
|
|
1434
|
+
const clockTicks = 100; // Standard for most systems
|
|
1435
|
+
const processStartSeconds = startTimeJiffies / clockTicks;
|
|
1436
|
+
const processAgeSeconds = uptimeSeconds - processStartSeconds;
|
|
1437
|
+
const ageHours = processAgeSeconds / 3600;
|
|
1438
|
+
// Get command line
|
|
1439
|
+
const cmdlinePath = `/proc/${pid}/cmdline`;
|
|
1440
|
+
let commandLine = null;
|
|
1441
|
+
if (existsSync(cmdlinePath)) {
|
|
1442
|
+
commandLine = readFileSync(cmdlinePath, 'utf8').replace(/\0/g, ' ').trim();
|
|
1443
|
+
}
|
|
1444
|
+
return { ageHours, commandLine };
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
catch (err) {
|
|
1448
|
+
logger.debug({ pid, error: err }, '[EmbeddingServerManager] Failed to get orphan process info');
|
|
1449
|
+
}
|
|
1450
|
+
return null;
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Kill process by PID file (using robust health check)
|
|
1454
|
+
*/
|
|
1455
|
+
async killByPidFile() {
|
|
1456
|
+
const healthInfo = checkProcessHealth({
|
|
1457
|
+
pidFilePath: this.pidFilePath,
|
|
1458
|
+
maxAgeHours: this.config.maxProcessAgeHours,
|
|
1459
|
+
expectedProcessName: 'frankenstein-embeddings',
|
|
1460
|
+
projectPath: this.projectPath,
|
|
1461
|
+
});
|
|
1462
|
+
if (!healthInfo) {
|
|
1463
|
+
logger.debug('[EmbeddingServerManager] No PID file found');
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
logger.debug({
|
|
1467
|
+
pid: healthInfo.pid,
|
|
1468
|
+
processExists: healthInfo.processExists,
|
|
1469
|
+
isEmbeddingServer: healthInfo.isEmbeddingServer,
|
|
1470
|
+
ageHours: healthInfo.processAgeHours?.toFixed(2) || 'unknown',
|
|
1471
|
+
recommendedAction: healthInfo.recommendedAction,
|
|
1472
|
+
statusMessage: healthInfo.statusMessage,
|
|
1473
|
+
}, '[EmbeddingServerManager] Checked PID file process');
|
|
1474
|
+
if (healthInfo.processExists) {
|
|
1475
|
+
await this.killProcessWithHealthInfo(healthInfo);
|
|
1476
|
+
}
|
|
1477
|
+
else {
|
|
1478
|
+
logger.debug({ pid: healthInfo.pid }, '[EmbeddingServerManager] Process from PID file no longer exists');
|
|
1479
|
+
}
|
|
1480
|
+
this.removePidFile();
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Find the embedding script path
|
|
1484
|
+
*
|
|
1485
|
+
* PRIORITY: frankenstein-embeddings.py (has ALL 4 optimizations + ACK verification)
|
|
1486
|
+
* > warm-start.sh (Docker)
|
|
1487
|
+
*
|
|
1488
|
+
* We NEVER use a model that hasn't been optimized with all 4 optimizations.
|
|
1489
|
+
*/
|
|
1490
|
+
findEmbeddingScript() {
|
|
1491
|
+
// specmem root dir is 2 levels up from src/mcp/
|
|
1492
|
+
const specmemRoot = dirname(dirname(__dirname));
|
|
1493
|
+
// PRIORITY 1: frankenstein-embeddings.py (OPTIMIZED - has all 4 optimizations + ACK verification)
|
|
1494
|
+
const embeddingPaths = [
|
|
1495
|
+
// Project-local (development)
|
|
1496
|
+
join(this.projectPath, 'embedding-sandbox', 'frankenstein-embeddings.py'),
|
|
1497
|
+
// SpecMem package root (via __dirname - works for all installs)
|
|
1498
|
+
join(specmemRoot, 'embedding-sandbox', 'frankenstein-embeddings.py'),
|
|
1499
|
+
// Local npm install
|
|
1500
|
+
join(this.projectPath, 'node_modules', 'specmem-hardwicksoftware', 'embedding-sandbox', 'frankenstein-embeddings.py'),
|
|
1501
|
+
// Global npm install fallback (platform-agnostic)
|
|
1502
|
+
join(dirname(dirname(process.execPath)), 'lib', 'node_modules', 'specmem-hardwicksoftware', 'embedding-sandbox', 'frankenstein-embeddings.py'),
|
|
1503
|
+
];
|
|
1504
|
+
for (const p of embeddingPaths) {
|
|
1505
|
+
if (existsSync(p)) {
|
|
1506
|
+
logger.info({ path: p }, '[EmbeddingServerManager] Using frankenstein-embeddings.py (all 4 optimizations + ACK verification)');
|
|
1507
|
+
return { script: p, useWarmStart: false };
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
// PRIORITY 2: warm-start.sh (Docker-based)
|
|
1511
|
+
const warmStartPaths = [
|
|
1512
|
+
join(this.projectPath, 'embedding-sandbox', 'warm-start.sh'),
|
|
1513
|
+
join(specmemRoot, 'embedding-sandbox', 'warm-start.sh'),
|
|
1514
|
+
join(this.projectPath, 'node_modules', 'specmem-hardwicksoftware', 'embedding-sandbox', 'warm-start.sh'),
|
|
1515
|
+
join(dirname(dirname(process.execPath)), 'lib', 'node_modules', 'specmem-hardwicksoftware', 'embedding-sandbox', 'warm-start.sh'),
|
|
1516
|
+
];
|
|
1517
|
+
for (const p of warmStartPaths) {
|
|
1518
|
+
if (existsSync(p)) {
|
|
1519
|
+
logger.info({ path: p }, '[EmbeddingServerManager] Using warm-start.sh (Docker mode)');
|
|
1520
|
+
return { script: p, useWarmStart: true };
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
logger.error({ searchedPaths: [...embeddingPaths, ...warmStartPaths] }, '[EmbeddingServerManager] Embedding script not found');
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Wait for socket file to appear AND server to be ready
|
|
1528
|
+
* FIX: Task #12 - Race condition fix: Poll with health checks instead of just file existence
|
|
1529
|
+
* The 60s timeout window was creating a race where socket file exists but server not ready
|
|
1530
|
+
*/
|
|
1531
|
+
async waitForSocket() {
|
|
1532
|
+
const startTime = Date.now();
|
|
1533
|
+
const fileCheckInterval = 200; // fast initial polling for file
|
|
1534
|
+
// FIX: Increased health check interval to account for health check timeout (10s default)
|
|
1535
|
+
// Previous 500ms was too aggressive - health check itself takes up to 10s
|
|
1536
|
+
const healthCheckInterval = 1000; // 1 second between health check attempts
|
|
1537
|
+
// FIX: Removed fixed maxHealthCheckAttempts - now uses time-based cutoff only
|
|
1538
|
+
// This ensures we use the full startupTimeoutMs window instead of arbitrary attempt limit
|
|
1539
|
+
logger.debug({ socketPath: this.socketPath, timeoutMs: this.config.startupTimeoutMs }, '[EmbeddingServerManager] Waiting for socket with readiness polling');
|
|
1540
|
+
// Phase 1: Wait for socket file to appear (use 50% of timeout for file appearance)
|
|
1541
|
+
const fileWaitDeadline = startTime + (this.config.startupTimeoutMs * 0.5);
|
|
1542
|
+
while (Date.now() < fileWaitDeadline) {
|
|
1543
|
+
if (existsSync(this.socketPath)) {
|
|
1544
|
+
logger.debug({ elapsed: Date.now() - startTime }, '[EmbeddingServerManager] Socket file appeared, starting health check polling');
|
|
1545
|
+
break;
|
|
1546
|
+
}
|
|
1547
|
+
await this.sleep(fileCheckInterval);
|
|
1548
|
+
}
|
|
1549
|
+
if (!existsSync(this.socketPath)) {
|
|
1550
|
+
logger.error({ elapsed: Date.now() - startTime, timeoutMs: this.config.startupTimeoutMs }, '[EmbeddingServerManager] Socket file never appeared within timeout');
|
|
1551
|
+
return false;
|
|
1552
|
+
}
|
|
1553
|
+
// Phase 2: Poll health check until server responds
|
|
1554
|
+
// FIX: Use remaining time from startup timeout instead of fixed attempt count
|
|
1555
|
+
// This prevents the case where health checks + interval exceed the startup timeout
|
|
1556
|
+
let healthAttempts = 0;
|
|
1557
|
+
const healthCheckDeadline = startTime + this.config.startupTimeoutMs;
|
|
1558
|
+
while (Date.now() < healthCheckDeadline) {
|
|
1559
|
+
healthAttempts++;
|
|
1560
|
+
const healthResult = await this.healthCheck();
|
|
1561
|
+
if (healthResult.success) {
|
|
1562
|
+
logger.info({
|
|
1563
|
+
elapsed: Date.now() - startTime,
|
|
1564
|
+
healthAttempts,
|
|
1565
|
+
responseTimeMs: healthResult.responseTimeMs,
|
|
1566
|
+
dimensions: healthResult.dimensions,
|
|
1567
|
+
}, '[EmbeddingServerManager] Server ready - health check passed');
|
|
1568
|
+
return true;
|
|
1569
|
+
}
|
|
1570
|
+
// FIX: Check if we have enough time for another health check before sleeping
|
|
1571
|
+
const remainingTime = healthCheckDeadline - Date.now();
|
|
1572
|
+
if (remainingTime < healthCheckInterval) {
|
|
1573
|
+
logger.debug({
|
|
1574
|
+
attempt: healthAttempts,
|
|
1575
|
+
remainingMs: remainingTime,
|
|
1576
|
+
error: healthResult.error,
|
|
1577
|
+
}, '[EmbeddingServerManager] Health check failed, not enough time for another attempt');
|
|
1578
|
+
break;
|
|
1579
|
+
}
|
|
1580
|
+
logger.debug({
|
|
1581
|
+
attempt: healthAttempts,
|
|
1582
|
+
remainingMs: remainingTime,
|
|
1583
|
+
error: healthResult.error,
|
|
1584
|
+
}, '[EmbeddingServerManager] Health check failed, retrying...');
|
|
1585
|
+
await this.sleep(healthCheckInterval);
|
|
1586
|
+
}
|
|
1587
|
+
logger.error({
|
|
1588
|
+
elapsed: Date.now() - startTime,
|
|
1589
|
+
healthAttempts,
|
|
1590
|
+
socketExists: existsSync(this.socketPath),
|
|
1591
|
+
}, '[EmbeddingServerManager] Server never became ready - health checks exhausted');
|
|
1592
|
+
return false;
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Handle process exit
|
|
1596
|
+
*/
|
|
1597
|
+
handleProcessExit(code, signal) {
|
|
1598
|
+
const wasRunning = this.isRunning;
|
|
1599
|
+
this.isRunning = false;
|
|
1600
|
+
// Only clear process if we actually had one (not external server)
|
|
1601
|
+
if (this.process) {
|
|
1602
|
+
this.process = null;
|
|
1603
|
+
}
|
|
1604
|
+
if (this.isShuttingDown) {
|
|
1605
|
+
// Expected exit during shutdown
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
logger.warn({ code, signal, wasRunning }, '[EmbeddingServerManager] Process exited unexpectedly');
|
|
1609
|
+
// Attempt restart if not shutting down
|
|
1610
|
+
this.attemptRestart();
|
|
1611
|
+
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Attempt to restart the server
|
|
1614
|
+
*/
|
|
1615
|
+
async attemptRestart() {
|
|
1616
|
+
// Phase 4: Don't restart if user manually stopped
|
|
1617
|
+
if (this.isStoppedByUser()) {
|
|
1618
|
+
logger.info('[EmbeddingServerManager] Skipping restart - stopped by user');
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
// Phase 4: Check for restart loop (>3 restarts in 60 seconds)
|
|
1622
|
+
const loopInfo = this.getRestartLoopInfo();
|
|
1623
|
+
if (loopInfo.inLoop) {
|
|
1624
|
+
logger.error({
|
|
1625
|
+
recentRestarts: loopInfo.recentRestarts,
|
|
1626
|
+
windowSeconds: 60,
|
|
1627
|
+
}, '[EmbeddingServerManager] RESTART LOOP DETECTED - backing off');
|
|
1628
|
+
this.emit('restart_loop', loopInfo);
|
|
1629
|
+
// Exponential backoff: wait 2^restartCount seconds (max 5 minutes)
|
|
1630
|
+
const backoffMs = Math.min(Math.pow(2, this.restartCount) * 1000, 300000);
|
|
1631
|
+
logger.info({ backoffMs }, '[EmbeddingServerManager] Waiting for exponential backoff');
|
|
1632
|
+
await this.sleep(backoffMs);
|
|
1633
|
+
}
|
|
1634
|
+
// Check cooldown
|
|
1635
|
+
const timeSinceLastRestart = Date.now() - this.lastRestartTime;
|
|
1636
|
+
if (timeSinceLastRestart < this.config.restartCooldownMs) {
|
|
1637
|
+
const waitTime = this.config.restartCooldownMs - timeSinceLastRestart;
|
|
1638
|
+
logger.debug({ waitTime }, '[EmbeddingServerManager] Waiting for restart cooldown');
|
|
1639
|
+
await this.sleep(waitTime);
|
|
1640
|
+
}
|
|
1641
|
+
// Check max restarts
|
|
1642
|
+
if (this.restartCount >= this.config.maxRestartAttempts) {
|
|
1643
|
+
logger.error({
|
|
1644
|
+
restartCount: this.restartCount,
|
|
1645
|
+
maxAttempts: this.config.maxRestartAttempts,
|
|
1646
|
+
}, '[EmbeddingServerManager] Max restart attempts reached');
|
|
1647
|
+
this.emit('restart_failed', { attempts: this.restartCount });
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
this.restartCount++;
|
|
1651
|
+
this.lastRestartTime = Date.now();
|
|
1652
|
+
// Phase 4: Track restart timestamp for loop detection
|
|
1653
|
+
this.restartTimestamps.push(Date.now());
|
|
1654
|
+
// Keep only last 10 timestamps
|
|
1655
|
+
if (this.restartTimestamps.length > 10) {
|
|
1656
|
+
this.restartTimestamps.shift();
|
|
1657
|
+
}
|
|
1658
|
+
logger.info({ attempt: this.restartCount }, '[EmbeddingServerManager] Attempting restart');
|
|
1659
|
+
this.emit('restarting', { attempt: this.restartCount });
|
|
1660
|
+
const success = await this.start();
|
|
1661
|
+
if (!success) {
|
|
1662
|
+
// Will retry on next health check
|
|
1663
|
+
logger.warn('[EmbeddingServerManager] Restart attempt failed');
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Start health monitoring
|
|
1668
|
+
*/
|
|
1669
|
+
startHealthMonitoring() {
|
|
1670
|
+
if (this.healthCheckTimer) {
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
this.healthCheckTimer = setInterval(async () => {
|
|
1674
|
+
const result = await this.healthCheck();
|
|
1675
|
+
this.emit('health', result);
|
|
1676
|
+
if (result.success) {
|
|
1677
|
+
// Reset failure counter on success
|
|
1678
|
+
if (this.consecutiveFailures > 0) {
|
|
1679
|
+
logger.info({
|
|
1680
|
+
responseTimeMs: result.responseTimeMs,
|
|
1681
|
+
dimensions: result.dimensions,
|
|
1682
|
+
previousFailures: this.consecutiveFailures,
|
|
1683
|
+
}, '[EmbeddingServerManager] Health check recovered - server is healthy');
|
|
1684
|
+
}
|
|
1685
|
+
else {
|
|
1686
|
+
logger.debug({
|
|
1687
|
+
responseTimeMs: result.responseTimeMs,
|
|
1688
|
+
dimensions: result.dimensions,
|
|
1689
|
+
}, '[EmbeddingServerManager] Health check successful');
|
|
1690
|
+
}
|
|
1691
|
+
this.consecutiveFailures = 0;
|
|
1692
|
+
}
|
|
1693
|
+
else {
|
|
1694
|
+
// FIX: During startup grace period, suppress failure counting and restart attempts
|
|
1695
|
+
// Grace period is dynamic, derived from config.startupTimeoutMs
|
|
1696
|
+
if (this.startupGraceUntil > 0 && Date.now() < this.startupGraceUntil) {
|
|
1697
|
+
const remainingMs = this.startupGraceUntil - Date.now();
|
|
1698
|
+
logger.debug({ remainingMs, error: result.error },
|
|
1699
|
+
'[EmbeddingServerManager] Health check failed during startup grace period - suppressing');
|
|
1700
|
+
return; // Don't count failures or attempt restarts during grace
|
|
1701
|
+
}
|
|
1702
|
+
// Clear expired grace period
|
|
1703
|
+
if (this.startupGraceUntil > 0 && Date.now() >= this.startupGraceUntil) {
|
|
1704
|
+
this.startupGraceUntil = 0;
|
|
1705
|
+
}
|
|
1706
|
+
// Check if server was killed by KYS watchdog - if so, auto-respawn immediately
|
|
1707
|
+
if (this.wasKilledByKYS()) {
|
|
1708
|
+
logger.info('[EmbeddingServerManager] Health check failed - server was killed by KYS watchdog, auto-respawning...');
|
|
1709
|
+
const respawned = await this.autoRespawnIfKYSDeath();
|
|
1710
|
+
if (respawned) {
|
|
1711
|
+
logger.info('[EmbeddingServerManager] Auto-respawn after KYS death successful');
|
|
1712
|
+
return; // Skip failure counting, server is back
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
this.consecutiveFailures++;
|
|
1716
|
+
logger.warn({
|
|
1717
|
+
failures: this.consecutiveFailures,
|
|
1718
|
+
maxFailures: this.config.maxFailuresBeforeRestart,
|
|
1719
|
+
error: result.error,
|
|
1720
|
+
responseTimeMs: result.responseTimeMs,
|
|
1721
|
+
socketPath: this.socketPath,
|
|
1722
|
+
socketExists: existsSync(this.socketPath),
|
|
1723
|
+
}, '[EmbeddingServerManager] Health check failed');
|
|
1724
|
+
this.emit('unhealthy', { failures: this.consecutiveFailures });
|
|
1725
|
+
// Attempt restart if too many failures
|
|
1726
|
+
if (this.consecutiveFailures >= this.config.maxFailuresBeforeRestart) {
|
|
1727
|
+
logger.warn({
|
|
1728
|
+
failures: this.consecutiveFailures,
|
|
1729
|
+
maxFailures: this.config.maxFailuresBeforeRestart,
|
|
1730
|
+
restartCount: this.restartCount,
|
|
1731
|
+
}, '[EmbeddingServerManager] Too many consecutive failures, initiating restart...');
|
|
1732
|
+
this.consecutiveFailures = 0;
|
|
1733
|
+
await this.attemptRestart();
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
// FIX 4: Duplicate process detection during health monitoring
|
|
1737
|
+
// Check for multiple embedding server processes and kill extras
|
|
1738
|
+
try {
|
|
1739
|
+
const runningServers = this.findRunningEmbeddingServers();
|
|
1740
|
+
if (runningServers.length > 1) {
|
|
1741
|
+
logger.error({
|
|
1742
|
+
count: runningServers.length,
|
|
1743
|
+
pids: runningServers.map(s => s.pid),
|
|
1744
|
+
}, '[EmbeddingServerManager] CRITICAL: Multiple embedding server processes detected!');
|
|
1745
|
+
// Determine the legitimate PID from PID file
|
|
1746
|
+
const legitimatePid = this.readPidFile();
|
|
1747
|
+
if (legitimatePid) {
|
|
1748
|
+
// Kill all processes that are NOT the legitimate one
|
|
1749
|
+
for (const server of runningServers) {
|
|
1750
|
+
if (server.pid !== legitimatePid) {
|
|
1751
|
+
try {
|
|
1752
|
+
logger.warn({ pid: server.pid, legitimatePid },
|
|
1753
|
+
'[EmbeddingServerManager] FIX 4: Killing duplicate embedding server process');
|
|
1754
|
+
process.kill(server.pid, 'SIGTERM');
|
|
1755
|
+
// Give it a moment, then force kill if needed
|
|
1756
|
+
setTimeout(() => {
|
|
1757
|
+
try {
|
|
1758
|
+
process.kill(server.pid, 0);
|
|
1759
|
+
process.kill(server.pid, 'SIGKILL');
|
|
1760
|
+
logger.warn({ pid: server.pid },
|
|
1761
|
+
'[EmbeddingServerManager] FIX 4: Force killed duplicate process');
|
|
1762
|
+
}
|
|
1763
|
+
catch { /* already dead */ }
|
|
1764
|
+
}, 2000);
|
|
1765
|
+
}
|
|
1766
|
+
catch (killErr) {
|
|
1767
|
+
logger.debug({ pid: server.pid, error: killErr.message },
|
|
1768
|
+
'[EmbeddingServerManager] FIX 4: Failed to kill duplicate (may be dead)');
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
else {
|
|
1774
|
+
// No PID file - keep the first one, kill the rest
|
|
1775
|
+
logger.warn('[EmbeddingServerManager] FIX 4: No PID file found, keeping oldest process');
|
|
1776
|
+
for (let i = 1; i < runningServers.length; i++) {
|
|
1777
|
+
try {
|
|
1778
|
+
process.kill(runningServers[i].pid, 'SIGTERM');
|
|
1779
|
+
logger.warn({ pid: runningServers[i].pid },
|
|
1780
|
+
'[EmbeddingServerManager] FIX 4: Killing extra duplicate process');
|
|
1781
|
+
}
|
|
1782
|
+
catch { /* ignore */ }
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
catch (dupErr) {
|
|
1788
|
+
logger.debug({ error: dupErr.message },
|
|
1789
|
+
'[EmbeddingServerManager] FIX 4: Duplicate detection check failed (non-fatal)');
|
|
1790
|
+
}
|
|
1791
|
+
}, this.config.healthCheckIntervalMs);
|
|
1792
|
+
// Don't prevent process exit
|
|
1793
|
+
this.healthCheckTimer.unref();
|
|
1794
|
+
logger.debug('[EmbeddingServerManager] Health monitoring started');
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Stop health monitoring
|
|
1798
|
+
*/
|
|
1799
|
+
stopHealthMonitoring() {
|
|
1800
|
+
if (this.healthCheckTimer) {
|
|
1801
|
+
clearInterval(this.healthCheckTimer);
|
|
1802
|
+
this.healthCheckTimer = null;
|
|
1803
|
+
logger.debug('[EmbeddingServerManager] Health monitoring stopped');
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Start KYS (Keep Yourself Safe) heartbeat - two-way ack system
|
|
1808
|
+
*
|
|
1809
|
+
* Sends {"type": "kys", "text": "kurt cobain t minus 25"} every 25 seconds.
|
|
1810
|
+
* If the embedding server doesn't receive this heartbeat within 30 seconds,
|
|
1811
|
+
* it commits suicide to prevent zombie processes.
|
|
1812
|
+
*
|
|
1813
|
+
* This prevents orphan embedding servers when MCP crashes or is killed.
|
|
1814
|
+
*/
|
|
1815
|
+
startKysHeartbeat() {
|
|
1816
|
+
// Stop existing heartbeat if any
|
|
1817
|
+
this.stopKysHeartbeat();
|
|
1818
|
+
logger.info('[EmbeddingServerManager] Starting KYS heartbeat (every 25s)');
|
|
1819
|
+
this.kysHeartbeatTimer = setInterval(async () => {
|
|
1820
|
+
// FIX: Only skip if shutting down - heartbeat should still try for externally started servers
|
|
1821
|
+
if (this.isShuttingDown) {
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
try {
|
|
1825
|
+
const socket = createConnection(this.socketPath);
|
|
1826
|
+
let responded = false;
|
|
1827
|
+
socket.setTimeout(5000); // 5 second timeout for kys ack
|
|
1828
|
+
socket.on('connect', () => {
|
|
1829
|
+
socket.write(JSON.stringify({
|
|
1830
|
+
type: 'kys',
|
|
1831
|
+
text: 'kurt cobain t minus 25'
|
|
1832
|
+
}) + '\n');
|
|
1833
|
+
});
|
|
1834
|
+
socket.on('data', (data) => {
|
|
1835
|
+
responded = true;
|
|
1836
|
+
try {
|
|
1837
|
+
const response = JSON.parse(data.toString().trim());
|
|
1838
|
+
logger.debug({
|
|
1839
|
+
status: response.status,
|
|
1840
|
+
ack: response.ack,
|
|
1841
|
+
}, '[EmbeddingServerManager] KYS heartbeat acknowledged');
|
|
1842
|
+
}
|
|
1843
|
+
catch (e) {
|
|
1844
|
+
// Parse error, but we got data so server is alive
|
|
1845
|
+
}
|
|
1846
|
+
socket.destroy();
|
|
1847
|
+
});
|
|
1848
|
+
socket.on('error', (err) => {
|
|
1849
|
+
if (!responded) {
|
|
1850
|
+
logger.warn({ error: err.message }, '[EmbeddingServerManager] KYS heartbeat failed');
|
|
1851
|
+
}
|
|
1852
|
+
socket.destroy();
|
|
1853
|
+
});
|
|
1854
|
+
socket.on('timeout', () => {
|
|
1855
|
+
if (!responded) {
|
|
1856
|
+
logger.warn('[EmbeddingServerManager] KYS heartbeat timed out');
|
|
1857
|
+
}
|
|
1858
|
+
socket.destroy();
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
catch (err) {
|
|
1862
|
+
logger.warn({ error: err.message }, '[EmbeddingServerManager] Failed to send KYS heartbeat');
|
|
1863
|
+
}
|
|
1864
|
+
}, this.KYS_HEARTBEAT_INTERVAL_MS);
|
|
1865
|
+
// Don't prevent process exit
|
|
1866
|
+
this.kysHeartbeatTimer.unref();
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Stop KYS heartbeat
|
|
1870
|
+
*/
|
|
1871
|
+
stopKysHeartbeat() {
|
|
1872
|
+
if (this.kysHeartbeatTimer) {
|
|
1873
|
+
clearInterval(this.kysHeartbeatTimer);
|
|
1874
|
+
this.kysHeartbeatTimer = null;
|
|
1875
|
+
logger.debug('[EmbeddingServerManager] KYS heartbeat stopped');
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Write PID file with timestamp
|
|
1880
|
+
*/
|
|
1881
|
+
writePidFile(pid) {
|
|
1882
|
+
try {
|
|
1883
|
+
const pidDir = dirname(this.pidFilePath);
|
|
1884
|
+
if (!existsSync(pidDir)) {
|
|
1885
|
+
mkdirSync(pidDir, { recursive: true });
|
|
1886
|
+
}
|
|
1887
|
+
// Format: PID:TIMESTAMP
|
|
1888
|
+
writeFileSync(this.pidFilePath, `${pid}:${Date.now()}`, 'utf8');
|
|
1889
|
+
logger.debug({ pid, path: this.pidFilePath }, '[EmbeddingServerManager] Wrote PID file');
|
|
1890
|
+
}
|
|
1891
|
+
catch (err) {
|
|
1892
|
+
logger.error({ error: err }, '[EmbeddingServerManager] Failed to write PID file');
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
/**
|
|
1896
|
+
* Read PID from file
|
|
1897
|
+
*/
|
|
1898
|
+
readPidFile() {
|
|
1899
|
+
try {
|
|
1900
|
+
if (!existsSync(this.pidFilePath)) {
|
|
1901
|
+
return null;
|
|
1902
|
+
}
|
|
1903
|
+
const content = readFileSync(this.pidFilePath, 'utf8').trim();
|
|
1904
|
+
const pid = parseInt(content.split(':')[0], 10);
|
|
1905
|
+
return isNaN(pid) ? null : pid;
|
|
1906
|
+
}
|
|
1907
|
+
catch (err) {
|
|
1908
|
+
return null;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Read PID file with timestamp
|
|
1913
|
+
*/
|
|
1914
|
+
readPidFileWithTimestamp() {
|
|
1915
|
+
try {
|
|
1916
|
+
if (!existsSync(this.pidFilePath)) {
|
|
1917
|
+
return null;
|
|
1918
|
+
}
|
|
1919
|
+
const content = readFileSync(this.pidFilePath, 'utf8').trim();
|
|
1920
|
+
const parts = content.split(':');
|
|
1921
|
+
const pid = parseInt(parts[0], 10);
|
|
1922
|
+
const timestamp = parseInt(parts[1], 10);
|
|
1923
|
+
if (isNaN(pid))
|
|
1924
|
+
return null;
|
|
1925
|
+
return { pid, timestamp: isNaN(timestamp) ? Date.now() : timestamp };
|
|
1926
|
+
}
|
|
1927
|
+
catch (err) {
|
|
1928
|
+
return null;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Remove PID file
|
|
1933
|
+
*/
|
|
1934
|
+
removePidFile() {
|
|
1935
|
+
try {
|
|
1936
|
+
if (existsSync(this.pidFilePath)) {
|
|
1937
|
+
unlinkSync(this.pidFilePath);
|
|
1938
|
+
logger.debug('[EmbeddingServerManager] Removed PID file');
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
catch (err) {
|
|
1942
|
+
logger.debug({ error: err }, '[EmbeddingServerManager] Failed to remove PID file');
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* FIX 6: Get process resource usage (CPU, memory) for the embedding server
|
|
1947
|
+
* Uses `ps -p PID -o pid,pcpu,pmem,rss,vsz` to get resource metrics
|
|
1948
|
+
* @returns {{ pid: number, cpu: number, memPercent: number, rss: number, vsz: number } | null}
|
|
1949
|
+
*/
|
|
1950
|
+
getProcessResources() {
|
|
1951
|
+
const pid = this.readPidFile();
|
|
1952
|
+
if (!pid) return null;
|
|
1953
|
+
try {
|
|
1954
|
+
// Check process is alive first
|
|
1955
|
+
process.kill(pid, 0);
|
|
1956
|
+
const result = execSync(`ps -p ${pid} -o pid,pcpu,pmem,rss,vsz --no-headers`, {
|
|
1957
|
+
encoding: 'utf8',
|
|
1958
|
+
timeout: 5000,
|
|
1959
|
+
}).trim();
|
|
1960
|
+
if (!result) return null;
|
|
1961
|
+
const parts = result.trim().split(/\s+/);
|
|
1962
|
+
if (parts.length < 5) return null;
|
|
1963
|
+
return {
|
|
1964
|
+
pid: parseInt(parts[0], 10),
|
|
1965
|
+
cpu: parseFloat(parts[1]), // %CPU
|
|
1966
|
+
memPercent: parseFloat(parts[2]), // %MEM
|
|
1967
|
+
rss: parseInt(parts[3], 10), // RSS in KB
|
|
1968
|
+
vsz: parseInt(parts[4], 10), // VSZ in KB
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
catch (err) {
|
|
1972
|
+
logger.debug({ pid, error: err.message }, '[EmbeddingServerManager] FIX 6: Failed to get process resources');
|
|
1973
|
+
return null;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* FIX 6: Check resource limits and log warnings if exceeded
|
|
1978
|
+
* Warns if RSS > 6000MB or CPU > 80%
|
|
1979
|
+
* @returns {{ withinLimits: boolean, warnings: string[] }}
|
|
1980
|
+
*/
|
|
1981
|
+
checkResourceLimits() {
|
|
1982
|
+
const resources = this.getProcessResources();
|
|
1983
|
+
if (!resources) {
|
|
1984
|
+
return { withinLimits: true, warnings: [] };
|
|
1985
|
+
}
|
|
1986
|
+
const warnings = [];
|
|
1987
|
+
const RSS_LIMIT_MB = 6000; // 6GB RSS limit
|
|
1988
|
+
const CPU_LIMIT_PCT = 80; // 80% CPU limit
|
|
1989
|
+
const rssMb = resources.rss / 1024; // Convert KB to MB
|
|
1990
|
+
if (rssMb > RSS_LIMIT_MB) {
|
|
1991
|
+
const msg = `Embedding server RSS memory (${rssMb.toFixed(0)}MB) exceeds limit (${RSS_LIMIT_MB}MB)`;
|
|
1992
|
+
warnings.push(msg);
|
|
1993
|
+
logger.warn({
|
|
1994
|
+
pid: resources.pid,
|
|
1995
|
+
rssMb: rssMb.toFixed(0),
|
|
1996
|
+
limitMb: RSS_LIMIT_MB,
|
|
1997
|
+
}, `[EmbeddingServerManager] FIX 6: RESOURCE WARNING - ${msg}`);
|
|
1998
|
+
}
|
|
1999
|
+
if (resources.cpu > CPU_LIMIT_PCT) {
|
|
2000
|
+
const msg = `Embedding server CPU usage (${resources.cpu.toFixed(1)}%) exceeds limit (${CPU_LIMIT_PCT}%)`;
|
|
2001
|
+
warnings.push(msg);
|
|
2002
|
+
logger.warn({
|
|
2003
|
+
pid: resources.pid,
|
|
2004
|
+
cpuPercent: resources.cpu.toFixed(1),
|
|
2005
|
+
limitPercent: CPU_LIMIT_PCT,
|
|
2006
|
+
}, `[EmbeddingServerManager] FIX 6: RESOURCE WARNING - ${msg}`);
|
|
2007
|
+
}
|
|
2008
|
+
if (warnings.length === 0) {
|
|
2009
|
+
logger.debug({
|
|
2010
|
+
pid: resources.pid,
|
|
2011
|
+
rssMb: rssMb.toFixed(0),
|
|
2012
|
+
cpuPercent: resources.cpu.toFixed(1),
|
|
2013
|
+
}, '[EmbeddingServerManager] FIX 6: Resource usage within limits');
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
withinLimits: warnings.length === 0,
|
|
2017
|
+
warnings,
|
|
2018
|
+
resources: {
|
|
2019
|
+
pid: resources.pid,
|
|
2020
|
+
cpuPercent: resources.cpu,
|
|
2021
|
+
rssMb: Math.round(rssMb),
|
|
2022
|
+
vszMb: Math.round(resources.vsz / 1024),
|
|
2023
|
+
memPercent: resources.memPercent,
|
|
2024
|
+
},
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Sleep helper
|
|
2029
|
+
*/
|
|
2030
|
+
sleep(ms) {
|
|
2031
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
// ============================================================================
|
|
2035
|
+
// PER-PROJECT INSTANCE MAP
|
|
2036
|
+
// ============================================================================
|
|
2037
|
+
// FIX: Use per-project Map instead of global singleton
|
|
2038
|
+
// This prevents cross-project socket conflicts when running multiple instances
|
|
2039
|
+
const embeddingManagersByProject = new Map();
|
|
2040
|
+
/**
|
|
2041
|
+
* Get or create the embedding server manager for a specific project
|
|
2042
|
+
* Uses per-project Map pattern to ensure proper isolation
|
|
2043
|
+
*/
|
|
2044
|
+
export function getEmbeddingServerManager(config) {
|
|
2045
|
+
const projectPath = getProjectPath();
|
|
2046
|
+
// Check if we already have a manager for this project
|
|
2047
|
+
if (embeddingManagersByProject.has(projectPath)) {
|
|
2048
|
+
return embeddingManagersByProject.get(projectPath);
|
|
2049
|
+
}
|
|
2050
|
+
// Create new manager for this project
|
|
2051
|
+
const manager = new EmbeddingServerManager(config);
|
|
2052
|
+
embeddingManagersByProject.set(projectPath, manager);
|
|
2053
|
+
logger.info({ projectPath, totalManagers: embeddingManagersByProject.size }, '[EmbeddingServerManager] Created new per-project manager');
|
|
2054
|
+
return manager;
|
|
2055
|
+
}
|
|
2056
|
+
/**
|
|
2057
|
+
* Reset the embedding server manager for a specific project (or current project)
|
|
2058
|
+
*/
|
|
2059
|
+
export async function resetEmbeddingServerManager(projectPath) {
|
|
2060
|
+
const targetPath = projectPath || getProjectPath();
|
|
2061
|
+
if (embeddingManagersByProject.has(targetPath)) {
|
|
2062
|
+
const manager = embeddingManagersByProject.get(targetPath);
|
|
2063
|
+
await manager.shutdown();
|
|
2064
|
+
embeddingManagersByProject.delete(targetPath);
|
|
2065
|
+
logger.info({ projectPath: targetPath }, '[EmbeddingServerManager] Reset project manager');
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Reset ALL embedding server managers (for global cleanup)
|
|
2070
|
+
*/
|
|
2071
|
+
export async function resetAllEmbeddingServerManagers() {
|
|
2072
|
+
const promises = [];
|
|
2073
|
+
for (const [projectPath, manager] of embeddingManagersByProject) {
|
|
2074
|
+
promises.push(manager.shutdown().then(() => {
|
|
2075
|
+
logger.info({ projectPath }, '[EmbeddingServerManager] Shutdown manager');
|
|
2076
|
+
}));
|
|
2077
|
+
}
|
|
2078
|
+
await Promise.all(promises);
|
|
2079
|
+
embeddingManagersByProject.clear();
|
|
2080
|
+
}
|
|
2081
|
+
//# sourceMappingURL=embeddingServerManager.js.map
|