@lenne.tech/nest-server 11.25.6 → 11.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/configurable-features.md +2 -0
- package/CLAUDE.md +18 -0
- package/FRAMEWORK-API.md +48 -1
- package/dist/config.env.js +35 -1
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/config.helper.js +2 -2
- package/dist/core/common/helpers/config.helper.js.map +1 -1
- package/dist/core/common/interceptors/check-security.interceptor.js +9 -2
- package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +89 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.d.ts +21 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.js +191 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.js.map +1 -0
- package/dist/core/modules/ai/core-ai.controller.d.ts +90 -0
- package/dist/core/modules/ai/core-ai.controller.js +576 -0
- package/dist/core/modules/ai/core-ai.controller.js.map +1 -0
- package/dist/core/modules/ai/core-ai.module.d.ts +43 -0
- package/dist/core/modules/ai/core-ai.module.js +164 -0
- package/dist/core/modules/ai/core-ai.module.js.map +1 -0
- package/dist/core/modules/ai/core-ai.resolver.d.ts +84 -0
- package/dist/core/modules/ai/core-ai.resolver.js +530 -0
- package/dist/core/modules/ai/core-ai.resolver.js.map +1 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.d.ts +7 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js +16 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js.map +1 -0
- package/dist/core/modules/ai/hooks/ai-hook.base.d.ts +10 -0
- package/dist/core/modules/ai/hooks/ai-hook.base.js +30 -0
- package/dist/core/modules/ai/hooks/ai-hook.base.js.map +1 -0
- package/dist/core/modules/ai/hooks/ai-hook.registry.d.ts +22 -0
- package/dist/core/modules/ai/hooks/ai-hook.registry.js +98 -0
- package/dist/core/modules/ai/hooks/ai-hook.registry.js.map +1 -0
- package/dist/core/modules/ai/index.d.ts +69 -0
- package/dist/core/modules/ai/index.js +86 -0
- package/dist/core/modules/ai/index.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-budget-limit-create.input.d.ts +5 -0
- package/dist/core/modules/ai/inputs/core-ai-budget-limit-create.input.js +43 -0
- package/dist/core/modules/ai/inputs/core-ai-budget-limit-create.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-budget-limit.input.d.ts +7 -0
- package/dist/core/modules/ai/inputs/core-ai-budget-limit.input.js +74 -0
- package/dist/core/modules/ai/inputs/core-ai-budget-limit.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-connection-create.input.d.ts +6 -0
- package/dist/core/modules/ai/inputs/core-ai-connection-create.input.js +49 -0
- package/dist/core/modules/ai/inputs/core-ai-connection-create.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-connection-preference.input.d.ts +6 -0
- package/dist/core/modules/ai/inputs/core-ai-connection-preference.input.js +60 -0
- package/dist/core/modules/ai/inputs/core-ai-connection-preference.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-connection.input.d.ts +23 -0
- package/dist/core/modules/ai/inputs/core-ai-connection.input.js +229 -0
- package/dist/core/modules/ai/inputs/core-ai-connection.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-conversation-create.input.d.ts +3 -0
- package/dist/core/modules/ai/inputs/core-ai-conversation-create.input.js +21 -0
- package/dist/core/modules/ai/inputs/core-ai-conversation-create.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-conversation.input.d.ts +4 -0
- package/dist/core/modules/ai/inputs/core-ai-conversation.input.js +42 -0
- package/dist/core/modules/ai/inputs/core-ai-conversation.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-create.input.d.ts +10 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-create.input.js +74 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-create.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-hint-create.input.d.ts +5 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-hint-create.input.js +41 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-hint-create.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-hint.input.d.ts +6 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-hint.input.js +63 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-hint.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-update.input.d.ts +10 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-update.input.js +70 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt-update.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt.input.d.ts +18 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt.input.js +131 -0
- package/dist/core/modules/ai/inputs/core-ai-prompt.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-slot-create.input.d.ts +5 -0
- package/dist/core/modules/ai/inputs/core-ai-slot-create.input.js +41 -0
- package/dist/core/modules/ai/inputs/core-ai-slot-create.input.js.map +1 -0
- package/dist/core/modules/ai/inputs/core-ai-slot-update.input.d.ts +9 -0
- package/dist/core/modules/ai/inputs/core-ai-slot-update.input.js +91 -0
- package/dist/core/modules/ai/inputs/core-ai-slot-update.input.js.map +1 -0
- package/dist/core/modules/ai/interfaces/ai-hook.interface.d.ts +28 -0
- package/dist/core/modules/ai/interfaces/ai-hook.interface.js +3 -0
- package/dist/core/modules/ai/interfaces/ai-hook.interface.js.map +1 -0
- package/dist/core/modules/ai/interfaces/ai-placeholder.interface.d.ts +21 -0
- package/dist/core/modules/ai/interfaces/ai-placeholder.interface.js +3 -0
- package/dist/core/modules/ai/interfaces/ai-placeholder.interface.js.map +1 -0
- package/dist/core/modules/ai/interfaces/ai-tool.interface.d.ts +30 -0
- package/dist/core/modules/ai/interfaces/ai-tool.interface.js +3 -0
- package/dist/core/modules/ai/interfaces/ai-tool.interface.js.map +1 -0
- package/dist/core/modules/ai/interfaces/llm-provider.interface.d.ts +55 -0
- package/dist/core/modules/ai/interfaces/llm-provider.interface.js +3 -0
- package/dist/core/modules/ai/interfaces/llm-provider.interface.js.map +1 -0
- package/dist/core/modules/ai/interfaces/resolved-ai-connection.interface.d.ts +16 -0
- package/dist/core/modules/ai/interfaces/resolved-ai-connection.interface.js +3 -0
- package/dist/core/modules/ai/interfaces/resolved-ai-connection.interface.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-action.model.d.ts +6 -0
- package/dist/core/modules/ai/models/core-ai-action.model.js +44 -0
- package/dist/core/modules/ai/models/core-ai-action.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-available-connection.model.d.ts +8 -0
- package/dist/core/modules/ai/models/core-ai-available-connection.model.js +53 -0
- package/dist/core/modules/ai/models/core-ai-available-connection.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-budget-limit.model.d.ts +97 -0
- package/dist/core/modules/ai/models/core-ai-budget-limit.model.js +79 -0
- package/dist/core/modules/ai/models/core-ai-budget-limit.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-connection-preference.model.d.ts +91 -0
- package/dist/core/modules/ai/models/core-ai-connection-preference.model.js +67 -0
- package/dist/core/modules/ai/models/core-ai-connection-preference.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-connection.model.d.ts +200 -0
- package/dist/core/modules/ai/models/core-ai-connection.model.js +263 -0
- package/dist/core/modules/ai/models/core-ai-connection.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-conversation.model.d.ts +93 -0
- package/dist/core/modules/ai/models/core-ai-conversation.model.js +82 -0
- package/dist/core/modules/ai/models/core-ai-conversation.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-interaction.model.d.ts +133 -0
- package/dist/core/modules/ai/models/core-ai-interaction.model.js +137 -0
- package/dist/core/modules/ai/models/core-ai-interaction.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-message.model.d.ts +5 -0
- package/dist/core/modules/ai/models/core-ai-message.model.js +38 -0
- package/dist/core/modules/ai/models/core-ai-message.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-mode.model.d.ts +109 -0
- package/dist/core/modules/ai/models/core-ai-mode.model.js +100 -0
- package/dist/core/modules/ai/models/core-ai-mode.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-prompt-hint.model.d.ts +103 -0
- package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js +89 -0
- package/dist/core/modules/ai/models/core-ai-prompt-hint.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-prompt.model.d.ts +122 -0
- package/dist/core/modules/ai/models/core-ai-prompt.model.js +117 -0
- package/dist/core/modules/ai/models/core-ai-prompt.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-response.model.d.ts +29 -0
- package/dist/core/modules/ai/models/core-ai-response.model.js +105 -0
- package/dist/core/modules/ai/models/core-ai-response.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-slot.model.d.ts +121 -0
- package/dist/core/modules/ai/models/core-ai-slot.model.js +119 -0
- package/dist/core/modules/ai/models/core-ai-slot.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-tool-grant.model.d.ts +103 -0
- package/dist/core/modules/ai/models/core-ai-tool-grant.model.js +88 -0
- package/dist/core/modules/ai/models/core-ai-tool-grant.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-tool-policy.model.d.ts +122 -0
- package/dist/core/modules/ai/models/core-ai-tool-policy.model.js +121 -0
- package/dist/core/modules/ai/models/core-ai-tool-policy.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-usage-info.model.d.ts +24 -0
- package/dist/core/modules/ai/models/core-ai-usage-info.model.js +127 -0
- package/dist/core/modules/ai/models/core-ai-usage-info.model.js.map +1 -0
- package/dist/core/modules/ai/models/core-ai-usage.model.d.ts +5 -0
- package/dist/core/modules/ai/models/core-ai-usage.model.js +38 -0
- package/dist/core/modules/ai/models/core-ai-usage.model.js.map +1 -0
- package/dist/core/modules/ai/providers/claude-cli.provider.d.ts +16 -0
- package/dist/core/modules/ai/providers/claude-cli.provider.js +151 -0
- package/dist/core/modules/ai/providers/claude-cli.provider.js.map +1 -0
- package/dist/core/modules/ai/providers/llm-provider.factory.d.ts +10 -0
- package/dist/core/modules/ai/providers/llm-provider.factory.js +45 -0
- package/dist/core/modules/ai/providers/llm-provider.factory.js.map +1 -0
- package/dist/core/modules/ai/providers/openai-compatible.provider.d.ts +32 -0
- package/dist/core/modules/ai/providers/openai-compatible.provider.js +236 -0
- package/dist/core/modules/ai/providers/openai-compatible.provider.js.map +1 -0
- package/dist/core/modules/ai/services/ai-crypto.service.d.ts +11 -0
- package/dist/core/modules/ai/services/ai-crypto.service.js +84 -0
- package/dist/core/modules/ai/services/ai-crypto.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +42 -0
- package/dist/core/modules/ai/services/core-ai-budget.service.js +211 -0
- package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +18 -0
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +53 -0
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-connection-resolver.service.d.ts +55 -0
- package/dist/core/modules/ai/services/core-ai-connection-resolver.service.js +177 -0
- package/dist/core/modules/ai/services/core-ai-connection-resolver.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +43 -0
- package/dist/core/modules/ai/services/core-ai-connection.service.js +272 -0
- package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +26 -0
- package/dist/core/modules/ai/services/core-ai-conversation.service.js +72 -0
- package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +13 -0
- package/dist/core/modules/ai/services/core-ai-interaction.service.js +52 -0
- package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-mcp-client.service.d.ts +51 -0
- package/dist/core/modules/ai/services/core-ai-mcp-client.service.js +123 -0
- package/dist/core/modules/ai/services/core-ai-mcp-client.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.d.ts +68 -0
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js +279 -0
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-mcp.service.d.ts +22 -0
- package/dist/core/modules/ai/services/core-ai-mcp.service.js +59 -0
- package/dist/core/modules/ai/services/core-ai-mcp.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +14 -0
- package/dist/core/modules/ai/services/core-ai-mode.service.js +56 -0
- package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-placeholder.registry.d.ts +15 -0
- package/dist/core/modules/ai/services/core-ai-placeholder.registry.js +102 -0
- package/dist/core/modules/ai/services/core-ai-placeholder.registry.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-prompt-builder.service.d.ts +42 -0
- package/dist/core/modules/ai/services/core-ai-prompt-builder.service.js +136 -0
- package/dist/core/modules/ai/services/core-ai-prompt-builder.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +29 -0
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +98 -0
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +21 -0
- package/dist/core/modules/ai/services/core-ai-prompt.service.js +143 -0
- package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +51 -0
- package/dist/core/modules/ai/services/core-ai-slot.service.js +372 -0
- package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +22 -0
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +97 -0
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +23 -0
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +102 -0
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai.service.d.ts +144 -0
- package/dist/core/modules/ai/services/core-ai.service.js +991 -0
- package/dist/core/modules/ai/services/core-ai.service.js.map +1 -0
- package/dist/core/modules/ai/tools/ai-tool.base.d.ts +15 -0
- package/dist/core/modules/ai/tools/ai-tool.base.js +30 -0
- package/dist/core/modules/ai/tools/ai-tool.base.js.map +1 -0
- package/dist/core/modules/ai/tools/ai-tool.registry.d.ts +19 -0
- package/dist/core/modules/ai/tools/ai-tool.registry.js +71 -0
- package/dist/core/modules/ai/tools/ai-tool.registry.js.map +1 -0
- package/dist/core/modules/ai/tools/ask-user-question.tool.d.ts +38 -0
- package/dist/core/modules/ai/tools/ask-user-question.tool.js +65 -0
- package/dist/core/modules/ai/tools/ask-user-question.tool.js.map +1 -0
- package/dist/core/modules/ai/tools/search-tools.tool.d.ts +24 -0
- package/dist/core/modules/ai/tools/search-tools.tool.js +68 -0
- package/dist/core/modules/ai/tools/search-tools.tool.js.map +1 -0
- package/dist/core/modules/error-code/error-codes.d.ts +81 -0
- package/dist/core/modules/error-code/error-codes.js +72 -0
- package/dist/core/modules/error-code/error-codes.js.map +1 -1
- package/dist/core.module.js +8 -0
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/ai/ai-tools.module.d.ts +5 -0
- package/dist/server/modules/ai/ai-tools.module.js +36 -0
- package/dist/server/modules/ai/ai-tools.module.js.map +1 -0
- package/dist/server/modules/ai/tools/delete-user.tool.d.ts +30 -0
- package/dist/server/modules/ai/tools/delete-user.tool.js +49 -0
- package/dist/server/modules/ai/tools/delete-user.tool.js.map +1 -0
- package/dist/server/modules/ai/tools/find-users.tool.d.ts +30 -0
- package/dist/server/modules/ai/tools/find-users.tool.js +52 -0
- package/dist/server/modules/ai/tools/find-users.tool.js.map +1 -0
- package/dist/server/modules/ai/tools/get-user.tool.d.ts +26 -0
- package/dist/server/modules/ai/tools/get-user.tool.js +48 -0
- package/dist/server/modules/ai/tools/get-user.tool.js.map +1 -0
- package/dist/server/modules/ai/tools/update-user-job-title.tool.d.ts +32 -0
- package/dist/server/modules/ai/tools/update-user-job-title.tool.js +54 -0
- package/dist/server/modules/ai/tools/update-user-job-title.tool.js.map +1 -0
- package/dist/server/modules/error-code/error-codes.d.ts +9 -0
- package/dist/server/server.module.js +2 -0
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +10 -0
- package/migration-guides/11.25.x-to-11.26.0.md +278 -0
- package/package.json +2 -1
- package/src/config.env.ts +48 -1
- package/src/core/common/helpers/config.helper.ts +5 -2
- package/src/core/common/interceptors/check-security.interceptor.ts +13 -3
- package/src/core/common/interfaces/server-options.interface.ts +312 -0
- package/src/core/modules/ai/INTEGRATION-CHECKLIST.md +220 -0
- package/src/core/modules/ai/README.md +526 -0
- package/src/core/modules/ai/core-ai-mcp.controller.ts +223 -0
- package/src/core/modules/ai/core-ai.controller.ts +547 -0
- package/src/core/modules/ai/core-ai.module.ts +269 -0
- package/src/core/modules/ai/core-ai.resolver.ts +526 -0
- package/src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts +35 -0
- package/src/core/modules/ai/hooks/ai-hook.base.ts +23 -0
- package/src/core/modules/ai/hooks/ai-hook.registry.ts +119 -0
- package/src/core/modules/ai/index.ts +70 -0
- package/src/core/modules/ai/inputs/core-ai-budget-limit-create.input.ts +27 -0
- package/src/core/modules/ai/inputs/core-ai-budget-limit.input.ts +52 -0
- package/src/core/modules/ai/inputs/core-ai-connection-create.input.ts +32 -0
- package/src/core/modules/ai/inputs/core-ai-connection-preference.input.ts +42 -0
- package/src/core/modules/ai/inputs/core-ai-connection.input.ts +235 -0
- package/src/core/modules/ai/inputs/core-ai-conversation-create.input.ts +13 -0
- package/src/core/modules/ai/inputs/core-ai-conversation.input.ts +33 -0
- package/src/core/modules/ai/inputs/core-ai-prompt-create.input.ts +50 -0
- package/src/core/modules/ai/inputs/core-ai-prompt-hint-create.input.ts +26 -0
- package/src/core/modules/ai/inputs/core-ai-prompt-hint.input.ts +43 -0
- package/src/core/modules/ai/inputs/core-ai-prompt-update.input.ts +42 -0
- package/src/core/modules/ai/inputs/core-ai-prompt.input.ts +144 -0
- package/src/core/modules/ai/inputs/core-ai-slot-create.input.ts +26 -0
- package/src/core/modules/ai/inputs/core-ai-slot-update.input.ts +65 -0
- package/src/core/modules/ai/interfaces/ai-hook.interface.ts +74 -0
- package/src/core/modules/ai/interfaces/ai-placeholder.interface.ts +49 -0
- package/src/core/modules/ai/interfaces/ai-tool.interface.ts +112 -0
- package/src/core/modules/ai/interfaces/llm-provider.interface.ts +187 -0
- package/src/core/modules/ai/interfaces/resolved-ai-connection.interface.ts +55 -0
- package/src/core/modules/ai/models/core-ai-action.model.ts +37 -0
- package/src/core/modules/ai/models/core-ai-available-connection.model.ts +42 -0
- package/src/core/modules/ai/models/core-ai-budget-limit.model.ts +82 -0
- package/src/core/modules/ai/models/core-ai-connection-preference.model.ts +72 -0
- package/src/core/modules/ai/models/core-ai-connection.model.ts +330 -0
- package/src/core/modules/ai/models/core-ai-conversation.model.ts +87 -0
- package/src/core/modules/ai/models/core-ai-interaction.model.ts +145 -0
- package/src/core/modules/ai/models/core-ai-message.model.ts +29 -0
- package/src/core/modules/ai/models/core-ai-mode.model.ts +93 -0
- package/src/core/modules/ai/models/core-ai-prompt-hint.model.ts +98 -0
- package/src/core/modules/ai/models/core-ai-prompt.model.ts +120 -0
- package/src/core/modules/ai/models/core-ai-response.model.ts +119 -0
- package/src/core/modules/ai/models/core-ai-slot.model.ts +146 -0
- package/src/core/modules/ai/models/core-ai-tool-grant.model.ts +87 -0
- package/src/core/modules/ai/models/core-ai-tool-policy.model.ts +115 -0
- package/src/core/modules/ai/models/core-ai-usage-info.model.ts +107 -0
- package/src/core/modules/ai/models/core-ai-usage.model.ts +29 -0
- package/src/core/modules/ai/providers/claude-cli.provider.ts +240 -0
- package/src/core/modules/ai/providers/llm-provider.factory.ts +63 -0
- package/src/core/modules/ai/providers/openai-compatible.provider.ts +329 -0
- package/src/core/modules/ai/services/ai-crypto.service.ts +120 -0
- package/src/core/modules/ai/services/core-ai-budget.service.ts +297 -0
- package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +74 -0
- package/src/core/modules/ai/services/core-ai-connection-resolver.service.ts +308 -0
- package/src/core/modules/ai/services/core-ai-connection.service.ts +387 -0
- package/src/core/modules/ai/services/core-ai-conversation.service.ts +105 -0
- package/src/core/modules/ai/services/core-ai-interaction.service.ts +56 -0
- package/src/core/modules/ai/services/core-ai-mcp-client.service.ts +169 -0
- package/src/core/modules/ai/services/core-ai-mcp-oauth.service.ts +445 -0
- package/src/core/modules/ai/services/core-ai-mcp.service.ts +94 -0
- package/src/core/modules/ai/services/core-ai-mode.service.ts +43 -0
- package/src/core/modules/ai/services/core-ai-placeholder.registry.ts +123 -0
- package/src/core/modules/ai/services/core-ai-prompt-builder.service.ts +228 -0
- package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +133 -0
- package/src/core/modules/ai/services/core-ai-prompt.service.ts +164 -0
- package/src/core/modules/ai/services/core-ai-slot.service.ts +526 -0
- package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +107 -0
- package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +124 -0
- package/src/core/modules/ai/services/core-ai.service.ts +1396 -0
- package/src/core/modules/ai/tools/ai-tool.base.ts +46 -0
- package/src/core/modules/ai/tools/ai-tool.registry.ts +124 -0
- package/src/core/modules/ai/tools/ask-user-question.tool.ts +76 -0
- package/src/core/modules/ai/tools/search-tools.tool.ts +70 -0
- package/src/core/modules/error-code/error-codes.ts +86 -0
- package/src/core.module.ts +12 -0
- package/src/index.ts +6 -0
- package/src/server/modules/ai/ai-tools.module.ts +33 -0
- package/src/server/modules/ai/tools/delete-user.tool.ts +44 -0
- package/src/server/modules/ai/tools/find-users.tool.ts +49 -0
- package/src/server/modules/ai/tools/get-user.tool.ts +43 -0
- package/src/server/modules/ai/tools/update-user-job-title.tool.ts +62 -0
- package/src/server/server.module.ts +5 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Field, Int, ObjectType } from '@nestjs/graphql';
|
|
2
|
+
|
|
3
|
+
import { Restricted } from '../../../common/decorators/restricted.decorator';
|
|
4
|
+
import { RoleEnum } from '../../../common/enums/role.enum';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Compact token-budget summary attached to every prompt response.
|
|
8
|
+
*
|
|
9
|
+
* Kept small on purpose: this prompt's token cost plus the remaining/used totals
|
|
10
|
+
* and the reset time for the binding (user) scope. The full breakdown (user +
|
|
11
|
+
* tenant scopes, prompts + tokens) is available via the `aiUsage` query / `GET /ai/usage`.
|
|
12
|
+
*/
|
|
13
|
+
@ObjectType({ description: 'Compact token-budget summary for a prompt response' })
|
|
14
|
+
@Restricted(RoleEnum.S_EVERYONE)
|
|
15
|
+
export class CoreAiBudgetSummary {
|
|
16
|
+
/**
|
|
17
|
+
* Effective token limit for the binding scope (resolution: user → tenant → LLM
|
|
18
|
+
* context window → null = no limit). Frontends use this to drive a usage progress
|
|
19
|
+
* bar; null/undefined = no limit → hide the bar.
|
|
20
|
+
*/
|
|
21
|
+
@Field(() => Int, { description: 'Effective token limit (null = no limit)', nullable: true })
|
|
22
|
+
maxTokens?: number;
|
|
23
|
+
|
|
24
|
+
/** Tokens this prompt consumed. */
|
|
25
|
+
@Field(() => Int, { description: 'Tokens consumed by this prompt', nullable: true })
|
|
26
|
+
promptTokens?: number;
|
|
27
|
+
|
|
28
|
+
/** Remaining tokens in the current period (null = unlimited). */
|
|
29
|
+
@Field(() => Int, { description: 'Remaining tokens in the current period (null = unlimited)', nullable: true })
|
|
30
|
+
remainingTokens?: number;
|
|
31
|
+
|
|
32
|
+
/** When the current period resets (null = never). */
|
|
33
|
+
@Field(() => Date, { description: 'When the current budget period resets (null = never)', nullable: true })
|
|
34
|
+
resetAt?: Date;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Which scope yielded {@link maxTokens} ('user', 'tenant' or 'llm').
|
|
38
|
+
*/
|
|
39
|
+
@Field(() => String, { description: "Which scope yielded the limit ('user', 'tenant' or 'llm')", nullable: true })
|
|
40
|
+
scope?: string;
|
|
41
|
+
|
|
42
|
+
/** Tokens used so far in the current period (user scope). */
|
|
43
|
+
@Field(() => Int, { description: 'Tokens used so far in the current period', nullable: true })
|
|
44
|
+
usedTokens?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Token/prompt usage for a single scope (user or tenant).
|
|
49
|
+
*/
|
|
50
|
+
@ObjectType({ description: 'AI usage for a single scope (user or tenant)' })
|
|
51
|
+
@Restricted(RoleEnum.S_EVERYONE)
|
|
52
|
+
export class CoreAiUsageScope {
|
|
53
|
+
/** Maximum prompts per period (null = unlimited). */
|
|
54
|
+
@Field(() => Int, { description: 'Maximum prompts per period (null = unlimited)', nullable: true })
|
|
55
|
+
maxPrompts?: number;
|
|
56
|
+
|
|
57
|
+
/** Maximum tokens per period (null = unlimited). */
|
|
58
|
+
@Field(() => Int, { description: 'Maximum tokens per period (null = unlimited)', nullable: true })
|
|
59
|
+
maxTokens?: number;
|
|
60
|
+
|
|
61
|
+
/** Reset period ('day', 'month' or 'none'). */
|
|
62
|
+
@Field(() => String, { description: "Reset period ('day', 'month', 'none')" })
|
|
63
|
+
period: string;
|
|
64
|
+
|
|
65
|
+
/** Remaining prompts (null = unlimited). */
|
|
66
|
+
@Field(() => Int, { description: 'Remaining prompts (null = unlimited)', nullable: true })
|
|
67
|
+
remainingPrompts?: number;
|
|
68
|
+
|
|
69
|
+
/** Remaining tokens (null = unlimited). */
|
|
70
|
+
@Field(() => Int, { description: 'Remaining tokens (null = unlimited)', nullable: true })
|
|
71
|
+
remainingTokens?: number;
|
|
72
|
+
|
|
73
|
+
/** When the period resets (null = never). */
|
|
74
|
+
@Field(() => Date, { description: 'When the period resets (null = never)', nullable: true })
|
|
75
|
+
resetAt?: Date;
|
|
76
|
+
|
|
77
|
+
/** Scope id (user id or tenant id). */
|
|
78
|
+
@Field(() => String, { description: 'Scope reference id', nullable: true })
|
|
79
|
+
refId?: string;
|
|
80
|
+
|
|
81
|
+
/** Scope name ('user' or 'tenant'). */
|
|
82
|
+
@Field(() => String, { description: "Scope: 'user' or 'tenant'" })
|
|
83
|
+
scope: string;
|
|
84
|
+
|
|
85
|
+
/** Prompts used in the current period. */
|
|
86
|
+
@Field(() => Int, { description: 'Prompts used in the current period' })
|
|
87
|
+
usedPrompts: number;
|
|
88
|
+
|
|
89
|
+
/** Tokens used in the current period. */
|
|
90
|
+
@Field(() => Int, { description: 'Tokens used in the current period' })
|
|
91
|
+
usedTokens: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Full AI usage info: user scope plus the tenant scope when a tenant context exists.
|
|
96
|
+
*/
|
|
97
|
+
@ObjectType({ description: 'Full AI token usage for the current user (and tenant)' })
|
|
98
|
+
@Restricted(RoleEnum.S_EVERYONE)
|
|
99
|
+
export class CoreAiUsageInfo {
|
|
100
|
+
/** Tenant-scope usage (present when a tenant context exists). */
|
|
101
|
+
@Field(() => CoreAiUsageScope, { description: 'Tenant-scope usage', nullable: true })
|
|
102
|
+
tenant?: CoreAiUsageScope;
|
|
103
|
+
|
|
104
|
+
/** User-scope usage. */
|
|
105
|
+
@Field(() => CoreAiUsageScope, { description: 'User-scope usage', nullable: true })
|
|
106
|
+
user?: CoreAiUsageScope;
|
|
107
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Field, Int, ObjectType } from '@nestjs/graphql';
|
|
2
|
+
|
|
3
|
+
import { Restricted } from '../../../common/decorators/restricted.decorator';
|
|
4
|
+
import { RoleEnum } from '../../../common/enums/role.enum';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Token usage of an AI prompt (best effort, summed across agent-loop iterations).
|
|
8
|
+
*/
|
|
9
|
+
@ObjectType({ description: 'Token usage of an AI prompt' })
|
|
10
|
+
@Restricted(RoleEnum.S_EVERYONE)
|
|
11
|
+
export class CoreAiUsage {
|
|
12
|
+
/**
|
|
13
|
+
* Number of completion (output) tokens.
|
|
14
|
+
*/
|
|
15
|
+
@Field(() => Int, { description: 'Number of completion tokens', nullable: true })
|
|
16
|
+
completionTokens?: number;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Number of prompt (input) tokens.
|
|
20
|
+
*/
|
|
21
|
+
@Field(() => Int, { description: 'Number of prompt tokens', nullable: true })
|
|
22
|
+
promptTokens?: number;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Total number of tokens.
|
|
26
|
+
*/
|
|
27
|
+
@Field(() => Int, { description: 'Total number of tokens', nullable: true })
|
|
28
|
+
totalTokens?: number;
|
|
29
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { BadGatewayException, Logger, ServiceUnavailableException } from '@nestjs/common';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
|
|
5
|
+
import { ConfigService } from '../../../common/services/config.service';
|
|
6
|
+
import { ErrorCode } from '../../error-code';
|
|
7
|
+
import {
|
|
8
|
+
ILlmProvider,
|
|
9
|
+
LlmCapabilities,
|
|
10
|
+
LlmCompletionOptions,
|
|
11
|
+
LlmMessage,
|
|
12
|
+
LlmResponse,
|
|
13
|
+
LlmToolSchema,
|
|
14
|
+
} from '../interfaces/llm-provider.interface';
|
|
15
|
+
import { ResolvedAiConnection } from '../interfaces/resolved-ai-connection.interface';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Provider that drives the local Claude Code CLI (`claude`) as an LLM backend.
|
|
19
|
+
*
|
|
20
|
+
* Claude Code is an agentic CLI, not an HTTP completion endpoint, so this provider
|
|
21
|
+
* shells out to it in non-interactive print mode and parses the single JSON result.
|
|
22
|
+
* It is **opt-in**: register it on the {@link LlmProviderFactory} from a consumer
|
|
23
|
+
* module, then create a connection with `providerType: 'claude-cli'`.
|
|
24
|
+
*
|
|
25
|
+
* ```typescript
|
|
26
|
+
* factory.registerBuilder('claude-cli', (conn) => new ClaudeCliProvider(conn));
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* ## Security model
|
|
30
|
+
*
|
|
31
|
+
* The CLI is invoked with **all of its own tools disabled** (`--tools ""`), so
|
|
32
|
+
* Claude Code cannot read files, run shell commands, or reach the network on its
|
|
33
|
+
* own. Tool calling for the orchestrator is therefore **emulated** (capabilities
|
|
34
|
+
* `nativeTools: false`) — exactly like any other emulated backend, the orchestrator
|
|
35
|
+
* injects the tool catalog into the system prompt and executes tools itself through
|
|
36
|
+
* `CrudService` with the caller's permissions. Additional hardening:
|
|
37
|
+
*
|
|
38
|
+
* - `spawn` is called with an **argument array (never a shell string)**, so prompt
|
|
39
|
+
* content can never be interpreted as a shell command (no command injection).
|
|
40
|
+
* - The conversation is written to **stdin**, not passed as an argv element.
|
|
41
|
+
* - The child runs in a neutral working directory ({@link tmpdir}) so no project
|
|
42
|
+
* `CLAUDE.md`/settings are auto-discovered into the context.
|
|
43
|
+
* - `--system-prompt` **replaces** Claude Code's default agent prompt with the
|
|
44
|
+
* orchestrator's prompt, and `--no-session-persistence` avoids writing session files.
|
|
45
|
+
* - The call is bounded by a timeout; the child is killed if it overruns.
|
|
46
|
+
*
|
|
47
|
+
* The connection's `model` is passed to `--model` (e.g. `opus`, `sonnet`, `haiku`,
|
|
48
|
+
* or a full model id). `baseUrl` is unused (auth is the CLI's own login / API key);
|
|
49
|
+
* if `apiKey` is set it is forwarded to the child as `ANTHROPIC_API_KEY`.
|
|
50
|
+
*
|
|
51
|
+
* Optional config (`ai.claudeCli`): `{ bin?: string; extraArgs?: string[]; maxBudgetUsd?: number }`.
|
|
52
|
+
*/
|
|
53
|
+
export class ClaudeCliProvider implements ILlmProvider {
|
|
54
|
+
readonly capabilities: LlmCapabilities = { jsonResponse: false, nativeTools: false, systemPrompt: true };
|
|
55
|
+
readonly name = 'claude-cli';
|
|
56
|
+
|
|
57
|
+
protected readonly defaultTimeoutMs: number;
|
|
58
|
+
private readonly logger = new Logger(ClaudeCliProvider.name);
|
|
59
|
+
|
|
60
|
+
constructor(protected readonly connection: ResolvedAiConnection) {
|
|
61
|
+
this.defaultTimeoutMs = connection.timeoutMs ?? 120_000;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Run a completion via `claude -p --output-format json`. `tools` is intentionally
|
|
66
|
+
* ignored — the CLI runs tool-free and the orchestrator emulates tool calling via
|
|
67
|
+
* the (replaced) system prompt. Maps spawn/exit/parse failures to a gateway error.
|
|
68
|
+
*/
|
|
69
|
+
async chat(messages: LlmMessage[], _tools: LlmToolSchema[], options?: LlmCompletionOptions): Promise<LlmResponse> {
|
|
70
|
+
const system = messages
|
|
71
|
+
.filter((m) => m.role === 'system')
|
|
72
|
+
.map((m) => m.content)
|
|
73
|
+
.join('\n\n');
|
|
74
|
+
// Flatten the remaining turns into a single labelled transcript for stdin. The
|
|
75
|
+
// emulated 'tool' role is rendered as tool results the model can read.
|
|
76
|
+
const transcript = messages
|
|
77
|
+
.filter((m) => m.role !== 'system')
|
|
78
|
+
.map((m) => `${this.labelFor(m.role)}:\n${m.content}`)
|
|
79
|
+
.join('\n\n');
|
|
80
|
+
|
|
81
|
+
const args = this.buildArgs(system, options);
|
|
82
|
+
const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
|
|
83
|
+
|
|
84
|
+
let stdout: string;
|
|
85
|
+
try {
|
|
86
|
+
stdout = await this.run(args, transcript, timeoutMs);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
this.logger.warn(`Claude CLI invocation for "${this.connection.name}" failed: ${(err as Error).message}`);
|
|
89
|
+
throw new ServiceUnavailableException(ErrorCode.AI_PROVIDER_ERROR);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let parsed: {
|
|
93
|
+
is_error?: boolean;
|
|
94
|
+
result?: string;
|
|
95
|
+
subtype?: string;
|
|
96
|
+
usage?: { input_tokens?: number; output_tokens?: number };
|
|
97
|
+
};
|
|
98
|
+
try {
|
|
99
|
+
parsed = JSON.parse(stdout);
|
|
100
|
+
} catch {
|
|
101
|
+
this.logger.warn(`Claude CLI returned non-JSON output for "${this.connection.name}": ${stdout.slice(0, 300)}`);
|
|
102
|
+
throw new BadGatewayException(ErrorCode.AI_PROVIDER_ERROR);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (parsed.is_error || parsed.subtype !== 'success' || typeof parsed.result !== 'string') {
|
|
106
|
+
this.logger.warn(`Claude CLI reported an error for "${this.connection.name}": ${stdout.slice(0, 300)}`);
|
|
107
|
+
throw new BadGatewayException(ErrorCode.AI_PROVIDER_ERROR);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const promptTokens = parsed.usage?.input_tokens;
|
|
111
|
+
const completionTokens = parsed.usage?.output_tokens;
|
|
112
|
+
return {
|
|
113
|
+
raw: parsed,
|
|
114
|
+
text: parsed.result,
|
|
115
|
+
usage: {
|
|
116
|
+
completionTokens,
|
|
117
|
+
promptTokens,
|
|
118
|
+
totalTokens:
|
|
119
|
+
promptTokens !== undefined || completionTokens !== undefined
|
|
120
|
+
? (promptTokens ?? 0) + (completionTokens ?? 0)
|
|
121
|
+
: undefined,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Claude models all expose a large context window. Return the model-appropriate
|
|
128
|
+
* size automatically (default 200k; 1M for the Opus 4.x 1m-context variants).
|
|
129
|
+
*/
|
|
130
|
+
async detectContextWindow(): Promise<number | undefined> {
|
|
131
|
+
const model = (this.connection.model || '').toLowerCase();
|
|
132
|
+
return model.includes('[1m]') || model.includes('-1m') ? 1_000_000 : 200_000;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Build the argv for the CLI. Override to customize flags. Tools are always
|
|
137
|
+
* disabled (`--tools ""`) — do not re-enable them unless you fully trust the model
|
|
138
|
+
* with local file/shell access.
|
|
139
|
+
*/
|
|
140
|
+
protected buildArgs(system: string, options?: LlmCompletionOptions): string[] {
|
|
141
|
+
const args = [
|
|
142
|
+
'-p',
|
|
143
|
+
'--output-format',
|
|
144
|
+
'json',
|
|
145
|
+
// Disable ALL of Claude Code's own tools — it must be a pure text generator.
|
|
146
|
+
'--tools',
|
|
147
|
+
'',
|
|
148
|
+
'--no-session-persistence',
|
|
149
|
+
'--model',
|
|
150
|
+
options?.model ?? this.connection.model,
|
|
151
|
+
];
|
|
152
|
+
if (system) {
|
|
153
|
+
// Replace (not append) Claude Code's default agent prompt with ours.
|
|
154
|
+
args.push('--system-prompt', system);
|
|
155
|
+
}
|
|
156
|
+
const maxBudgetUsd = ConfigService.get<number>('ai.claudeCli.maxBudgetUsd');
|
|
157
|
+
if (typeof maxBudgetUsd === 'number' && maxBudgetUsd > 0) {
|
|
158
|
+
args.push('--max-budget-usd', String(maxBudgetUsd));
|
|
159
|
+
}
|
|
160
|
+
const extraArgs = ConfigService.get<string[]>('ai.claudeCli.extraArgs');
|
|
161
|
+
if (Array.isArray(extraArgs)) {
|
|
162
|
+
args.push(...extraArgs);
|
|
163
|
+
}
|
|
164
|
+
return args;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Resolve the CLI binary (override or `ai.claudeCli.bin`, default `claude`). */
|
|
168
|
+
protected getBinary(): string {
|
|
169
|
+
return ConfigService.get<string>('ai.claudeCli.bin') || 'claude';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Render a transcript label for a non-system message role. */
|
|
173
|
+
protected labelFor(role: LlmMessage['role']): string {
|
|
174
|
+
if (role === 'assistant') {
|
|
175
|
+
return 'Assistant';
|
|
176
|
+
}
|
|
177
|
+
if (role === 'tool') {
|
|
178
|
+
return 'Tool results';
|
|
179
|
+
}
|
|
180
|
+
return 'User';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Spawn the CLI with an argument array (never a shell), feed the transcript via
|
|
185
|
+
* stdin, and resolve with stdout. Rejects on non-zero exit, spawn error, or timeout
|
|
186
|
+
* (the child is killed). Runs in {@link tmpdir} so no project files are discovered.
|
|
187
|
+
*/
|
|
188
|
+
protected run(args: string[], input: string, timeoutMs: number): Promise<string> {
|
|
189
|
+
return new Promise<string>((resolve, reject) => {
|
|
190
|
+
const env = { ...process.env };
|
|
191
|
+
if (this.connection.apiKey) {
|
|
192
|
+
env.ANTHROPIC_API_KEY = this.connection.apiKey;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const child = spawn(this.getBinary(), args, { cwd: tmpdir(), env, shell: false });
|
|
196
|
+
|
|
197
|
+
let stdout = '';
|
|
198
|
+
let stderr = '';
|
|
199
|
+
let settled = false;
|
|
200
|
+
|
|
201
|
+
const timer = setTimeout(() => {
|
|
202
|
+
if (!settled) {
|
|
203
|
+
settled = true;
|
|
204
|
+
child.kill('SIGKILL');
|
|
205
|
+
reject(new Error(`Claude CLI timed out after ${timeoutMs}ms`));
|
|
206
|
+
}
|
|
207
|
+
}, timeoutMs);
|
|
208
|
+
|
|
209
|
+
child.stdout.on('data', (chunk) => (stdout += chunk.toString()));
|
|
210
|
+
child.stderr.on('data', (chunk) => (stderr += chunk.toString()));
|
|
211
|
+
|
|
212
|
+
child.on('error', (err) => {
|
|
213
|
+
if (!settled) {
|
|
214
|
+
settled = true;
|
|
215
|
+
clearTimeout(timer);
|
|
216
|
+
reject(err);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
child.on('close', (code) => {
|
|
221
|
+
if (settled) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
settled = true;
|
|
225
|
+
clearTimeout(timer);
|
|
226
|
+
if (code === 0) {
|
|
227
|
+
resolve(stdout);
|
|
228
|
+
} else {
|
|
229
|
+
reject(new Error(`Claude CLI exited with code ${code}: ${stderr.slice(0, 300)}`));
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
child.stdin.on('error', () => {
|
|
234
|
+
// Ignore EPIPE if the child closed stdin early; close handler reports the failure.
|
|
235
|
+
});
|
|
236
|
+
child.stdin.write(input);
|
|
237
|
+
child.stdin.end();
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { ILlmProvider } from '../interfaces/llm-provider.interface';
|
|
4
|
+
import { ResolvedAiConnection } from '../interfaces/resolved-ai-connection.interface';
|
|
5
|
+
import { OpenAiCompatibleProvider } from './openai-compatible.provider';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Builds an {@link ILlmProvider} from a resolved connection.
|
|
9
|
+
*
|
|
10
|
+
* Builders are keyed by `providerType`. The factory ships with a builder for the
|
|
11
|
+
* `openai-compatible` chat API shape (a de-facto standard spoken by many local and
|
|
12
|
+
* hosted backends — not a specific vendor). Projects add builders for other
|
|
13
|
+
* backends/protocols by calling {@link registerBuilder} — typically from a
|
|
14
|
+
* module's `onModuleInit` — without subclassing the factory.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* factory.registerBuilder('my-provider', (conn) => new MyProvider(conn));
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
@Injectable()
|
|
22
|
+
export class LlmProviderFactory {
|
|
23
|
+
private readonly logger = new Logger(LlmProviderFactory.name);
|
|
24
|
+
private readonly builders = new Map<string, (connection: ResolvedAiConnection) => ILlmProvider>();
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
// Default builder for the OpenAI-compatible API shape (protocol, not a vendor).
|
|
28
|
+
const openAiCompatibleBuilder = (connection: ResolvedAiConnection) => new OpenAiCompatibleProvider(connection);
|
|
29
|
+
this.registerBuilder('openai-compatible', openAiCompatibleBuilder);
|
|
30
|
+
// Convenience alias for connections that store the shorter protocol key.
|
|
31
|
+
this.registerBuilder('openai', openAiCompatibleBuilder);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Register (or override) a provider builder for a `providerType`.
|
|
36
|
+
*/
|
|
37
|
+
registerBuilder(providerType: string, builder: (connection: ResolvedAiConnection) => ILlmProvider): void {
|
|
38
|
+
this.builders.set(providerType, builder);
|
|
39
|
+
this.logger.debug(`Registered LLM provider builder: ${providerType}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a provider for a resolved connection.
|
|
44
|
+
* @throws Error if no builder is registered for the connection's providerType.
|
|
45
|
+
*/
|
|
46
|
+
create(connection: ResolvedAiConnection): ILlmProvider {
|
|
47
|
+
const builder = this.builders.get(connection.providerType);
|
|
48
|
+
if (!builder) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`No LLM provider builder registered for providerType "${connection.providerType}". ` +
|
|
51
|
+
`Register one via LlmProviderFactory.registerBuilder().`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return builder(connection);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Whether a builder exists for the given provider type.
|
|
59
|
+
*/
|
|
60
|
+
supports(providerType: string): boolean {
|
|
61
|
+
return this.builders.has(providerType);
|
|
62
|
+
}
|
|
63
|
+
}
|