@tenex-chat/backend 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +194 -0
- package/dist/backend-wrapper.cjs +3 -0
- package/dist/src/index.js +331928 -0
- package/package.json +103 -0
- package/src/agents/AgentRegistry.ts +418 -0
- package/src/agents/AgentStorage.ts +1133 -0
- package/src/agents/ConfigResolver.ts +229 -0
- package/src/agents/agent-installer.ts +236 -0
- package/src/agents/agent-loader.ts +241 -0
- package/src/agents/constants.ts +82 -0
- package/src/agents/errors.ts +48 -0
- package/src/agents/execution/AgentExecutor.ts +561 -0
- package/src/agents/execution/ExecutionContextFactory.ts +112 -0
- package/src/agents/execution/MessageCompiler.ts +597 -0
- package/src/agents/execution/MessageSyncer.ts +100 -0
- package/src/agents/execution/PostCompletionChecker.ts +278 -0
- package/src/agents/execution/ProgressMonitor.ts +50 -0
- package/src/agents/execution/RALResolver.ts +177 -0
- package/src/agents/execution/SessionManager.ts +181 -0
- package/src/agents/execution/StreamCallbacks.ts +312 -0
- package/src/agents/execution/StreamExecutionHandler.ts +579 -0
- package/src/agents/execution/StreamSetup.ts +313 -0
- package/src/agents/execution/ToolEventHandlers.ts +239 -0
- package/src/agents/execution/ToolExecutionTracker.ts +498 -0
- package/src/agents/execution/ToolResultUtils.ts +97 -0
- package/src/agents/execution/ToolSupervisionWrapper.ts +174 -0
- package/src/agents/execution/constants.ts +16 -0
- package/src/agents/execution/index.ts +3 -0
- package/src/agents/execution/types.ts +96 -0
- package/src/agents/execution/utils.ts +26 -0
- package/src/agents/index.ts +4 -0
- package/src/agents/script-installer.ts +266 -0
- package/src/agents/supervision/SupervisorLLMService.ts +253 -0
- package/src/agents/supervision/SupervisorOrchestrator.ts +471 -0
- package/src/agents/supervision/heuristics/ConsecutiveToolsWithoutTodoHeuristic.ts +73 -0
- package/src/agents/supervision/heuristics/DelegationClaimHeuristic.ts +80 -0
- package/src/agents/supervision/heuristics/HeuristicRegistry.ts +114 -0
- package/src/agents/supervision/heuristics/PendingTodosHeuristic.ts +93 -0
- package/src/agents/supervision/heuristics/SilentAgentHeuristic.ts +54 -0
- package/src/agents/supervision/heuristics/index.ts +5 -0
- package/src/agents/supervision/index.ts +28 -0
- package/src/agents/supervision/registerHeuristics.ts +110 -0
- package/src/agents/supervision/supervisionHealthCheck.ts +123 -0
- package/src/agents/supervision/types.ts +171 -0
- package/src/agents/tool-names.ts +46 -0
- package/src/agents/tool-normalization.ts +184 -0
- package/src/agents/types/index.ts +2 -0
- package/src/agents/types/runtime.ts +74 -0
- package/src/agents/types/storage.ts +145 -0
- package/src/commands/agent/import/index.ts +6 -0
- package/src/commands/agent/import/openclaw-distiller.ts +57 -0
- package/src/commands/agent/import/openclaw-reader.ts +141 -0
- package/src/commands/agent/import/openclaw.ts +154 -0
- package/src/commands/agent/index.ts +6 -0
- package/src/commands/agent.ts +215 -0
- package/src/commands/daemon.ts +198 -0
- package/src/commands/doctor.ts +134 -0
- package/src/commands/setup/embed.ts +228 -0
- package/src/commands/setup/global-system-prompt.ts +223 -0
- package/src/commands/setup/image.ts +179 -0
- package/src/commands/setup/index.ts +16 -0
- package/src/commands/setup/interactive.ts +95 -0
- package/src/commands/setup/llm.ts +38 -0
- package/src/commands/setup/onboarding.ts +294 -0
- package/src/commands/setup/providers.ts +27 -0
- package/src/constants.ts +34 -0
- package/src/conversations/ConversationDiskReader.ts +148 -0
- package/src/conversations/ConversationRegistry.ts +728 -0
- package/src/conversations/ConversationStore.ts +868 -0
- package/src/conversations/MessageBuilder.ts +866 -0
- package/src/conversations/executionTime.ts +62 -0
- package/src/conversations/formatters/DelegationXmlFormatter.ts +64 -0
- package/src/conversations/formatters/ThreadedConversationFormatter.ts +303 -0
- package/src/conversations/formatters/index.ts +9 -0
- package/src/conversations/formatters/utils/MessageFormatter.ts +46 -0
- package/src/conversations/formatters/utils/TimestampFormatter.ts +56 -0
- package/src/conversations/formatters/utils/TreeBuilder.ts +131 -0
- package/src/conversations/formatters/utils/TreeRenderer.ts +49 -0
- package/src/conversations/index.ts +2 -0
- package/src/conversations/persistence/ToolMessageStorage.ts +143 -0
- package/src/conversations/search/ConversationIndexManager.ts +393 -0
- package/src/conversations/search/QueryParser.ts +114 -0
- package/src/conversations/search/SearchEngine.ts +175 -0
- package/src/conversations/search/SnippetExtractor.ts +345 -0
- package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +484 -0
- package/src/conversations/search/embeddings/ConversationIndexingJob.ts +320 -0
- package/src/conversations/search/embeddings/IndexingStateManager.ts +338 -0
- package/src/conversations/search/embeddings/index.ts +18 -0
- package/src/conversations/search/index.ts +49 -0
- package/src/conversations/search/types.ts +124 -0
- package/src/conversations/services/CategoryManager.ts +160 -0
- package/src/conversations/services/ConversationResolver.ts +296 -0
- package/src/conversations/services/ConversationSummarizer.ts +234 -0
- package/src/conversations/services/MetadataDebounceManager.ts +188 -0
- package/src/conversations/services/index.ts +2 -0
- package/src/conversations/types.ts +148 -0
- package/src/conversations/utils/content-utils.ts +69 -0
- package/src/conversations/utils/image-placeholder.ts +281 -0
- package/src/conversations/utils/image-url-utils.ts +171 -0
- package/src/conversations/utils/multimodal-content.ts +90 -0
- package/src/conversations/utils/tool-result-truncator.ts +159 -0
- package/src/daemon/Daemon.ts +1883 -0
- package/src/daemon/ProjectRuntime.ts +657 -0
- package/src/daemon/RestartState.ts +152 -0
- package/src/daemon/RuntimeLifecycle.ts +268 -0
- package/src/daemon/SubscriptionManager.ts +305 -0
- package/src/daemon/UnixSocketTransport.ts +318 -0
- package/src/daemon/filters/SubscriptionFilterBuilder.ts +119 -0
- package/src/daemon/index.ts +9 -0
- package/src/daemon/routing/DaemonRouter.ts +491 -0
- package/src/daemon/types.ts +150 -0
- package/src/daemon/utils/routing-log.ts +76 -0
- package/src/daemon/utils/telemetry.ts +173 -0
- package/src/event-handler/agentDeletion.ts +383 -0
- package/src/event-handler/index.ts +749 -0
- package/src/event-handler/newConversation.ts +165 -0
- package/src/event-handler/project.ts +166 -0
- package/src/event-handler/reply.ts +18 -0
- package/src/events/NDKAgentDefinition.ts +292 -0
- package/src/events/NDKAgentLesson.ts +106 -0
- package/src/events/NDKEventMetadata.ts +34 -0
- package/src/events/NDKMCPTool.ts +60 -0
- package/src/events/NDKProjectStatus.ts +384 -0
- package/src/events/index.ts +4 -0
- package/src/index.ts +126 -0
- package/src/lib/agent-home.ts +334 -0
- package/src/lib/error-formatter.ts +200 -0
- package/src/lib/fs/filesystem.ts +128 -0
- package/src/lib/fs/index.ts +1 -0
- package/src/lib/json-parser.ts +30 -0
- package/src/lib/string.ts +15 -0
- package/src/lib/time.ts +74 -0
- package/src/llm/ChunkHandler.ts +277 -0
- package/src/llm/FinishHandler.ts +250 -0
- package/src/llm/LLMConfigEditor.ts +154 -0
- package/src/llm/LLMServiceFactory.ts +230 -0
- package/src/llm/MessageProcessor.ts +90 -0
- package/src/llm/RecordingState.ts +37 -0
- package/src/llm/StreamPublisher.ts +40 -0
- package/src/llm/TracingUtils.ts +77 -0
- package/src/llm/chunk-validators.ts +57 -0
- package/src/llm/constants.ts +6 -0
- package/src/llm/index.ts +12 -0
- package/src/llm/meta/MetaModelResolver.ts +352 -0
- package/src/llm/meta/index.ts +11 -0
- package/src/llm/middleware/flight-recorder.ts +188 -0
- package/src/llm/providers/MockProvider.ts +332 -0
- package/src/llm/providers/agent/ClaudeCodeProvider.ts +343 -0
- package/src/llm/providers/agent/ClaudeCodeToolsAdapter.ts +203 -0
- package/src/llm/providers/agent/CodexAppServerProvider.ts +214 -0
- package/src/llm/providers/agent/CodexAppServerToolsAdapter.ts +91 -0
- package/src/llm/providers/agent/index.ts +10 -0
- package/src/llm/providers/base/AgentProvider.ts +107 -0
- package/src/llm/providers/base/BaseProvider.ts +114 -0
- package/src/llm/providers/base/StandardProvider.ts +38 -0
- package/src/llm/providers/base/index.ts +9 -0
- package/src/llm/providers/index.ts +106 -0
- package/src/llm/providers/key-manager.ts +238 -0
- package/src/llm/providers/ollama-models.ts +105 -0
- package/src/llm/providers/openrouter-models.ts +102 -0
- package/src/llm/providers/provider-ids.ts +18 -0
- package/src/llm/providers/registry/ProviderRegistry.ts +414 -0
- package/src/llm/providers/registry/index.ts +7 -0
- package/src/llm/providers/standard/AnthropicProvider.ts +71 -0
- package/src/llm/providers/standard/OllamaProvider.ts +59 -0
- package/src/llm/providers/standard/OpenAIProvider.ts +44 -0
- package/src/llm/providers/standard/OpenRouterProvider.ts +103 -0
- package/src/llm/providers/standard/index.ts +10 -0
- package/src/llm/providers/types.ts +194 -0
- package/src/llm/providers/usage-metadata.ts +78 -0
- package/src/llm/service.ts +713 -0
- package/src/llm/types.ts +167 -0
- package/src/llm/utils/ConfigurationManager.ts +650 -0
- package/src/llm/utils/ConfigurationTester.ts +229 -0
- package/src/llm/utils/ModelSelector.ts +212 -0
- package/src/llm/utils/ProviderConfigUI.ts +177 -0
- package/src/llm/utils/claudeCodePromptCompiler.ts +141 -0
- package/src/llm/utils/codex-models.ts +53 -0
- package/src/llm/utils/context-window-cache.ts +30 -0
- package/src/llm/utils/models-dev-cache.ts +267 -0
- package/src/llm/utils/provider-setup.ts +50 -0
- package/src/llm/utils/tool-errors.ts +78 -0
- package/src/llm/utils/usage.ts +74 -0
- package/src/logging/EventRoutingLogger.ts +205 -0
- package/src/nostr/AgentEventDecoder.ts +357 -0
- package/src/nostr/AgentEventEncoder.ts +677 -0
- package/src/nostr/AgentProfilePublisher.ts +657 -0
- package/src/nostr/AgentPublisher.ts +437 -0
- package/src/nostr/BlossomService.ts +226 -0
- package/src/nostr/InterventionPublisher.ts +132 -0
- package/src/nostr/TagExtractor.ts +228 -0
- package/src/nostr/collectEvents.ts +83 -0
- package/src/nostr/constants.ts +38 -0
- package/src/nostr/encryption.ts +26 -0
- package/src/nostr/index.ts +31 -0
- package/src/nostr/keys.ts +17 -0
- package/src/nostr/kinds.ts +37 -0
- package/src/nostr/ndkClient.ts +72 -0
- package/src/nostr/relays.ts +43 -0
- package/src/nostr/trace-context.ts +39 -0
- package/src/nostr/types.ts +227 -0
- package/src/nostr/utils.ts +84 -0
- package/src/prompts/core/FragmentRegistry.ts +30 -0
- package/src/prompts/core/PromptBuilder.ts +98 -0
- package/src/prompts/core/index.ts +3 -0
- package/src/prompts/core/types.ts +13 -0
- package/src/prompts/fragments/00-global-system-prompt.ts +44 -0
- package/src/prompts/fragments/01-agent-identity.ts +69 -0
- package/src/prompts/fragments/02-agent-home-directory.ts +114 -0
- package/src/prompts/fragments/03-system-reminders-explanation.ts +14 -0
- package/src/prompts/fragments/04-relay-configuration.ts +38 -0
- package/src/prompts/fragments/05-delegation-chain.ts +45 -0
- package/src/prompts/fragments/06-agent-todos.ts +74 -0
- package/src/prompts/fragments/06-todo-usage-guidance.ts +34 -0
- package/src/prompts/fragments/07-meta-project-context.ts +234 -0
- package/src/prompts/fragments/08-active-conversations.ts +382 -0
- package/src/prompts/fragments/09-recent-conversations.ts +153 -0
- package/src/prompts/fragments/10-referenced-article.ts +21 -0
- package/src/prompts/fragments/11-nudges.ts +134 -0
- package/src/prompts/fragments/12-skills.ts +127 -0
- package/src/prompts/fragments/13-available-nudges.ts +122 -0
- package/src/prompts/fragments/15-available-agents.ts +53 -0
- package/src/prompts/fragments/16-stay-in-your-lane.ts +41 -0
- package/src/prompts/fragments/17-todo-before-delegation.ts +39 -0
- package/src/prompts/fragments/20-voice-mode.ts +62 -0
- package/src/prompts/fragments/22-scheduled-tasks.ts +175 -0
- package/src/prompts/fragments/24-retrieved-lessons.ts +26 -0
- package/src/prompts/fragments/25-rag-instructions.ts +333 -0
- package/src/prompts/fragments/26-mcp-resources.ts +237 -0
- package/src/prompts/fragments/27-memorized-reports.ts +77 -0
- package/src/prompts/fragments/28-agent-directed-monitoring.ts +32 -0
- package/src/prompts/fragments/29-rag-collections.ts +50 -0
- package/src/prompts/fragments/30-worktree-context.ts +98 -0
- package/src/prompts/fragments/31-agents-md-guidance.ts +96 -0
- package/src/prompts/fragments/32-process-metrics.ts +72 -0
- package/src/prompts/fragments/debug-mode.ts +48 -0
- package/src/prompts/fragments/delegation-completion.ts +44 -0
- package/src/prompts/fragments/index.ts +91 -0
- package/src/prompts/index.ts +21 -0
- package/src/prompts/utils/systemPromptBuilder.ts +777 -0
- package/src/scripts/migrate-prefix-index.ts +157 -0
- package/src/services/AgentDefinitionMonitor.ts +701 -0
- package/src/services/ConfigService.ts +723 -0
- package/src/services/CooldownRegistry.ts +199 -0
- package/src/services/LLMOperationsRegistry.ts +424 -0
- package/src/services/OwnerAgentListService.ts +354 -0
- package/src/services/PubkeyService.ts +308 -0
- package/src/services/agents/AgentMetadataStore.ts +72 -0
- package/src/services/agents/AgentResolution.ts +59 -0
- package/src/services/agents/EscalationService.ts +281 -0
- package/src/services/agents/NDKAgentDiscovery.ts +95 -0
- package/src/services/agents/index.ts +7 -0
- package/src/services/agents-md/AgentsMdService.ts +184 -0
- package/src/services/agents-md/SystemReminderInjector.ts +238 -0
- package/src/services/agents-md/index.ts +11 -0
- package/src/services/apns/APNsClient.ts +203 -0
- package/src/services/apns/APNsService.ts +358 -0
- package/src/services/apns/index.ts +11 -0
- package/src/services/apns/types.ts +80 -0
- package/src/services/compression/CompressionService.ts +445 -0
- package/src/services/compression/compression-schema.ts +28 -0
- package/src/services/compression/compression-types.ts +74 -0
- package/src/services/compression/compression-utils.ts +587 -0
- package/src/services/config/types.ts +394 -0
- package/src/services/dispatch/AgentDispatchService.ts +937 -0
- package/src/services/dispatch/AgentRouter.ts +181 -0
- package/src/services/dispatch/DelegationCompletionHandler.ts +232 -0
- package/src/services/embedding/EmbeddingProvider.ts +188 -0
- package/src/services/embedding/index.ts +5 -0
- package/src/services/event-context/EventContextService.ts +108 -0
- package/src/services/event-context/index.ts +2 -0
- package/src/services/heuristics/ContextBuilder.ts +106 -0
- package/src/services/heuristics/HeuristicEngine.ts +200 -0
- package/src/services/heuristics/formatters.ts +58 -0
- package/src/services/heuristics/index.ts +12 -0
- package/src/services/heuristics/rules/index.ts +25 -0
- package/src/services/heuristics/rules/todoBeforeDelegation.ts +69 -0
- package/src/services/heuristics/rules/todoReminderOnToolUse.ts +63 -0
- package/src/services/heuristics/types.ts +144 -0
- package/src/services/image/ImageGenerationService.ts +389 -0
- package/src/services/image/index.ts +12 -0
- package/src/services/intervention/InterventionService.ts +1352 -0
- package/src/services/intervention/index.ts +7 -0
- package/src/services/mcp/MCPManager.ts +683 -0
- package/src/services/mcp/McpNotificationDelivery.ts +139 -0
- package/src/services/mcp/McpSubscriptionService.ts +653 -0
- package/src/services/mcp/mcpInstaller.ts +130 -0
- package/src/services/nip46/Nip46SigningLog.ts +81 -0
- package/src/services/nip46/Nip46SigningService.ts +467 -0
- package/src/services/nip46/index.ts +4 -0
- package/src/services/nudge/NudgeService.ts +224 -0
- package/src/services/nudge/NudgeWhitelistService.ts +382 -0
- package/src/services/nudge/index.ts +5 -0
- package/src/services/nudge/types.ts +83 -0
- package/src/services/projects/ProjectContext.ts +672 -0
- package/src/services/projects/ProjectContextStore.ts +102 -0
- package/src/services/projects/index.ts +6 -0
- package/src/services/prompt-compiler/index.ts +15 -0
- package/src/services/prompt-compiler/prompt-compiler-service.ts +1143 -0
- package/src/services/pubkey-gate/PubkeyGateService.ts +93 -0
- package/src/services/pubkey-gate/index.ts +1 -0
- package/src/services/rag/EmbeddingProviderFactory.ts +292 -0
- package/src/services/rag/LanceDBMaintenanceService.ts +211 -0
- package/src/services/rag/RAGDatabaseService.ts +173 -0
- package/src/services/rag/RAGOperations.ts +682 -0
- package/src/services/rag/RAGService.ts +240 -0
- package/src/services/rag/RagSubscriptionService.ts +618 -0
- package/src/services/rag/rag-utils.ts +174 -0
- package/src/services/ral/PendingDelegationsRegistry.ts +168 -0
- package/src/services/ral/RALRegistry.ts +2782 -0
- package/src/services/ral/index.ts +4 -0
- package/src/services/ral/types.ts +292 -0
- package/src/services/reports/LocalReportStore.ts +380 -0
- package/src/services/reports/ReportEmbeddingService.ts +430 -0
- package/src/services/reports/ReportService.ts +440 -0
- package/src/services/reports/articleUtils.ts +52 -0
- package/src/services/reports/index.ts +7 -0
- package/src/services/scheduling/SchedulerService.ts +1057 -0
- package/src/services/scheduling/errors.ts +14 -0
- package/src/services/scheduling/index.ts +7 -0
- package/src/services/scheduling/utils.ts +77 -0
- package/src/services/search/SearchProviderRegistry.ts +78 -0
- package/src/services/search/UnifiedSearchService.ts +218 -0
- package/src/services/search/index.ts +47 -0
- package/src/services/search/projectFilter.ts +22 -0
- package/src/services/search/providers/ConversationSearchProvider.ts +48 -0
- package/src/services/search/providers/LessonSearchProvider.ts +75 -0
- package/src/services/search/providers/ReportSearchProvider.ts +49 -0
- package/src/services/search/types.ts +144 -0
- package/src/services/skill/SkillService.ts +482 -0
- package/src/services/skill/index.ts +2 -0
- package/src/services/skill/types.ts +70 -0
- package/src/services/status/OperationsStatusService.ts +276 -0
- package/src/services/status/ProjectStatusService.ts +522 -0
- package/src/services/status/index.ts +11 -0
- package/src/services/storage/PrefixKVStore.ts +242 -0
- package/src/services/storage/index.ts +1 -0
- package/src/services/system-reminder/SystemReminderUtils.ts +96 -0
- package/src/services/system-reminder/index.ts +7 -0
- package/src/services/trust-pubkeys/TrustPubkeyService.ts +325 -0
- package/src/services/trust-pubkeys/index.ts +2 -0
- package/src/telemetry/ConversationSpanManager.ts +111 -0
- package/src/telemetry/EventLoopMonitor.ts +206 -0
- package/src/telemetry/LLMSpanRegistry.ts +20 -0
- package/src/telemetry/NostrSpanProcessor.ts +89 -0
- package/src/telemetry/ToolCallSpanProcessor.ts +66 -0
- package/src/telemetry/diagnostics.ts +27 -0
- package/src/telemetry/setup.ts +120 -0
- package/src/tools/implementations/agents_discover.ts +121 -0
- package/src/tools/implementations/agents_hire.ts +127 -0
- package/src/tools/implementations/agents_list.ts +96 -0
- package/src/tools/implementations/agents_publish.ts +611 -0
- package/src/tools/implementations/agents_read.ts +173 -0
- package/src/tools/implementations/agents_write.ts +200 -0
- package/src/tools/implementations/ask.ts +411 -0
- package/src/tools/implementations/change_model.ts +141 -0
- package/src/tools/implementations/conversation_get.ts +661 -0
- package/src/tools/implementations/conversation_list.ts +377 -0
- package/src/tools/implementations/conversation_search.ts +370 -0
- package/src/tools/implementations/delegate.ts +327 -0
- package/src/tools/implementations/delegate_crossproject.ts +209 -0
- package/src/tools/implementations/delegate_followup.ts +300 -0
- package/src/tools/implementations/fs_edit.ts +162 -0
- package/src/tools/implementations/fs_glob.ts +182 -0
- package/src/tools/implementations/fs_grep.ts +513 -0
- package/src/tools/implementations/fs_read.ts +332 -0
- package/src/tools/implementations/fs_write.ts +113 -0
- package/src/tools/implementations/generate_image.ts +259 -0
- package/src/tools/implementations/home_fs.ts +515 -0
- package/src/tools/implementations/kill.ts +651 -0
- package/src/tools/implementations/learn.ts +166 -0
- package/src/tools/implementations/lesson-formatter.ts +38 -0
- package/src/tools/implementations/lesson_delete.ts +164 -0
- package/src/tools/implementations/lesson_get.ts +105 -0
- package/src/tools/implementations/lessons_list.ts +153 -0
- package/src/tools/implementations/mcp_resource_read.ts +161 -0
- package/src/tools/implementations/mcp_subscribe.ts +158 -0
- package/src/tools/implementations/mcp_subscription_stop.ts +85 -0
- package/src/tools/implementations/nostr_fetch.ts +149 -0
- package/src/tools/implementations/nostr_publish_as_user.ts +353 -0
- package/src/tools/implementations/project_list.ts +146 -0
- package/src/tools/implementations/rag_add_documents.ts +573 -0
- package/src/tools/implementations/rag_create_collection.ts +65 -0
- package/src/tools/implementations/rag_delete_collection.ts +68 -0
- package/src/tools/implementations/rag_list_collections.ts +77 -0
- package/src/tools/implementations/rag_query.ts +107 -0
- package/src/tools/implementations/rag_subscription_create.ts +105 -0
- package/src/tools/implementations/rag_subscription_delete.ts +80 -0
- package/src/tools/implementations/rag_subscription_get.ts +123 -0
- package/src/tools/implementations/rag_subscription_list.ts +128 -0
- package/src/tools/implementations/report_delete.ts +79 -0
- package/src/tools/implementations/report_read.ts +160 -0
- package/src/tools/implementations/report_write.ts +278 -0
- package/src/tools/implementations/reports_list.ts +77 -0
- package/src/tools/implementations/schedule_task.ts +104 -0
- package/src/tools/implementations/schedule_task_cancel.ts +62 -0
- package/src/tools/implementations/schedule_task_once.ts +128 -0
- package/src/tools/implementations/schedule_tasks_list.ts +79 -0
- package/src/tools/implementations/search.ts +160 -0
- package/src/tools/implementations/shell.ts +553 -0
- package/src/tools/implementations/todo.ts +260 -0
- package/src/tools/implementations/upload_blob.ts +381 -0
- package/src/tools/implementations/web_fetch.ts +153 -0
- package/src/tools/implementations/web_search.ts +250 -0
- package/src/tools/registry.ts +670 -0
- package/src/tools/types.ts +177 -0
- package/src/tools/utils.ts +256 -0
- package/src/types/event-ids.ts +320 -0
- package/src/types/index.ts +46 -0
- package/src/utils/agentFetcher.ts +107 -0
- package/src/utils/cli-error.ts +29 -0
- package/src/utils/conversation-id.ts +27 -0
- package/src/utils/conversation-utils.ts +1 -0
- package/src/utils/delegation-chain.ts +357 -0
- package/src/utils/error-handler.ts +42 -0
- package/src/utils/git/gitignore.ts +69 -0
- package/src/utils/git/index.ts +2 -0
- package/src/utils/git/initializeGitRepo.ts +204 -0
- package/src/utils/git/worktree.ts +260 -0
- package/src/utils/lessonFormatter.ts +70 -0
- package/src/utils/lessonTrust.ts +24 -0
- package/src/utils/lockfile.ts +123 -0
- package/src/utils/logger.ts +149 -0
- package/src/utils/nostr-entity-parser.ts +365 -0
- package/src/utils/process.ts +49 -0
- package/src/wrapper.ts +262 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import type { EventRoutingLogger } from "@/logging/EventRoutingLogger";
|
|
2
|
+
import { logger } from "@/utils/logger";
|
|
3
|
+
import type { Hexpubkey, NDKEvent, NDKFilter, NDKSubscription } from "@nostr-dev-kit/ndk";
|
|
4
|
+
import type NDK from "@nostr-dev-kit/ndk";
|
|
5
|
+
import { SubscriptionFilterBuilder } from "./filters/SubscriptionFilterBuilder";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Manages independent subscriptions for different event categories.
|
|
9
|
+
*
|
|
10
|
+
* Instead of a single monolithic subscription that is torn down and recreated
|
|
11
|
+
* whenever any tracked set changes, this manager maintains four independent
|
|
12
|
+
* subscription groups:
|
|
13
|
+
*
|
|
14
|
+
* 1. Static — project discovery, config updates, lesson comments (never recreated)
|
|
15
|
+
* 2. Known-projects — events tagged with project a-tags (recreated on project changes)
|
|
16
|
+
* 3. Agent-mentions — events p-tagging agents (recreated on agent pubkey changes)
|
|
17
|
+
* 4. Per-agent lessons — one per agent definition ID (individually managed)
|
|
18
|
+
*/
|
|
19
|
+
export class SubscriptionManager {
|
|
20
|
+
private ndk: NDK;
|
|
21
|
+
private eventHandler: (event: NDKEvent) => Promise<void>;
|
|
22
|
+
private routingLogger: EventRoutingLogger;
|
|
23
|
+
private whitelistedPubkeys: Set<Hexpubkey>;
|
|
24
|
+
|
|
25
|
+
// Dedup: multiple subscriptions can deliver the same event
|
|
26
|
+
private recentEventIds = new Set<string>();
|
|
27
|
+
private recentEventTimer: NodeJS.Timeout | null = null;
|
|
28
|
+
|
|
29
|
+
// Independent subscription groups
|
|
30
|
+
private staticSubscription: NDKSubscription | null = null;
|
|
31
|
+
private projectSubscription: NDKSubscription | null = null;
|
|
32
|
+
private agentMentionsSubscription: NDKSubscription | null = null;
|
|
33
|
+
private lessonSubscriptions = new Map<string, NDKSubscription>();
|
|
34
|
+
|
|
35
|
+
// Per-subscription since tracking
|
|
36
|
+
private lastProjectSubCreatedAt: number | null = null;
|
|
37
|
+
private lastAgentMentionsSubCreatedAt: number | null = null;
|
|
38
|
+
|
|
39
|
+
// Debounce for agent mentions updates
|
|
40
|
+
private agentMentionsTimer: NodeJS.Timeout | null = null;
|
|
41
|
+
private pendingAgentMentionsPubkeys: Set<Hexpubkey> | null = null;
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
ndk: NDK,
|
|
45
|
+
eventHandler: (event: NDKEvent) => Promise<void>,
|
|
46
|
+
whitelistedPubkeys: Hexpubkey[],
|
|
47
|
+
routingLogger: EventRoutingLogger
|
|
48
|
+
) {
|
|
49
|
+
this.ndk = ndk;
|
|
50
|
+
this.eventHandler = eventHandler;
|
|
51
|
+
this.whitelistedPubkeys = new Set(whitelistedPubkeys);
|
|
52
|
+
this.routingLogger = routingLogger;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Start subscriptions. Creates the static subscription immediately.
|
|
57
|
+
* Project and agent-mentions subscriptions are created later as data arrives.
|
|
58
|
+
*/
|
|
59
|
+
async start(): Promise<void> {
|
|
60
|
+
logger.debug("Starting subscription manager", {
|
|
61
|
+
whitelistedPubkeys: Array.from(this.whitelistedPubkeys).map((p) => p.slice(0, 8)),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const filters = SubscriptionFilterBuilder.buildStaticFilters(this.whitelistedPubkeys);
|
|
65
|
+
if (filters.length > 0) {
|
|
66
|
+
this.staticSubscription = this.createSub(filters, "static");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await this.routingLogger.logSubscriptionFilters({
|
|
70
|
+
filters,
|
|
71
|
+
whitelistedAuthors: this.whitelistedPubkeys.size,
|
|
72
|
+
trackedProjects: 0,
|
|
73
|
+
trackedAgents: 0,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Update known projects. Recreates the project subscription with new project IDs.
|
|
79
|
+
* Uses `since` on restarts to prevent historical event re-delivery.
|
|
80
|
+
*/
|
|
81
|
+
updateKnownProjects(projectIds: string[]): void {
|
|
82
|
+
const knownProjects = new Set(projectIds);
|
|
83
|
+
|
|
84
|
+
// Stop existing project subscription
|
|
85
|
+
if (this.projectSubscription) {
|
|
86
|
+
this.projectSubscription.stop();
|
|
87
|
+
this.projectSubscription = null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (knownProjects.size === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const since = this.lastProjectSubCreatedAt ?? undefined;
|
|
95
|
+
this.lastProjectSubCreatedAt = Math.floor(Date.now() / 1000);
|
|
96
|
+
|
|
97
|
+
const filters: NDKFilter[] = [];
|
|
98
|
+
|
|
99
|
+
const taggedFilter = SubscriptionFilterBuilder.buildProjectTaggedFilter(knownProjects, since);
|
|
100
|
+
if (taggedFilter) filters.push(taggedFilter);
|
|
101
|
+
|
|
102
|
+
const reportFilter = SubscriptionFilterBuilder.buildReportFilter(knownProjects);
|
|
103
|
+
if (reportFilter) filters.push(reportFilter);
|
|
104
|
+
|
|
105
|
+
if (filters.length > 0) {
|
|
106
|
+
this.projectSubscription = this.createSub(filters, "projects");
|
|
107
|
+
logger.debug("Project subscription updated", {
|
|
108
|
+
projects: knownProjects.size,
|
|
109
|
+
since,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Update agent mentions subscription. Debounced with 2s timer since
|
|
116
|
+
* agent additions can batch (e.g. during project boot with multiple agents).
|
|
117
|
+
*/
|
|
118
|
+
updateAgentMentions(pubkeys: Hexpubkey[]): void {
|
|
119
|
+
this.pendingAgentMentionsPubkeys = new Set(pubkeys);
|
|
120
|
+
|
|
121
|
+
if (this.agentMentionsTimer) {
|
|
122
|
+
clearTimeout(this.agentMentionsTimer);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.agentMentionsTimer = setTimeout(() => {
|
|
126
|
+
this.agentMentionsTimer = null;
|
|
127
|
+
this.applyAgentMentionsUpdate();
|
|
128
|
+
}, 2000);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private applyAgentMentionsUpdate(): void {
|
|
132
|
+
const pubkeys = this.pendingAgentMentionsPubkeys;
|
|
133
|
+
this.pendingAgentMentionsPubkeys = null;
|
|
134
|
+
|
|
135
|
+
if (!pubkeys) return;
|
|
136
|
+
|
|
137
|
+
// Stop existing agent mentions subscription
|
|
138
|
+
if (this.agentMentionsSubscription) {
|
|
139
|
+
this.agentMentionsSubscription.stop();
|
|
140
|
+
this.agentMentionsSubscription = null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (pubkeys.size === 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const since = this.lastAgentMentionsSubCreatedAt ?? undefined;
|
|
148
|
+
this.lastAgentMentionsSubCreatedAt = Math.floor(Date.now() / 1000);
|
|
149
|
+
|
|
150
|
+
const filter = SubscriptionFilterBuilder.buildAgentMentionsFilter(pubkeys, since);
|
|
151
|
+
if (filter) {
|
|
152
|
+
this.agentMentionsSubscription = this.createSub([filter], "agent-mentions");
|
|
153
|
+
logger.debug("Agent mentions subscription updated", {
|
|
154
|
+
agents: pubkeys.size,
|
|
155
|
+
since,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Add a lesson subscription for a specific agent definition ID.
|
|
162
|
+
* Uses NDK groupable to merge with other lesson subscriptions.
|
|
163
|
+
*/
|
|
164
|
+
addLessonSubscription(definitionId: string): void {
|
|
165
|
+
if (this.lessonSubscriptions.has(definitionId)) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const filter = SubscriptionFilterBuilder.buildLessonFilter(definitionId);
|
|
170
|
+
const sub = this.ndk.subscribe([filter], {
|
|
171
|
+
closeOnEose: false,
|
|
172
|
+
groupable: true,
|
|
173
|
+
onEvent: (event: NDKEvent) => this.handleEvent(event),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
this.lessonSubscriptions.set(definitionId, sub);
|
|
177
|
+
logger.debug("Lesson subscription added", {
|
|
178
|
+
definitionId: definitionId.substring(0, 12),
|
|
179
|
+
totalLessonSubs: this.lessonSubscriptions.size,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Remove a lesson subscription for a specific agent definition ID.
|
|
185
|
+
*/
|
|
186
|
+
removeLessonSubscription(definitionId: string): void {
|
|
187
|
+
const sub = this.lessonSubscriptions.get(definitionId);
|
|
188
|
+
if (sub) {
|
|
189
|
+
sub.stop();
|
|
190
|
+
this.lessonSubscriptions.delete(definitionId);
|
|
191
|
+
logger.debug("Lesson subscription removed", {
|
|
192
|
+
definitionId: definitionId.substring(0, 12),
|
|
193
|
+
totalLessonSubs: this.lessonSubscriptions.size,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Stop all subscriptions.
|
|
200
|
+
*/
|
|
201
|
+
stop(): void {
|
|
202
|
+
if (this.agentMentionsTimer) {
|
|
203
|
+
clearTimeout(this.agentMentionsTimer);
|
|
204
|
+
this.agentMentionsTimer = null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (this.recentEventTimer) {
|
|
208
|
+
clearTimeout(this.recentEventTimer);
|
|
209
|
+
this.recentEventTimer = null;
|
|
210
|
+
}
|
|
211
|
+
this.recentEventIds.clear();
|
|
212
|
+
|
|
213
|
+
if (this.staticSubscription) {
|
|
214
|
+
this.staticSubscription.stop();
|
|
215
|
+
this.staticSubscription = null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (this.projectSubscription) {
|
|
219
|
+
this.projectSubscription.stop();
|
|
220
|
+
this.projectSubscription = null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (this.agentMentionsSubscription) {
|
|
224
|
+
this.agentMentionsSubscription.stop();
|
|
225
|
+
this.agentMentionsSubscription = null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
for (const [, sub] of this.lessonSubscriptions) {
|
|
229
|
+
sub.stop();
|
|
230
|
+
}
|
|
231
|
+
this.lessonSubscriptions.clear();
|
|
232
|
+
|
|
233
|
+
logger.debug("All subscriptions stopped");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get current subscription status.
|
|
238
|
+
*/
|
|
239
|
+
getStatus(): {
|
|
240
|
+
active: boolean;
|
|
241
|
+
whitelistedPubkeys: number;
|
|
242
|
+
staticActive: boolean;
|
|
243
|
+
projectActive: boolean;
|
|
244
|
+
agentMentionsActive: boolean;
|
|
245
|
+
lessonSubscriptions: number;
|
|
246
|
+
} {
|
|
247
|
+
return {
|
|
248
|
+
active: this.staticSubscription !== null,
|
|
249
|
+
whitelistedPubkeys: this.whitelistedPubkeys.size,
|
|
250
|
+
staticActive: this.staticSubscription !== null,
|
|
251
|
+
projectActive: this.projectSubscription !== null,
|
|
252
|
+
agentMentionsActive: this.agentMentionsSubscription !== null,
|
|
253
|
+
lessonSubscriptions: this.lessonSubscriptions.size,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Create an NDK subscription with the shared event handler.
|
|
259
|
+
*/
|
|
260
|
+
private createSub(filters: NDKFilter[], label: string): NDKSubscription {
|
|
261
|
+
const sub = this.ndk.subscribe(filters, {
|
|
262
|
+
closeOnEose: false,
|
|
263
|
+
groupable: false,
|
|
264
|
+
onEvent: (event: NDKEvent) => this.handleEvent(event),
|
|
265
|
+
onEose: () => {
|
|
266
|
+
logger.debug(`Subscription EOSE received [${label}]`);
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return sub;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Handle incoming events from any subscription.
|
|
275
|
+
* Deduplicates across independent subscriptions — the same event can match
|
|
276
|
+
* both the project (#a) and agent-mentions (#p) filters.
|
|
277
|
+
*/
|
|
278
|
+
private async handleEvent(event: NDKEvent): Promise<void> {
|
|
279
|
+
if (this.recentEventIds.has(event.id)) return;
|
|
280
|
+
this.recentEventIds.add(event.id);
|
|
281
|
+
this.scheduleEventIdCleanup();
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
await this.eventHandler(event);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger.error("Error handling event in subscription", {
|
|
287
|
+
error: error instanceof Error ? error.message : String(error),
|
|
288
|
+
eventId: event.id,
|
|
289
|
+
eventKind: event.kind,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Periodically flush the seen-event set to prevent unbounded growth.
|
|
296
|
+
* Batches cleanup into 60s intervals.
|
|
297
|
+
*/
|
|
298
|
+
private scheduleEventIdCleanup(): void {
|
|
299
|
+
if (this.recentEventTimer) return;
|
|
300
|
+
this.recentEventTimer = setTimeout(() => {
|
|
301
|
+
this.recentEventIds.clear();
|
|
302
|
+
this.recentEventTimer = null;
|
|
303
|
+
}, 60_000);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import * as net from "net";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import type { StreamTransport } from "@/llm";
|
|
5
|
+
import type { LocalStreamChunk } from "@/llm";
|
|
6
|
+
import { logger } from "@/utils/logger";
|
|
7
|
+
|
|
8
|
+
const LOG_PREFIX = "[UnixSocketTransport]";
|
|
9
|
+
|
|
10
|
+
/** Extract a brief caller context from stack trace for debugging (only computed when debug logging enabled) */
|
|
11
|
+
function getCallerContext(): string | undefined {
|
|
12
|
+
// Only compute stack trace if debug logging is actually enabled
|
|
13
|
+
// This avoids the expensive stack capture in production
|
|
14
|
+
if (!logger.isLevelEnabled?.("debug")) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const stack = new Error().stack;
|
|
18
|
+
if (!stack) return undefined;
|
|
19
|
+
// Skip first 3 lines: Error, getCallerContext, cleanup/caller
|
|
20
|
+
const lines = stack.split("\n").slice(3, 6);
|
|
21
|
+
return lines.map((l) => l.trim()).join(" <- ");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Result of checking socket file info */
|
|
25
|
+
type SocketFileInfoResult =
|
|
26
|
+
| { status: "ok"; exists: true; isSocket: boolean; isFile: boolean; mode: string; mtime: string; age: string }
|
|
27
|
+
| { status: "ok"; exists: false }
|
|
28
|
+
| { status: "error"; error: string };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Unix domain socket transport for local streaming
|
|
32
|
+
* Single client connection model
|
|
33
|
+
*/
|
|
34
|
+
export class UnixSocketTransport implements StreamTransport {
|
|
35
|
+
private server: net.Server | null = null;
|
|
36
|
+
private client: net.Socket | null = null;
|
|
37
|
+
private socketPath: string;
|
|
38
|
+
|
|
39
|
+
constructor(socketPath?: string) {
|
|
40
|
+
this.socketPath = socketPath ?? this.defaultSocketPath();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private defaultSocketPath(): string {
|
|
44
|
+
const runtimeDir = process.env.XDG_RUNTIME_DIR;
|
|
45
|
+
if (runtimeDir) {
|
|
46
|
+
return path.join(runtimeDir, "tenex-stream.sock");
|
|
47
|
+
}
|
|
48
|
+
return "/tmp/tenex-stream.sock";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async start(): Promise<void> {
|
|
52
|
+
logger.info(`${LOG_PREFIX} start() called`, {
|
|
53
|
+
socketPath: this.socketPath,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Check for existing socket and log details
|
|
57
|
+
const existingSocketInfo = this.getSocketFileInfo();
|
|
58
|
+
if (existingSocketInfo.status === "ok" && existingSocketInfo.exists) {
|
|
59
|
+
logger.warn(`${LOG_PREFIX} Stale socket found before start`, {
|
|
60
|
+
socketPath: this.socketPath,
|
|
61
|
+
isSocket: existingSocketInfo.isSocket,
|
|
62
|
+
age: existingSocketInfo.age,
|
|
63
|
+
});
|
|
64
|
+
} else if (existingSocketInfo.status === "error") {
|
|
65
|
+
logger.error(`${LOG_PREFIX} Failed to check socket file before start`, {
|
|
66
|
+
socketPath: this.socketPath,
|
|
67
|
+
error: existingSocketInfo.error,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Clean up stale socket, passing the file info we already have
|
|
72
|
+
this.cleanup("start() - cleaning stale socket before creating new one", existingSocketInfo);
|
|
73
|
+
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
this.server = net.createServer((socket) => {
|
|
76
|
+
logger.info(`${LOG_PREFIX} Client connected to streaming socket`, {
|
|
77
|
+
socketPath: this.socketPath,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Only one client at a time
|
|
81
|
+
if (this.client) {
|
|
82
|
+
logger.warn(`${LOG_PREFIX} Replacing existing client connection`, {
|
|
83
|
+
socketPath: this.socketPath,
|
|
84
|
+
});
|
|
85
|
+
this.client.destroy();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.client = socket;
|
|
89
|
+
|
|
90
|
+
socket.on("close", () => {
|
|
91
|
+
logger.info(`${LOG_PREFIX} Client disconnected from streaming socket`, {
|
|
92
|
+
socketPath: this.socketPath,
|
|
93
|
+
});
|
|
94
|
+
if (this.client === socket) {
|
|
95
|
+
this.client = null;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
socket.on("error", (err) => {
|
|
100
|
+
logger.error(`${LOG_PREFIX} Socket client error`, {
|
|
101
|
+
error: err.message,
|
|
102
|
+
socketPath: this.socketPath,
|
|
103
|
+
});
|
|
104
|
+
if (this.client === socket) {
|
|
105
|
+
this.client = null;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this.server.on("error", (err) => {
|
|
111
|
+
logger.error(`${LOG_PREFIX} Socket server error`, {
|
|
112
|
+
error: err.message,
|
|
113
|
+
socketPath: this.socketPath,
|
|
114
|
+
});
|
|
115
|
+
reject(err);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
this.server.listen(this.socketPath, () => {
|
|
119
|
+
logger.info(`${LOG_PREFIX} Streaming socket started and listening`, {
|
|
120
|
+
socketPath: this.socketPath,
|
|
121
|
+
});
|
|
122
|
+
resolve();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
write(chunk: LocalStreamChunk): void {
|
|
128
|
+
if (!this.client) return;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const line = JSON.stringify(chunk) + "\n";
|
|
132
|
+
this.client.write(line);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
logger.error(`${LOG_PREFIX} Failed to write chunk`, {
|
|
135
|
+
error: err instanceof Error ? err.message : String(err),
|
|
136
|
+
socketPath: this.socketPath,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isConnected(): boolean {
|
|
142
|
+
return this.client !== null && !this.client.destroyed;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async stop(): Promise<void> {
|
|
146
|
+
const callerContext = getCallerContext();
|
|
147
|
+
logger.info(`${LOG_PREFIX} stop() called`, {
|
|
148
|
+
socketPath: this.socketPath,
|
|
149
|
+
hasClient: !!this.client,
|
|
150
|
+
hasServer: !!this.server,
|
|
151
|
+
...(callerContext && { callerContext }),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (this.client) {
|
|
155
|
+
logger.info(`${LOG_PREFIX} Destroying client connection in stop()`, {
|
|
156
|
+
socketPath: this.socketPath,
|
|
157
|
+
});
|
|
158
|
+
this.client.destroy();
|
|
159
|
+
this.client = null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return new Promise((resolve) => {
|
|
163
|
+
if (this.server) {
|
|
164
|
+
logger.info(`${LOG_PREFIX} Closing server in stop()`, {
|
|
165
|
+
socketPath: this.socketPath,
|
|
166
|
+
});
|
|
167
|
+
this.server.close(() => {
|
|
168
|
+
logger.info(`${LOG_PREFIX} Server closed, now calling cleanup()`, {
|
|
169
|
+
socketPath: this.socketPath,
|
|
170
|
+
});
|
|
171
|
+
this.cleanup("stop() - server closed callback");
|
|
172
|
+
logger.info(`${LOG_PREFIX} stop() completed`, {
|
|
173
|
+
socketPath: this.socketPath,
|
|
174
|
+
});
|
|
175
|
+
resolve();
|
|
176
|
+
});
|
|
177
|
+
} else {
|
|
178
|
+
logger.info(`${LOG_PREFIX} stop() called but no server to close`, {
|
|
179
|
+
socketPath: this.socketPath,
|
|
180
|
+
});
|
|
181
|
+
resolve();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get file info for the socket path (for debugging)
|
|
188
|
+
* Returns discriminated result: ok (exists/not), or error
|
|
189
|
+
*/
|
|
190
|
+
private getSocketFileInfo(): SocketFileInfoResult {
|
|
191
|
+
try {
|
|
192
|
+
const stats = fs.lstatSync(this.socketPath);
|
|
193
|
+
const ageMs = Date.now() - stats.mtime.getTime();
|
|
194
|
+
return {
|
|
195
|
+
status: "ok",
|
|
196
|
+
exists: true,
|
|
197
|
+
isSocket: stats.isSocket(),
|
|
198
|
+
isFile: stats.isFile(),
|
|
199
|
+
mode: stats.mode.toString(8),
|
|
200
|
+
mtime: stats.mtime.toISOString(),
|
|
201
|
+
age: `${Math.floor(ageMs / 1000)}s`,
|
|
202
|
+
};
|
|
203
|
+
} catch (err) {
|
|
204
|
+
// ENOENT means file doesn't exist - that's a valid "missing" result
|
|
205
|
+
if (err instanceof Error && "code" in err && (err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
206
|
+
return { status: "ok", exists: false };
|
|
207
|
+
}
|
|
208
|
+
// Any other error (permission, IO, etc.) is an actual error
|
|
209
|
+
return {
|
|
210
|
+
status: "error",
|
|
211
|
+
error: err instanceof Error ? err.message : String(err),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check if socket exists and is healthy
|
|
218
|
+
*/
|
|
219
|
+
checkSocketHealth(): {
|
|
220
|
+
exists: boolean;
|
|
221
|
+
isSocket: boolean;
|
|
222
|
+
serverRunning: boolean;
|
|
223
|
+
clientConnected: boolean;
|
|
224
|
+
} {
|
|
225
|
+
const info = this.getSocketFileInfo();
|
|
226
|
+
const exists = info.status === "ok" && info.exists;
|
|
227
|
+
const isSocket = info.status === "ok" && info.exists && info.isSocket;
|
|
228
|
+
const result = {
|
|
229
|
+
exists,
|
|
230
|
+
isSocket,
|
|
231
|
+
serverRunning: this.server !== null && this.server.listening,
|
|
232
|
+
clientConnected: this.isConnected(),
|
|
233
|
+
};
|
|
234
|
+
logger.debug(`${LOG_PREFIX} Health check`, {
|
|
235
|
+
socketPath: this.socketPath,
|
|
236
|
+
...result,
|
|
237
|
+
});
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Clean up socket file if it exists and is safe to remove
|
|
243
|
+
* @param reason - Why cleanup is being called (for logging)
|
|
244
|
+
* @param fileInfo - Optional pre-fetched file info to avoid redundant FS calls
|
|
245
|
+
*/
|
|
246
|
+
private cleanup(reason: string, fileInfo?: SocketFileInfoResult): void {
|
|
247
|
+
const callerContext = getCallerContext();
|
|
248
|
+
|
|
249
|
+
logger.info(`${LOG_PREFIX} cleanup() called`, {
|
|
250
|
+
socketPath: this.socketPath,
|
|
251
|
+
reason,
|
|
252
|
+
...(callerContext && { callerContext }),
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Use provided file info or fetch it
|
|
256
|
+
const info = fileInfo ?? this.getSocketFileInfo();
|
|
257
|
+
|
|
258
|
+
// Handle error case
|
|
259
|
+
if (info.status === "error") {
|
|
260
|
+
logger.error(`${LOG_PREFIX} cleanup() cannot proceed - failed to check socket file`, {
|
|
261
|
+
socketPath: this.socketPath,
|
|
262
|
+
reason,
|
|
263
|
+
error: info.error,
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Nothing to clean up
|
|
269
|
+
if (!info.exists) {
|
|
270
|
+
logger.debug(`${LOG_PREFIX} cleanup() - socket does not exist, nothing to unlink`, {
|
|
271
|
+
socketPath: this.socketPath,
|
|
272
|
+
reason,
|
|
273
|
+
});
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// CRITICAL SAFETY CHECK: Only unlink if it's actually a socket
|
|
278
|
+
if (!info.isSocket) {
|
|
279
|
+
logger.error(`${LOG_PREFIX} cleanup() REFUSED to unlink - path is not a socket`, {
|
|
280
|
+
socketPath: this.socketPath,
|
|
281
|
+
reason,
|
|
282
|
+
isFile: info.isFile,
|
|
283
|
+
mode: info.mode,
|
|
284
|
+
});
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Safe to unlink - it's a socket
|
|
289
|
+
try {
|
|
290
|
+
logger.info(`${LOG_PREFIX} About to unlink socket`, {
|
|
291
|
+
socketPath: this.socketPath,
|
|
292
|
+
reason,
|
|
293
|
+
age: info.age,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
fs.unlinkSync(this.socketPath);
|
|
297
|
+
|
|
298
|
+
logger.info(`${LOG_PREFIX} Socket unlinked successfully`, {
|
|
299
|
+
socketPath: this.socketPath,
|
|
300
|
+
reason,
|
|
301
|
+
});
|
|
302
|
+
} catch (err) {
|
|
303
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
304
|
+
const errorCode = err instanceof Error && "code" in err ? (err as NodeJS.ErrnoException).code : undefined;
|
|
305
|
+
|
|
306
|
+
logger.error(`${LOG_PREFIX} cleanup() failed to unlink socket`, {
|
|
307
|
+
socketPath: this.socketPath,
|
|
308
|
+
reason,
|
|
309
|
+
error: errorMessage,
|
|
310
|
+
errorCode,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
getSocketPath(): string {
|
|
316
|
+
return this.socketPath;
|
|
317
|
+
}
|
|
318
|
+
}
|