blockmine 1.21.0 → 1.23.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/agents/README.md +469 -0
- package/.claude/agents/auth-route-debugger.md +118 -0
- package/.claude/agents/auth-route-tester.md +93 -0
- package/.claude/agents/auto-error-resolver.md +97 -0
- package/.claude/agents/build-optimizer.md +236 -0
- package/.claude/agents/code-architecture-reviewer.md +83 -0
- package/.claude/agents/code-refactor-master.md +94 -0
- package/.claude/agents/cost-optimizer.md +134 -0
- package/.claude/agents/deployment-orchestrator.md +113 -0
- package/.claude/agents/documentation-architect.md +82 -0
- package/.claude/agents/frontend-error-fixer.md +77 -0
- package/.claude/agents/iac-code-generator.md +71 -0
- package/.claude/agents/incident-responder.md +346 -0
- package/.claude/agents/infrastructure-architect.md +31 -0
- package/.claude/agents/kubernetes-specialist.md +56 -0
- package/.claude/agents/migration-planner.md +181 -0
- package/.claude/agents/network-architect.md +196 -0
- package/.claude/agents/plan-reviewer.md +52 -0
- package/.claude/agents/refactor-planner.md +63 -0
- package/.claude/agents/security-scanner.md +102 -0
- package/.claude/agents/web-research-specialist.md +78 -0
- package/.claude/commands/cost-analysis.md +315 -0
- package/.claude/commands/dev-docs-update.md +55 -0
- package/.claude/commands/dev-docs.md +51 -0
- package/.claude/commands/incident-debug.md +247 -0
- package/.claude/commands/infra-plan.md +81 -0
- package/.claude/commands/migration-plan.md +478 -0
- package/.claude/commands/route-research-for-testing.md +37 -0
- package/.claude/commands/security-review.md +66 -0
- package/.claude/hooks/CONFIG.md +448 -0
- package/.claude/hooks/README.md +163 -0
- package/.claude/hooks/SKILL_ACTIVATION_COMPLETE.md +226 -0
- package/.claude/hooks/WINDOWS_HOOKS_README.md +151 -0
- package/.claude/hooks/add-skill-activation-banners.ts +132 -0
- package/.claude/hooks/comprehensive-skill-test.ts +1315 -0
- package/.claude/hooks/error-handling-reminder.sh +12 -0
- package/.claude/hooks/error-handling-reminder.ts +222 -0
- package/.claude/hooks/k8s-manifest-validator.sh +56 -0
- package/.claude/hooks/package-lock.json +556 -0
- package/.claude/hooks/package.json +16 -0
- package/.claude/hooks/post-tool-use-tracker.ps1 +174 -0
- package/.claude/hooks/post-tool-use-tracker.sh +183 -0
- package/.claude/hooks/security-policy-check.sh +247 -0
- package/.claude/hooks/skill-activation-prompt.ps1 +10 -0
- package/.claude/hooks/skill-activation-prompt.sh +10 -0
- package/.claude/hooks/skill-activation-prompt.ts +141 -0
- package/.claude/hooks/stop-build-check-enhanced.sh +130 -0
- package/.claude/hooks/terraform-validator.sh +53 -0
- package/.claude/hooks/test-input.json +7 -0
- package/.claude/hooks/test-skill-activation.ts +427 -0
- package/.claude/hooks/trigger-build-resolver.sh +79 -0
- package/.claude/hooks/tsc-check.sh +173 -0
- package/.claude/hooks/tsconfig.json +19 -0
- package/.claude/settings.json +59 -0
- package/.claude/settings.local.json +36 -14
- package/.claude/skills/README.md +507 -0
- package/.claude/skills/api-engineering/SKILL.md +63 -0
- package/.claude/skills/api-engineering/resources/api-versioning.md +88 -0
- package/.claude/skills/api-engineering/resources/graphql-patterns.md +106 -0
- package/.claude/skills/api-engineering/resources/rate-limiting.md +118 -0
- package/.claude/skills/api-engineering/resources/rest-api-design.md +105 -0
- package/.claude/skills/backend-dev-guidelines/SKILL.md +306 -0
- package/.claude/skills/backend-dev-guidelines/resources/architecture-overview.md +451 -0
- package/.claude/skills/backend-dev-guidelines/resources/async-and-errors.md +307 -0
- package/.claude/skills/backend-dev-guidelines/resources/complete-examples.md +638 -0
- package/.claude/skills/backend-dev-guidelines/resources/configuration.md +275 -0
- package/.claude/skills/backend-dev-guidelines/resources/database-patterns.md +224 -0
- package/.claude/skills/backend-dev-guidelines/resources/middleware-guide.md +213 -0
- package/.claude/skills/backend-dev-guidelines/resources/routing-and-controllers.md +756 -0
- package/.claude/skills/backend-dev-guidelines/resources/sentry-and-monitoring.md +336 -0
- package/.claude/skills/backend-dev-guidelines/resources/services-and-repositories.md +789 -0
- package/.claude/skills/backend-dev-guidelines/resources/testing-guide.md +235 -0
- package/.claude/skills/backend-dev-guidelines/resources/validation-patterns.md +754 -0
- package/.claude/skills/budget-and-cost-management/SKILL.md +850 -0
- package/.claude/skills/build-engineering/SKILL.md +431 -0
- package/.claude/skills/build-engineering/resources/artifact-repositories.md +72 -0
- package/.claude/skills/build-engineering/resources/build-caching.md +96 -0
- package/.claude/skills/build-engineering/resources/build-pipelines.md +105 -0
- package/.claude/skills/build-engineering/resources/build-security.md +95 -0
- package/.claude/skills/build-engineering/resources/build-systems.md +389 -0
- package/.claude/skills/build-engineering/resources/compilation-optimization.md +201 -0
- package/.claude/skills/build-engineering/resources/dependency-management.md +73 -0
- package/.claude/skills/build-engineering/resources/monorepo-builds.md +110 -0
- package/.claude/skills/build-engineering/resources/performance-optimization.md +113 -0
- package/.claude/skills/build-engineering/resources/reproducible-builds.md +82 -0
- package/.claude/skills/cloud-engineering/SKILL.md +675 -0
- package/.claude/skills/cloud-engineering/resources/aws-patterns.md +742 -0
- package/.claude/skills/cloud-engineering/resources/azure-patterns.md +714 -0
- package/.claude/skills/cloud-engineering/resources/cleared-cloud-environments.md +987 -0
- package/.claude/skills/cloud-engineering/resources/cloud-cost-optimization.md +757 -0
- package/.claude/skills/cloud-engineering/resources/cloud-networking.md +1058 -0
- package/.claude/skills/cloud-engineering/resources/cloud-security-tools.md +1530 -0
- package/.claude/skills/cloud-engineering/resources/cloud-security.md +990 -0
- package/.claude/skills/cloud-engineering/resources/gcp-patterns.md +758 -0
- package/.claude/skills/cloud-engineering/resources/migration-strategies.md +820 -0
- package/.claude/skills/cloud-engineering/resources/multi-cloud-strategies.md +670 -0
- package/.claude/skills/cloud-engineering/resources/oci-patterns.md +1198 -0
- package/.claude/skills/cloud-engineering/resources/serverless-patterns.md +795 -0
- package/.claude/skills/cloud-engineering/resources/well-architected-frameworks.md +966 -0
- package/.claude/skills/cybersecurity/SKILL.md +409 -0
- package/.claude/skills/cybersecurity/resources/security-architecture.md +266 -0
- package/.claude/skills/database-engineering/SKILL.md +61 -0
- package/.claude/skills/database-engineering/resources/backup-and-recovery.md +72 -0
- package/.claude/skills/database-engineering/resources/database-replication.md +63 -0
- package/.claude/skills/database-engineering/resources/postgresql-fundamentals.md +70 -0
- package/.claude/skills/database-engineering/resources/query-optimization.md +68 -0
- package/.claude/skills/devsecops/SKILL.md +374 -0
- package/.claude/skills/devsecops/resources/ci-cd-security.md +204 -0
- package/.claude/skills/devsecops/resources/compliance-automation.md +530 -0
- package/.claude/skills/devsecops/resources/compliance-frameworks.md +2322 -0
- package/.claude/skills/devsecops/resources/container-security.md +915 -0
- package/.claude/skills/devsecops/resources/cspm-integration.md +1440 -0
- package/.claude/skills/devsecops/resources/policy-enforcement.md +619 -0
- package/.claude/skills/devsecops/resources/secrets-management.md +755 -0
- package/.claude/skills/devsecops/resources/security-monitoring.md +146 -0
- package/.claude/skills/devsecops/resources/security-scanning.md +887 -0
- package/.claude/skills/devsecops/resources/security-testing.md +203 -0
- package/.claude/skills/devsecops/resources/supply-chain-security.md +518 -0
- package/.claude/skills/devsecops/resources/vulnerability-management.md +481 -0
- package/.claude/skills/devsecops/resources/zero-trust-architecture.md +177 -0
- package/.claude/skills/documentation-as-code/SKILL.md +323 -0
- package/.claude/skills/documentation-as-code/resources/api-documentation.md +90 -0
- package/.claude/skills/documentation-as-code/resources/changelog-management.md +79 -0
- package/.claude/skills/documentation-as-code/resources/diagram-generation.md +44 -0
- package/.claude/skills/documentation-as-code/resources/docs-as-code-workflow.md +99 -0
- package/.claude/skills/documentation-as-code/resources/documentation-automation.md +68 -0
- package/.claude/skills/documentation-as-code/resources/documentation-sites.md +79 -0
- package/.claude/skills/documentation-as-code/resources/markdown-best-practices.md +162 -0
- package/.claude/skills/documentation-as-code/resources/openapi-specification.md +77 -0
- package/.claude/skills/documentation-as-code/resources/readme-engineering.md +60 -0
- package/.claude/skills/documentation-as-code/resources/technical-writing-guide.md +202 -0
- package/.claude/skills/engineering-management/SKILL.md +356 -0
- package/.claude/skills/engineering-management/resources/career-ladders.md +609 -0
- package/.claude/skills/engineering-management/resources/hiring-and-assessment.md +555 -0
- package/.claude/skills/engineering-management/resources/one-on-one-guides.md +609 -0
- package/.claude/skills/engineering-management/resources/resource-planning.md +557 -0
- package/.claude/skills/engineering-management/resources/team-organization-patterns.md +491 -0
- package/.claude/skills/engineering-management/resources/technical-interviews.md +474 -0
- package/.claude/skills/engineering-operations-management/SKILL.md +817 -0
- package/.claude/skills/error-tracking/SKILL.md +379 -0
- package/.claude/skills/frontend-dev-guidelines/SKILL.md +403 -0
- package/.claude/skills/frontend-dev-guidelines/resources/common-patterns.md +331 -0
- package/.claude/skills/frontend-dev-guidelines/resources/complete-examples.md +872 -0
- package/.claude/skills/frontend-dev-guidelines/resources/component-patterns.md +502 -0
- package/.claude/skills/frontend-dev-guidelines/resources/data-fetching.md +767 -0
- package/.claude/skills/frontend-dev-guidelines/resources/file-organization.md +502 -0
- package/.claude/skills/frontend-dev-guidelines/resources/loading-and-error-states.md +501 -0
- package/.claude/skills/frontend-dev-guidelines/resources/performance.md +406 -0
- package/.claude/skills/frontend-dev-guidelines/resources/routing-guide.md +364 -0
- package/.claude/skills/frontend-dev-guidelines/resources/styling-guide.md +428 -0
- package/.claude/skills/frontend-dev-guidelines/resources/typescript-standards.md +418 -0
- package/.claude/skills/general-it-engineering/SKILL.md +393 -0
- package/.claude/skills/general-it-engineering/resources/asset-management.md +712 -0
- package/.claude/skills/general-it-engineering/resources/automation-orchestration.md +817 -0
- package/.claude/skills/general-it-engineering/resources/business-continuity.md +786 -0
- package/.claude/skills/general-it-engineering/resources/change-management.md +715 -0
- package/.claude/skills/general-it-engineering/resources/enterprise-monitoring.md +729 -0
- package/.claude/skills/general-it-engineering/resources/help-desk-operations.md +738 -0
- package/.claude/skills/general-it-engineering/resources/incident-service-management.md +834 -0
- package/.claude/skills/general-it-engineering/resources/it-governance.md +753 -0
- package/.claude/skills/general-it-engineering/resources/itil-framework.md +503 -0
- package/.claude/skills/general-it-engineering/resources/service-management.md +669 -0
- package/.claude/skills/infrastructure-architecture/SKILL.md +328 -0
- package/.claude/skills/infrastructure-architecture/resources/architecture-decision-records.md +505 -0
- package/.claude/skills/infrastructure-architecture/resources/architecture-patterns.md +528 -0
- package/.claude/skills/infrastructure-architecture/resources/capacity-planning.md +453 -0
- package/.claude/skills/infrastructure-architecture/resources/cleared-environment-architecture.md +773 -0
- package/.claude/skills/infrastructure-architecture/resources/cost-architecture.md +499 -0
- package/.claude/skills/infrastructure-architecture/resources/data-architecture.md +501 -0
- package/.claude/skills/infrastructure-architecture/resources/disaster-recovery.md +535 -0
- package/.claude/skills/infrastructure-architecture/resources/migration-architecture.md +512 -0
- package/.claude/skills/infrastructure-architecture/resources/multi-region-design.md +608 -0
- package/.claude/skills/infrastructure-architecture/resources/reference-architectures.md +562 -0
- package/.claude/skills/infrastructure-architecture/resources/security-architecture.md +538 -0
- package/.claude/skills/infrastructure-architecture/resources/system-design-principles.md +489 -0
- package/.claude/skills/infrastructure-architecture/resources/workload-classification.md +1000 -0
- package/.claude/skills/infrastructure-strategy/SKILL.md +924 -0
- package/.claude/skills/network-engineering/SKILL.md +385 -0
- package/.claude/skills/network-engineering/resources/dns-management.md +738 -0
- package/.claude/skills/network-engineering/resources/load-balancing.md +820 -0
- package/.claude/skills/network-engineering/resources/network-architecture.md +546 -0
- package/.claude/skills/network-engineering/resources/network-security.md +921 -0
- package/.claude/skills/network-engineering/resources/network-troubleshooting.md +749 -0
- package/.claude/skills/network-engineering/resources/routing-switching.md +373 -0
- package/.claude/skills/network-engineering/resources/sdn-networking.md +695 -0
- package/.claude/skills/network-engineering/resources/service-mesh-networking.md +777 -0
- package/.claude/skills/network-engineering/resources/tcp-ip-protocols.md +444 -0
- package/.claude/skills/network-engineering/resources/vpn-connectivity.md +672 -0
- package/.claude/skills/observability-engineering/SKILL.md +101 -0
- package/.claude/skills/observability-engineering/resources/apm-tools.md +97 -0
- package/.claude/skills/observability-engineering/resources/correlation-strategies.md +87 -0
- package/.claude/skills/observability-engineering/resources/distributed-tracing.md +98 -0
- package/.claude/skills/observability-engineering/resources/logs-aggregation.md +118 -0
- package/.claude/skills/observability-engineering/resources/observability-cost-optimization.md +141 -0
- package/.claude/skills/observability-engineering/resources/opentelemetry.md +110 -0
- package/.claude/skills/platform-engineering/SKILL.md +555 -0
- package/.claude/skills/platform-engineering/resources/architecture-overview.md +600 -0
- package/.claude/skills/platform-engineering/resources/container-orchestration.md +916 -0
- package/.claude/skills/platform-engineering/resources/cost-optimization.md +634 -0
- package/.claude/skills/platform-engineering/resources/developer-platforms.md +670 -0
- package/.claude/skills/platform-engineering/resources/gitops-automation.md +650 -0
- package/.claude/skills/platform-engineering/resources/infrastructure-as-code.md +778 -0
- package/.claude/skills/platform-engineering/resources/infrastructure-standards.md +708 -0
- package/.claude/skills/platform-engineering/resources/multi-tenancy.md +602 -0
- package/.claude/skills/platform-engineering/resources/platform-security.md +711 -0
- package/.claude/skills/platform-engineering/resources/resource-management.md +592 -0
- package/.claude/skills/platform-engineering/resources/service-mesh.md +628 -0
- package/.claude/skills/release-engineering/SKILL.md +393 -0
- package/.claude/skills/release-engineering/resources/artifact-management.md +108 -0
- package/.claude/skills/release-engineering/resources/build-optimization.md +84 -0
- package/.claude/skills/release-engineering/resources/ci-cd-pipelines.md +411 -0
- package/.claude/skills/release-engineering/resources/deployment-strategies.md +197 -0
- package/.claude/skills/release-engineering/resources/pipeline-security.md +62 -0
- package/.claude/skills/release-engineering/resources/progressive-delivery.md +83 -0
- package/.claude/skills/release-engineering/resources/release-automation.md +68 -0
- package/.claude/skills/release-engineering/resources/release-orchestration.md +77 -0
- package/.claude/skills/release-engineering/resources/rollback-strategies.md +66 -0
- package/.claude/skills/release-engineering/resources/versioning-strategies.md +59 -0
- package/.claude/skills/route-tester/SKILL.md +392 -0
- package/.claude/skills/skill-developer/ADVANCED.md +197 -0
- package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +306 -0
- package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +152 -0
- package/.claude/skills/skill-developer/SKILL.md +430 -0
- package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +315 -0
- package/.claude/skills/skill-developer/TRIGGER_TYPES.md +305 -0
- package/.claude/skills/skill-developer/TROUBLESHOOTING.md +514 -0
- package/.claude/skills/skill-rules.json +2940 -0
- package/.claude/skills/sre/SKILL.md +464 -0
- package/.claude/skills/sre/resources/alerting-best-practices.md +282 -0
- package/.claude/skills/sre/resources/capacity-planning.md +226 -0
- package/.claude/skills/sre/resources/chaos-engineering.md +193 -0
- package/.claude/skills/sre/resources/disaster-recovery.md +232 -0
- package/.claude/skills/sre/resources/incident-management.md +436 -0
- package/.claude/skills/sre/resources/observability-stack.md +240 -0
- package/.claude/skills/sre/resources/on-call-runbooks.md +167 -0
- package/.claude/skills/sre/resources/performance-optimization.md +108 -0
- package/.claude/skills/sre/resources/reliability-patterns.md +183 -0
- package/.claude/skills/sre/resources/slo-sli-sla.md +464 -0
- package/.claude/skills/sre/resources/toil-reduction.md +145 -0
- package/.claude/skills/systems-engineering/SKILL.md +648 -0
- package/.claude/skills/systems-engineering/resources/automation-patterns.md +771 -0
- package/.claude/skills/systems-engineering/resources/configuration-management.md +998 -0
- package/.claude/skills/systems-engineering/resources/linux-administration.md +672 -0
- package/.claude/skills/systems-engineering/resources/networking-fundamentals.md +982 -0
- package/.claude/skills/systems-engineering/resources/performance-tuning.md +871 -0
- package/.claude/skills/systems-engineering/resources/powershell-scripting.md +482 -0
- package/.claude/skills/systems-engineering/resources/security-hardening.md +739 -0
- package/.claude/skills/systems-engineering/resources/shell-scripting.md +915 -0
- package/.claude/skills/systems-engineering/resources/storage-management.md +628 -0
- package/.claude/skills/systems-engineering/resources/system-monitoring.md +787 -0
- package/.claude/skills/systems-engineering/resources/troubleshooting-guide.md +753 -0
- package/.claude/skills/systems-engineering/resources/windows-administration.md +738 -0
- package/.claude/skills/technical-leadership/SKILL.md +728 -0
- package/CHANGELOG.md +102 -42
- package/CLAUDE.md +284 -0
- package/README.md +315 -71
- package/backend/docs/SECRETS_DOCUMENTATION.md +327 -0
- package/backend/jest.config.js +59 -0
- package/backend/package-lock.json +6801 -0
- package/backend/package.json +24 -4
- package/backend/prisma/migrations/20251026104609_add_websocket_api/migration.sql +33 -0
- package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
- package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
- package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
- package/backend/prisma/migrations/migration_lock.toml +2 -2
- package/backend/prisma/schema.prisma +103 -1
- package/backend/src/__tests__/core/DependencyService.test.js +336 -0
- package/backend/src/__tests__/core/UserService.test.js +875 -0
- package/backend/src/__tests__/repositories/BaseRepository.test.js +146 -0
- package/backend/src/__tests__/repositories/BotRepository.test.js +118 -0
- package/backend/src/__tests__/repositories/CommandRepository.test.js +132 -0
- package/backend/src/__tests__/repositories/EventGraphRepository.test.js +93 -0
- package/backend/src/__tests__/repositories/GroupRepository.test.js +155 -0
- package/backend/src/__tests__/repositories/PermissionRepository.test.js +130 -0
- package/backend/src/__tests__/repositories/PluginRepository.test.js +107 -0
- package/backend/src/__tests__/repositories/ServerRepository.test.js +80 -0
- package/backend/src/__tests__/repositories/UserRepository.test.js +128 -0
- package/backend/src/__tests__/secretsFilter.test.js +425 -0
- package/backend/src/__tests__/services/BotLifecycleService.test.js +416 -0
- package/backend/src/__tests__/services/BotProcessManager.test.js +285 -0
- package/backend/src/__tests__/services/CacheManager.test.js +125 -0
- package/backend/src/__tests__/services/CommandExecutionService.test.js +460 -0
- package/backend/src/__tests__/services/ResourceMonitorService.test.js +207 -0
- package/backend/src/__tests__/services/TelemetryService.test.js +291 -0
- package/backend/src/__tests__/setup.js +25 -0
- package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
- package/backend/src/api/middleware/auth.js +27 -0
- package/backend/src/api/middleware/botAccess.js +7 -3
- package/backend/src/api/middleware/panelApiAuth.js +135 -0
- package/backend/src/api/routes/aiAssistant.js +995 -0
- package/backend/src/api/routes/apiKeys.js +181 -0
- package/backend/src/api/routes/auth.js +669 -633
- package/backend/src/api/routes/botCommands.js +107 -0
- package/backend/src/api/routes/botGroups.js +165 -0
- package/backend/src/api/routes/botHistory.js +108 -0
- package/backend/src/api/routes/botPermissions.js +99 -0
- package/backend/src/api/routes/botStatus.js +36 -0
- package/backend/src/api/routes/botUsers.js +162 -0
- package/backend/src/api/routes/bots.js +2451 -2360
- package/backend/src/api/routes/eventGraphs.js +4 -1
- package/backend/src/api/routes/logs.js +13 -3
- package/backend/src/api/routes/panel.js +66 -66
- package/backend/src/api/routes/panelApiKeys.js +179 -0
- package/backend/src/api/routes/pluginIde.js +1715 -135
- package/backend/src/api/routes/plugins.js +376 -218
- package/backend/src/api/routes/proxies.js +130 -0
- package/backend/src/api/routes/search.js +4 -0
- package/backend/src/api/routes/servers.js +20 -3
- package/backend/src/api/routes/settings.js +5 -0
- package/backend/src/api/routes/system.js +174 -0
- package/backend/src/api/routes/traces.js +131 -0
- package/backend/src/config/debug.config.js +36 -0
- package/backend/src/container.js +82 -0
- package/backend/src/core/BotHistoryStore.js +180 -0
- package/backend/src/core/BotManager.js +149 -868
- package/backend/src/core/BotManager.old.js +1093 -0
- package/backend/src/core/BotProcess.js +850 -191
- package/backend/src/core/EventGraphManager.js +194 -198
- package/backend/src/core/GraphExecutionEngine.js +709 -57
- package/backend/src/core/MessageQueue.js +39 -12
- package/backend/src/core/NodeRegistry.js +37 -1134
- package/backend/src/core/PluginLoader.js +99 -5
- package/backend/src/core/PluginManager.js +126 -15
- package/backend/src/core/PrismaService.js +32 -0
- package/backend/src/core/TaskScheduler.js +1 -1
- package/backend/src/core/UserService.js +3 -3
- package/backend/src/core/__tests__/PrismaService.test.js +24 -0
- package/backend/src/core/commands/README.md +305 -0
- package/backend/src/core/commands/dev.js +13 -7
- package/backend/src/core/commands/ping.js +10 -4
- package/backend/src/core/commands/whois.js +63 -0
- package/backend/src/core/config/validation.js +27 -0
- package/backend/src/core/constants/graphTypes.js +21 -0
- package/backend/src/core/node-registries/actions.js +202 -0
- package/backend/src/core/node-registries/arrays.js +155 -0
- package/backend/src/core/node-registries/bot.js +23 -0
- package/backend/src/core/node-registries/data.js +290 -0
- package/backend/src/core/node-registries/debug.js +26 -0
- package/backend/src/core/node-registries/events.js +201 -0
- package/backend/src/core/node-registries/flow.js +139 -0
- package/backend/src/core/node-registries/logic.js +62 -0
- package/backend/src/core/node-registries/math.js +42 -0
- package/backend/src/core/node-registries/objects.js +98 -0
- package/backend/src/core/node-registries/strings.js +187 -0
- package/backend/src/core/node-registries/time.js +113 -0
- package/backend/src/core/node-registries/type.js +25 -0
- package/backend/src/core/node-registries/users.js +79 -0
- package/backend/src/core/nodes/{action_bot_look_at.js → actions/bot_look_at.js} +36 -36
- package/backend/src/core/nodes/{action_bot_set_variable.js → actions/bot_set_variable.js} +32 -32
- package/backend/src/core/nodes/actions/create_command.js +189 -0
- package/backend/src/core/nodes/actions/delete_command.js +92 -0
- package/backend/src/core/nodes/{action_send_log.js → actions/send_log.js} +28 -23
- package/backend/src/core/nodes/{action_send_message.js → actions/send_message.js} +32 -32
- package/backend/src/core/nodes/actions/send_websocket_response.js +33 -0
- package/backend/src/core/nodes/actions/update_command.js +133 -0
- package/backend/src/core/nodes/arrays/get_next.js +35 -0
- package/backend/src/core/nodes/arrays/join.js +28 -0
- package/backend/src/core/nodes/{data_cast.js → data/cast.js} +10 -1
- package/backend/src/core/nodes/data/datetime_literal.js +27 -0
- package/backend/src/core/nodes/data/entity_info.js +69 -0
- package/backend/src/core/nodes/data/get_nearby_entities.js +32 -0
- package/backend/src/core/nodes/data/get_nearby_players.js +64 -0
- package/backend/src/core/nodes/{data_get_user_field.js → data/get_user_field.js} +1 -1
- package/backend/src/core/nodes/data/type_check.js +53 -0
- package/backend/src/core/nodes/{debug_log.js → debug/log.js} +16 -16
- package/backend/src/core/nodes/{flow_branch.js → flow/branch.js} +15 -15
- package/backend/src/core/nodes/{flow_break.js → flow/break.js} +14 -14
- package/backend/src/core/nodes/flow/delay.js +43 -0
- package/backend/src/core/nodes/{flow_for_each.js → flow/for_each.js} +39 -39
- package/backend/src/core/nodes/{flow_sequence.js → flow/sequence.js} +16 -16
- package/backend/src/core/nodes/{flow_switch.js → flow/switch.js} +47 -47
- package/backend/src/core/nodes/{flow_while.js → flow/while.js} +1 -1
- package/backend/src/core/nodes/logic/__tests__/compare.test.js +83 -0
- package/backend/src/core/nodes/logic/not.js +22 -0
- package/backend/src/core/nodes/math/__tests__/operation.test.js +65 -0
- package/backend/src/core/nodes/strings/__tests__/concat.test.js +89 -0
- package/backend/src/core/nodes/{string_starts_with.js → strings/starts_with.js} +1 -1
- package/backend/src/core/nodes/strings/to_lower.js +22 -0
- package/backend/src/core/nodes/strings/to_upper.js +22 -0
- package/backend/src/core/nodes/time/__tests__/now.test.js +24 -0
- package/backend/src/core/nodes/time/add.js +33 -0
- package/backend/src/core/nodes/time/compare.js +35 -0
- package/backend/src/core/nodes/time/diff.js +29 -0
- package/backend/src/core/nodes/time/format.js +32 -0
- package/backend/src/core/nodes/time/now.js +18 -0
- package/backend/src/core/nodes/type/to_string.js +32 -0
- package/backend/src/core/nodes/{user_check_blacklist.js → users/check_blacklist.js} +37 -37
- package/backend/src/core/nodes/{user_get_groups.js → users/get_groups.js} +36 -36
- package/backend/src/core/nodes/{user_get_permissions.js → users/get_permissions.js} +36 -36
- package/backend/src/core/nodes/{user_set_blacklist.js → users/set_blacklist.js} +37 -37
- package/backend/src/core/services/BotLifecycleService.js +835 -0
- package/backend/src/core/services/BotProcessManager.js +163 -0
- package/backend/src/core/services/CacheManager.js +111 -0
- package/backend/src/core/services/CommandExecutionService.js +430 -0
- package/backend/src/core/services/DebugSessionManager.js +347 -0
- package/backend/src/core/services/GraphCollaborationManager.js +501 -0
- package/backend/src/core/services/MinecraftBotManager.js +259 -0
- package/backend/src/core/services/MinecraftViewerService.js +216 -0
- package/backend/src/core/services/ResourceMonitorService.js +90 -0
- package/backend/src/core/services/TelemetryService.js +124 -0
- package/backend/src/core/services/TraceCollectorService.js +545 -0
- package/backend/src/core/services/ValidationService.js +132 -0
- package/backend/src/core/services/__tests__/ValidationService.test.js +148 -0
- package/backend/src/core/services.js +20 -5
- package/backend/src/core/system/CommandContext.js +84 -0
- package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
- package/backend/src/core/system/Transport.js +74 -0
- package/backend/src/core/utils/__tests__/jsonParser.test.js +44 -0
- package/backend/src/core/utils/jsonParser.js +18 -0
- package/backend/src/core/utils/secretsFilter.js +262 -0
- package/backend/src/core/utils/variableParser.js +89 -0
- package/backend/src/core/validation/__tests__/nodeSchemas.test.js +175 -0
- package/backend/src/core/validation/nodeSchemas.js +112 -0
- package/backend/src/lib/prisma.js +2 -4
- package/backend/src/real-time/botApi/handlers/commandHandlers.js +28 -0
- package/backend/src/real-time/botApi/handlers/graphHandlers.js +99 -0
- package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +147 -0
- package/backend/src/real-time/botApi/handlers/index.js +43 -0
- package/backend/src/real-time/botApi/handlers/messageHandlers.js +66 -0
- package/backend/src/real-time/botApi/handlers/statusHandlers.js +17 -0
- package/backend/src/real-time/botApi/handlers/userHandlers.js +141 -0
- package/backend/src/real-time/botApi/index.js +40 -0
- package/backend/src/real-time/botApi/middleware.js +79 -0
- package/backend/src/real-time/botApi/utils.js +65 -0
- package/backend/src/real-time/panelNamespace.js +387 -0
- package/backend/src/real-time/presence.js +7 -2
- package/backend/src/real-time/socketHandler.js +400 -5
- package/backend/src/repositories/BaseRepository.js +43 -0
- package/backend/src/repositories/BotRepository.js +42 -0
- package/backend/src/repositories/CommandRepository.js +53 -0
- package/backend/src/repositories/EventGraphRepository.js +40 -0
- package/backend/src/repositories/GroupRepository.js +69 -0
- package/backend/src/repositories/PermissionRepository.js +48 -0
- package/backend/src/repositories/PluginRepository.js +42 -0
- package/backend/src/repositories/ServerRepository.js +27 -0
- package/backend/src/repositories/UserRepository.js +48 -0
- package/backend/src/server.js +21 -0
- package/backend/src/test-refactor.js +85 -0
- package/frontend/dist/assets/index-B1serztM.js +11210 -0
- package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package-lock.json +9437 -0
- package/frontend/package.json +8 -5
- package/package.json +3 -2
- package/screen/console.png +0 -0
- package/screen/dashboard.png +0 -0
- package/screen/graph_collabe.png +0 -0
- package/screen/graph_live_debug.png +0 -0
- package/screen/management_command.png +0 -0
- package/screen/node_debug_trace.png +0 -0
- package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
- package/screen/websocket.png +0 -0
- package/screen//320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270_/320/276/321/202/320/264/320/265/320/273/321/214/320/275/321/213/321/205_/320/272/320/276/320/274/320/260/320/275/320/264_/320/272/320/260/320/266/320/264/321/203_/320/272/320/276/320/274/320/260/320/275/320/273/320/264/321/203_/320/274/320/276/320/266/320/275/320/276_/320/275/320/260/321/201/321/202/321/200/320/260/320/270/320/262/320/260/321/202/321/214.png +0 -0
- package/screen//320/277/320/273/320/260/320/275/320/270/321/200/320/276/320/262/321/211/320/270/320/272_/320/274/320/276/320/266/320/275/320/276_/320/267/320/260/320/264/320/260/320/262/320/260/321/202/321/214_/320/264/320/265/320/271/321/201/321/202/320/262/320/270/321/217_/320/277/320/276_/320/262/321/200/320/265/320/274/320/265/320/275/320/270.png +0 -0
- package/frontend/dist/assets/index-B9GedHEa.js +0 -8352
- package/frontend/dist/assets/index-zLiy9MDx.css +0 -1
- package/nul +0 -0
- /package/backend/src/core/nodes/{action_http_request.js → actions/http_request.js} +0 -0
- /package/backend/src/core/nodes/{array_add_element.js → arrays/add_element.js} +0 -0
- /package/backend/src/core/nodes/{array_contains.js → arrays/contains.js} +0 -0
- /package/backend/src/core/nodes/{array_find_index.js → arrays/find_index.js} +0 -0
- /package/backend/src/core/nodes/{array_get_by_index.js → arrays/get_by_index.js} +0 -0
- /package/backend/src/core/nodes/{array_get_random_element.js → arrays/get_random_element.js} +0 -0
- /package/backend/src/core/nodes/{array_remove_by_index.js → arrays/remove_by_index.js} +0 -0
- /package/backend/src/core/nodes/{bot_get_position.js → bot/get_position.js} +0 -0
- /package/backend/src/core/nodes/{data_array_literal.js → data/array_literal.js} +0 -0
- /package/backend/src/core/nodes/{data_boolean_literal.js → data/boolean_literal.js} +0 -0
- /package/backend/src/core/nodes/{data_get_argument.js → data/get_argument.js} +0 -0
- /package/backend/src/core/nodes/{data_get_bot_look.js → data/get_bot_look.js} +0 -0
- /package/backend/src/core/nodes/{data_get_entity_field.js → data/get_entity_field.js} +0 -0
- /package/backend/src/core/nodes/{data_get_server_players.js → data/get_server_players.js} +0 -0
- /package/backend/src/core/nodes/{data_get_variable.js → data/get_variable.js} +0 -0
- /package/backend/src/core/nodes/{data_length.js → data/length.js} +0 -0
- /package/backend/src/core/nodes/{data_make_object.js → data/make_object.js} +0 -0
- /package/backend/src/core/nodes/{data_number_literal.js → data/number_literal.js} +0 -0
- /package/backend/src/core/nodes/{data_string_literal.js → data/string_literal.js} +0 -0
- /package/backend/src/core/nodes/{logic_compare.js → logic/compare.js} +0 -0
- /package/backend/src/core/nodes/{logic_operation.js → logic/operation.js} +0 -0
- /package/backend/src/core/nodes/{math_operation.js → math/operation.js} +0 -0
- /package/backend/src/core/nodes/{math_random_number.js → math/random_number.js} +0 -0
- /package/backend/src/core/nodes/{object_create.js → objects/create.js} +0 -0
- /package/backend/src/core/nodes/{object_delete.js → objects/delete.js} +0 -0
- /package/backend/src/core/nodes/{object_get.js → objects/get.js} +0 -0
- /package/backend/src/core/nodes/{object_has_key.js → objects/has_key.js} +0 -0
- /package/backend/src/core/nodes/{object_set.js → objects/set.js} +0 -0
- /package/backend/src/core/nodes/{string_concat.js → strings/concat.js} +0 -0
- /package/backend/src/core/nodes/{string_contains.js → strings/contains.js} +0 -0
- /package/backend/src/core/nodes/{string_ends_with.js → strings/ends_with.js} +0 -0
- /package/backend/src/core/nodes/{string_equals.js → strings/equals.js} +0 -0
- /package/backend/src/core/nodes/{string_length.js → strings/length.js} +0 -0
- /package/backend/src/core/nodes/{string_matches.js → strings/matches.js} +0 -0
- /package/backend/src/core/nodes/{string_split.js → strings/split.js} +0 -0
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router({ mergeParams: true });
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
const { OpenRouterClient, MemoryHistoryStorage } = require('openrouter-kit');
|
|
6
|
+
const { GeminiClient } = require('google-ai-kit');
|
|
7
|
+
const { PrismaClient } = require('@prisma/client');
|
|
8
|
+
const Diff = require('diff');
|
|
9
|
+
|
|
10
|
+
const prisma = new PrismaClient();
|
|
11
|
+
const { botManager } = require('../../core/services');
|
|
12
|
+
|
|
13
|
+
// Хранилище истории чатов в памяти: Map<"botId_pluginName", messages[]>
|
|
14
|
+
const chatHistoryStore = new Map();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Конвертирует JSON Schema параметры в формат Gemini
|
|
18
|
+
*/
|
|
19
|
+
function convertToGeminiParameters(jsonSchemaParams) {
|
|
20
|
+
if (!jsonSchemaParams || !jsonSchemaParams.properties) {
|
|
21
|
+
return {
|
|
22
|
+
type: 'OBJECT',
|
|
23
|
+
properties: {},
|
|
24
|
+
required: []
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const convertType = (type) => {
|
|
29
|
+
const typeMap = {
|
|
30
|
+
'string': 'STRING',
|
|
31
|
+
'number': 'NUMBER',
|
|
32
|
+
'integer': 'INTEGER',
|
|
33
|
+
'boolean': 'BOOLEAN',
|
|
34
|
+
'object': 'OBJECT',
|
|
35
|
+
'array': 'ARRAY'
|
|
36
|
+
};
|
|
37
|
+
return typeMap[type] || 'STRING';
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const convertedProperties = {};
|
|
41
|
+
for (const [key, value] of Object.entries(jsonSchemaParams.properties)) {
|
|
42
|
+
convertedProperties[key] = {
|
|
43
|
+
type: convertType(value.type),
|
|
44
|
+
description: value.description || ''
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
if (value.enum) {
|
|
48
|
+
convertedProperties[key].enum = value.enum;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
type: 'OBJECT',
|
|
54
|
+
properties: convertedProperties,
|
|
55
|
+
required: jsonSchemaParams.required || []
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Парсит строку прокси формата "login:password@ip:port"
|
|
61
|
+
* Возвращает объект конфигурации прокси или null если строка пустая
|
|
62
|
+
*/
|
|
63
|
+
function parseProxyString(proxyString) {
|
|
64
|
+
if (!proxyString || proxyString.trim() === '') {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Формат: login:password@ip:port
|
|
70
|
+
const atIndex = proxyString.lastIndexOf('@');
|
|
71
|
+
if (atIndex === -1) {
|
|
72
|
+
console.warn('Invalid proxy format: missing @ separator');
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const auth = proxyString.substring(0, atIndex);
|
|
77
|
+
const hostPort = proxyString.substring(atIndex + 1);
|
|
78
|
+
|
|
79
|
+
const colonAuthIndex = auth.indexOf(':');
|
|
80
|
+
if (colonAuthIndex === -1) {
|
|
81
|
+
console.warn('Invalid proxy format: missing : in auth');
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const user = auth.substring(0, colonAuthIndex);
|
|
86
|
+
const pass = auth.substring(colonAuthIndex + 1);
|
|
87
|
+
|
|
88
|
+
const colonHostIndex = hostPort.lastIndexOf(':');
|
|
89
|
+
if (colonHostIndex === -1) {
|
|
90
|
+
console.warn('Invalid proxy format: missing : in host:port');
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const host = hostPort.substring(0, colonHostIndex);
|
|
95
|
+
const port = hostPort.substring(colonHostIndex + 1);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
host: host,
|
|
99
|
+
port: parseInt(port),
|
|
100
|
+
user: user,
|
|
101
|
+
pass: pass
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('Error parsing proxy string:', error);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Список игнорируемых файлов/папок для сканирования проекта
|
|
110
|
+
const IGNORE_LIST = [
|
|
111
|
+
'node_modules/',
|
|
112
|
+
'package-lock.json',
|
|
113
|
+
'.git/',
|
|
114
|
+
'.vscode/',
|
|
115
|
+
'.idea/',
|
|
116
|
+
'*.log',
|
|
117
|
+
'*.db',
|
|
118
|
+
'dist/',
|
|
119
|
+
'.env',
|
|
120
|
+
'coverage/',
|
|
121
|
+
'.claude/'
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
// Вспомогательная функция для проверки игнорирования
|
|
125
|
+
function shouldIgnore(filePath, ignoreList) {
|
|
126
|
+
// Нормализуем путь к forward slashes для кроссплатформенности
|
|
127
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
128
|
+
|
|
129
|
+
return ignoreList.some(pattern => {
|
|
130
|
+
if (pattern.endsWith('/')) {
|
|
131
|
+
const dirPattern = pattern.slice(0, -1);
|
|
132
|
+
const pathParts = normalizedPath.split('/');
|
|
133
|
+
return pathParts.includes(dirPattern) || normalizedPath.startsWith(pattern) || normalizedPath === dirPattern;
|
|
134
|
+
}
|
|
135
|
+
if (pattern.startsWith('*.')) {
|
|
136
|
+
const extension = pattern.substring(1);
|
|
137
|
+
return normalizedPath.endsWith(extension);
|
|
138
|
+
}
|
|
139
|
+
const fileName = path.basename(normalizedPath);
|
|
140
|
+
return normalizedPath === pattern ||
|
|
141
|
+
normalizedPath.startsWith(pattern + '/') ||
|
|
142
|
+
fileName === pattern;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Получить все файлы рекурсивно
|
|
147
|
+
function getAllFilesRecursive(dir, basePath = dir, fileList = [], ignoreList = []) {
|
|
148
|
+
const files = fse.readdirSync(dir);
|
|
149
|
+
|
|
150
|
+
files.forEach(file => {
|
|
151
|
+
const fullPath = path.join(dir, file);
|
|
152
|
+
const relativePath = path.relative(basePath, fullPath);
|
|
153
|
+
|
|
154
|
+
if (shouldIgnore(relativePath, ignoreList)) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (fse.statSync(fullPath).isDirectory()) {
|
|
159
|
+
getAllFilesRecursive(fullPath, basePath, fileList, ignoreList);
|
|
160
|
+
} else {
|
|
161
|
+
fileList.push(relativePath);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return fileList;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Получить древовидную структуру
|
|
169
|
+
function getTreeStructure(dir, basePath = dir, prefix = '', ignoreList = []) {
|
|
170
|
+
const files = fse.readdirSync(dir);
|
|
171
|
+
let result = '';
|
|
172
|
+
|
|
173
|
+
files.forEach((file, index) => {
|
|
174
|
+
const fullPath = path.join(dir, file);
|
|
175
|
+
const relativePath = path.relative(basePath, fullPath);
|
|
176
|
+
|
|
177
|
+
if (shouldIgnore(relativePath, ignoreList)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const isLast = index === files.length - 1;
|
|
182
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
183
|
+
|
|
184
|
+
if (fse.statSync(fullPath).isDirectory()) {
|
|
185
|
+
result += `${prefix}${connector}${file}/\n`;
|
|
186
|
+
result += getTreeStructure(fullPath, basePath, prefix + (isLast ? ' ' : '│ '), ignoreList);
|
|
187
|
+
} else {
|
|
188
|
+
result += `${prefix}${connector}${file}\n`;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Debug middleware
|
|
196
|
+
router.use((req, res, next) => {
|
|
197
|
+
console.log('[AI Assistant Router] Request:', req.method, req.path, 'Params:', req.params);
|
|
198
|
+
next();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Middleware для проверки плагина
|
|
203
|
+
*/
|
|
204
|
+
async function resolvePluginPath(req, res, next) {
|
|
205
|
+
try {
|
|
206
|
+
console.log('resolvePluginPath - botId:', req.params.botId, 'pluginName:', req.params.pluginName);
|
|
207
|
+
const { botId, pluginName } = req.params;
|
|
208
|
+
|
|
209
|
+
if (!pluginName) {
|
|
210
|
+
return res.status(400).json({ error: 'Имя плагина обязательно в пути.' });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const plugin = await prisma.installedPlugin.findFirst({
|
|
214
|
+
where: {
|
|
215
|
+
botId: parseInt(botId),
|
|
216
|
+
name: pluginName
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (!plugin) {
|
|
221
|
+
console.log('Plugin not found in database');
|
|
222
|
+
return res.status(404).json({ error: 'Плагин не найден в базе данных.' });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const pluginPath = plugin.path;
|
|
226
|
+
console.log('Checking plugin path:', pluginPath);
|
|
227
|
+
|
|
228
|
+
if (!await fse.pathExists(pluginPath)) {
|
|
229
|
+
console.log('Plugin path NOT found in filesystem!');
|
|
230
|
+
return res.status(404).json({ error: 'Директория плагина не найдена в файловой системе.' });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log('Plugin path found, proceeding to route handler');
|
|
234
|
+
req.pluginPath = pluginPath;
|
|
235
|
+
next();
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error('Error in resolvePluginPath:', error);
|
|
238
|
+
res.status(500).json({ error: 'Не удалось определить путь к плагину.' });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Создаем tools для AI ассистента
|
|
243
|
+
function createPluginTools(pluginPath, res, botId) {
|
|
244
|
+
return [
|
|
245
|
+
// Tool 1: Получить древовидную структуру проекта
|
|
246
|
+
{
|
|
247
|
+
type: 'function',
|
|
248
|
+
function: {
|
|
249
|
+
name: 'getProjectTree',
|
|
250
|
+
description: 'Получает древовидную структуру файлов и папок плагина. Используй это первым делом чтобы увидеть какие файлы есть в проекте.',
|
|
251
|
+
parameters: {
|
|
252
|
+
type: 'object',
|
|
253
|
+
properties: {},
|
|
254
|
+
required: []
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
execute: async () => {
|
|
258
|
+
console.log('getProjectTree called for path:', pluginPath);
|
|
259
|
+
try {
|
|
260
|
+
const tree = getTreeStructure(pluginPath, pluginPath, '', IGNORE_LIST);
|
|
261
|
+
const allFiles = getAllFilesRecursive(pluginPath, pluginPath, [], IGNORE_LIST);
|
|
262
|
+
console.log('Found files:', allFiles);
|
|
263
|
+
|
|
264
|
+
let result = `Древовидная структура плагина:\n${tree}\n`;
|
|
265
|
+
result += `\nВсего файлов: ${allFiles.length}`;
|
|
266
|
+
|
|
267
|
+
return result;
|
|
268
|
+
} catch (error) {
|
|
269
|
+
return `Ошибка при чтении структуры проекта: ${error.message}`;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// Tool 2: Получить полный контекст проекта
|
|
275
|
+
{
|
|
276
|
+
type: 'function',
|
|
277
|
+
function: {
|
|
278
|
+
name: 'getFullProjectContext',
|
|
279
|
+
description: 'Получает полную структуру файлов плагина и содержимое ВСЕХ файлов',
|
|
280
|
+
parameters: {
|
|
281
|
+
type: 'object',
|
|
282
|
+
properties: {},
|
|
283
|
+
required: []
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
execute: async () => {
|
|
287
|
+
console.log('getFullProjectContext called for path:', pluginPath);
|
|
288
|
+
try {
|
|
289
|
+
const tree = getTreeStructure(pluginPath, pluginPath, '', IGNORE_LIST);
|
|
290
|
+
const allFiles = getAllFilesRecursive(pluginPath, pluginPath, [], IGNORE_LIST);
|
|
291
|
+
console.log('Found files:', allFiles);
|
|
292
|
+
|
|
293
|
+
let result = `Древовидная структура плагина:\n${tree}\n\n`;
|
|
294
|
+
result += `Всего файлов: ${allFiles.length}\n\n`;
|
|
295
|
+
result += `Содержимое файлов:\n\n`;
|
|
296
|
+
|
|
297
|
+
for (const file of allFiles) {
|
|
298
|
+
const fullPath = path.join(pluginPath, file);
|
|
299
|
+
const fileContent = await fse.readFile(fullPath, 'utf8');
|
|
300
|
+
result += `=== Файл: ${file} ===\n${fileContent}\n\n`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return result;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return `Ошибка при чтении структуры проекта: ${error.message}`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
// Tool 2: Прочитать конкретный файл
|
|
311
|
+
{
|
|
312
|
+
type: 'function',
|
|
313
|
+
function: {
|
|
314
|
+
name: 'readFile',
|
|
315
|
+
description: 'Читает содержимое конкретного файла из плагина',
|
|
316
|
+
parameters: {
|
|
317
|
+
type: 'object',
|
|
318
|
+
properties: {
|
|
319
|
+
filePath: {
|
|
320
|
+
type: 'string',
|
|
321
|
+
description: 'Относительный путь к файлу внутри плагина (например: "index.js" или "commands/hello.js")'
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
required: ['filePath']
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
execute: async (args) => {
|
|
328
|
+
console.log('readFile called with:', args.filePath);
|
|
329
|
+
try {
|
|
330
|
+
const safePath = path.resolve(pluginPath, args.filePath);
|
|
331
|
+
|
|
332
|
+
// Проверка безопасности - файл должен быть внутри pluginPath
|
|
333
|
+
const normalizedPluginPath = pluginPath.endsWith(path.sep) ? pluginPath : pluginPath + path.sep;
|
|
334
|
+
if (!safePath.startsWith(normalizedPluginPath) && safePath !== pluginPath) {
|
|
335
|
+
return `Ошибка: Доступ запрещен. Файл находится за пределами плагина.`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!await fse.pathExists(safePath)) {
|
|
339
|
+
return `Ошибка: Файл "${args.filePath}" не найден.`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const content = await fse.readFile(safePath, 'utf8');
|
|
343
|
+
return `Содержимое файла "${args.filePath}":\n\n${content}`;
|
|
344
|
+
} catch (error) {
|
|
345
|
+
return `Ошибка при чтении файла: ${error.message}`;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
// Tool 3: Обновить содержимое файла
|
|
351
|
+
{
|
|
352
|
+
type: 'function',
|
|
353
|
+
function: {
|
|
354
|
+
name: 'updateFile',
|
|
355
|
+
description: 'Обновляет содержимое файла в плагине. ВАЖНО: Полностью заменяет содержимое файла новым.',
|
|
356
|
+
parameters: {
|
|
357
|
+
type: 'object',
|
|
358
|
+
properties: {
|
|
359
|
+
filePath: {
|
|
360
|
+
type: 'string',
|
|
361
|
+
description: 'Относительный путь к файлу внутри плагина (например: "index.js" или "commands/hello.js")'
|
|
362
|
+
},
|
|
363
|
+
content: {
|
|
364
|
+
type: 'string',
|
|
365
|
+
description: 'Новое содержимое файла (полный текст файла)'
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
required: ['filePath', 'content']
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
execute: async (args, context) => {
|
|
372
|
+
console.log('updateFile called for:', args.filePath);
|
|
373
|
+
try {
|
|
374
|
+
const safePath = path.resolve(pluginPath, args.filePath);
|
|
375
|
+
|
|
376
|
+
// Проверка безопасности - файл должен быть внутри pluginPath
|
|
377
|
+
const normalizedPluginPath = pluginPath.endsWith(path.sep) ? pluginPath : pluginPath + path.sep;
|
|
378
|
+
if (!safePath.startsWith(normalizedPluginPath) && safePath !== pluginPath) {
|
|
379
|
+
return `Ошибка: Доступ запрещен. Файл находится за пределами плагина.`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Читаем старое содержимое для подсчёта diff
|
|
383
|
+
let oldContent = '';
|
|
384
|
+
let oldLines = [];
|
|
385
|
+
let isNewFile = false;
|
|
386
|
+
|
|
387
|
+
if (await fse.pathExists(safePath)) {
|
|
388
|
+
oldContent = await fse.readFile(safePath, 'utf8');
|
|
389
|
+
oldLines = oldContent.split('\n');
|
|
390
|
+
} else {
|
|
391
|
+
isNewFile = true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Создаем директорию если не существует
|
|
395
|
+
await fse.ensureDir(path.dirname(safePath));
|
|
396
|
+
|
|
397
|
+
// Записываем новое содержимое
|
|
398
|
+
await fse.writeFile(safePath, args.content, 'utf8');
|
|
399
|
+
|
|
400
|
+
// Вычисляем реальные изменённые строки используя diff
|
|
401
|
+
const newLines = args.content.split('\n');
|
|
402
|
+
let linesAdded = 0;
|
|
403
|
+
let linesRemoved = 0;
|
|
404
|
+
let changedLineRanges = []; // Массив объектов { start: number, end: number }
|
|
405
|
+
|
|
406
|
+
if (isNewFile) {
|
|
407
|
+
linesAdded = newLines.length;
|
|
408
|
+
// Все строки нового файла - это добавленные строки
|
|
409
|
+
changedLineRanges = [{ start: 1, end: newLines.length }];
|
|
410
|
+
} else {
|
|
411
|
+
// Используем библиотеку diff для вычисления изменений
|
|
412
|
+
const diffResult = Diff.diffLines(oldContent, args.content);
|
|
413
|
+
|
|
414
|
+
let currentLine = 1; // Текущая строка в новом файле
|
|
415
|
+
|
|
416
|
+
diffResult.forEach(part => {
|
|
417
|
+
const lineCount = part.count || 0;
|
|
418
|
+
|
|
419
|
+
if (part.added) {
|
|
420
|
+
// Добавленные строки
|
|
421
|
+
linesAdded += lineCount;
|
|
422
|
+
changedLineRanges.push({
|
|
423
|
+
start: currentLine,
|
|
424
|
+
end: currentLine + lineCount - 1
|
|
425
|
+
});
|
|
426
|
+
currentLine += lineCount;
|
|
427
|
+
} else if (part.removed) {
|
|
428
|
+
// Удалённые строки (не увеличиваем currentLine)
|
|
429
|
+
linesRemoved += lineCount;
|
|
430
|
+
} else {
|
|
431
|
+
// Неизменённые строки
|
|
432
|
+
currentLine += lineCount;
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Отправляем событие на фронтенд для обновления редактора
|
|
438
|
+
const sseEvent = {
|
|
439
|
+
type: 'file_updated',
|
|
440
|
+
filePath: args.filePath,
|
|
441
|
+
newContent: args.content,
|
|
442
|
+
oldContent: oldContent,
|
|
443
|
+
linesAdded,
|
|
444
|
+
linesRemoved,
|
|
445
|
+
isNewFile,
|
|
446
|
+
changedLineRanges // Добавляем точные диапазоны изменённых строк
|
|
447
|
+
};
|
|
448
|
+
console.log('Sending SSE event file_updated:', {
|
|
449
|
+
filePath: args.filePath,
|
|
450
|
+
contentLength: args.content.length,
|
|
451
|
+
linesAdded,
|
|
452
|
+
linesRemoved,
|
|
453
|
+
isNewFile,
|
|
454
|
+
changedLineRanges
|
|
455
|
+
});
|
|
456
|
+
res.write(`data: ${JSON.stringify(sseEvent)}\n\n`);
|
|
457
|
+
console.log('SSE event sent successfully');
|
|
458
|
+
|
|
459
|
+
if (isNewFile) {
|
|
460
|
+
return `Создан новый файл "${args.filePath}". Размер: ${args.content.length} символов (${newLines.length} строк).`;
|
|
461
|
+
} else {
|
|
462
|
+
return `Успешно обновлен файл "${args.filePath}". +${linesAdded} -${linesRemoved} строк.`;
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
console.error('Error in updateFile:', error);
|
|
466
|
+
return `Ошибка при обновлении файла: ${error.message}`;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
// Tool 4: Прочитать логи бота (чат из игры и console.log)
|
|
472
|
+
{
|
|
473
|
+
type: 'function',
|
|
474
|
+
function: {
|
|
475
|
+
name: 'readBotLogs',
|
|
476
|
+
description: 'Читает логи бота: игровой чат, системные сообщения и console.log от плагинов. Полезно для отладки плагинов.',
|
|
477
|
+
parameters: {
|
|
478
|
+
type: 'object',
|
|
479
|
+
properties: {
|
|
480
|
+
limit: {
|
|
481
|
+
type: 'number',
|
|
482
|
+
description: 'Максимальное количество записей логов (по умолчанию 50, максимум 200)'
|
|
483
|
+
},
|
|
484
|
+
filter: {
|
|
485
|
+
type: 'string',
|
|
486
|
+
description: 'Фильтр по тексту (опционально). Показывает только логи содержащие этот текст.'
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
required: []
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
execute: async (args) => {
|
|
493
|
+
console.log('readBotLogs called with:', args);
|
|
494
|
+
try {
|
|
495
|
+
const limit = Math.min(args.limit || 50, 200);
|
|
496
|
+
const logs = botManager.getBotLogs(parseInt(botId));
|
|
497
|
+
|
|
498
|
+
if (!logs || logs.length === 0) {
|
|
499
|
+
return 'Логи бота пусты. Возможно бот ещё не запущен или не было активности.';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Берём последние N записей
|
|
503
|
+
let filteredLogs = logs.slice(-limit);
|
|
504
|
+
|
|
505
|
+
// Применяем фильтр если указан
|
|
506
|
+
if (args.filter) {
|
|
507
|
+
const filterLower = args.filter.toLowerCase();
|
|
508
|
+
filteredLogs = filteredLogs.filter(log => {
|
|
509
|
+
const message = typeof log === 'string' ? log : (log.message || JSON.stringify(log));
|
|
510
|
+
return message.toLowerCase().includes(filterLower);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (filteredLogs.length === 0) {
|
|
515
|
+
return `Логи не найдены${args.filter ? ` по фильтру "${args.filter}"` : ''}.`;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Форматируем логи
|
|
519
|
+
const formattedLogs = filteredLogs.map(log => {
|
|
520
|
+
if (typeof log === 'string') {
|
|
521
|
+
return log;
|
|
522
|
+
} else if (log.timestamp && log.message) {
|
|
523
|
+
const time = new Date(log.timestamp).toLocaleTimeString('ru-RU');
|
|
524
|
+
const type = log.type ? `[${log.type}]` : '';
|
|
525
|
+
return `${time} ${type} ${log.message}`;
|
|
526
|
+
} else {
|
|
527
|
+
return JSON.stringify(log);
|
|
528
|
+
}
|
|
529
|
+
}).join('\n');
|
|
530
|
+
|
|
531
|
+
return `Последние ${filteredLogs.length} записей логов бота:\n\n${formattedLogs}`;
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error('Error in readBotLogs:', error);
|
|
534
|
+
return `Ошибка при чтении логов: ${error.message}`;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
// Tool 5: Удалить файл
|
|
540
|
+
{
|
|
541
|
+
type: 'function',
|
|
542
|
+
function: {
|
|
543
|
+
name: 'deleteFile',
|
|
544
|
+
description: 'Удаляет файл из плагина. ВНИМАНИЕ: Операция необратима!',
|
|
545
|
+
parameters: {
|
|
546
|
+
type: 'object',
|
|
547
|
+
properties: {
|
|
548
|
+
filePath: {
|
|
549
|
+
type: 'string',
|
|
550
|
+
description: 'Относительный путь к файлу внутри плагина (например: "old-file.js" или "temp/cache.json")'
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
required: ['filePath']
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
execute: async (args) => {
|
|
557
|
+
console.log('deleteFile called for:', args.filePath);
|
|
558
|
+
try {
|
|
559
|
+
const safePath = path.resolve(pluginPath, args.filePath);
|
|
560
|
+
|
|
561
|
+
// Проверка безопасности - файл должен быть внутри pluginPath
|
|
562
|
+
const normalizedPluginPath = pluginPath.endsWith(path.sep) ? pluginPath : pluginPath + path.sep;
|
|
563
|
+
if (!safePath.startsWith(normalizedPluginPath) && safePath !== pluginPath) {
|
|
564
|
+
return `Ошибка: Доступ запрещен. Файл находится за пределами плагина.`;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Проверяем что путь существует
|
|
568
|
+
if (!await fse.pathExists(safePath)) {
|
|
569
|
+
return `Ошибка: Файл "${args.filePath}" не найден.`;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Проверяем что это файл, а не директория
|
|
573
|
+
const stats = await fse.stat(safePath);
|
|
574
|
+
if (stats.isDirectory()) {
|
|
575
|
+
return `Ошибка: "${args.filePath}" является папкой. Используйте deleteFolder для удаления папок.`;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Удаляем файл
|
|
579
|
+
await fse.remove(safePath);
|
|
580
|
+
|
|
581
|
+
// Отправляем событие на фронтенд
|
|
582
|
+
const sseEvent = {
|
|
583
|
+
type: 'file_deleted',
|
|
584
|
+
filePath: args.filePath
|
|
585
|
+
};
|
|
586
|
+
res.write(`data: ${JSON.stringify(sseEvent)}\n\n`);
|
|
587
|
+
|
|
588
|
+
return `Файл "${args.filePath}" успешно удалён.`;
|
|
589
|
+
} catch (error) {
|
|
590
|
+
console.error('Error in deleteFile:', error);
|
|
591
|
+
return `Ошибка при удалении файла: ${error.message}`;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
// Tool 6: Удалить папку
|
|
597
|
+
{
|
|
598
|
+
type: 'function',
|
|
599
|
+
function: {
|
|
600
|
+
name: 'deleteFolder',
|
|
601
|
+
description: 'Удаляет папку и всё её содержимое из плагина. ВНИМАНИЕ: Операция необратима! Удаляет папку рекурсивно со всеми вложенными файлами и папками.',
|
|
602
|
+
parameters: {
|
|
603
|
+
type: 'object',
|
|
604
|
+
properties: {
|
|
605
|
+
folderPath: {
|
|
606
|
+
type: 'string',
|
|
607
|
+
description: 'Относительный путь к папке внутри плагина (например: "temp" или "old-modules/cache")'
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
required: ['folderPath']
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
execute: async (args) => {
|
|
614
|
+
console.log('deleteFolder called for:', args.folderPath);
|
|
615
|
+
try {
|
|
616
|
+
const safePath = path.resolve(pluginPath, args.folderPath);
|
|
617
|
+
|
|
618
|
+
// Проверка безопасности - папка должна быть внутри pluginPath
|
|
619
|
+
const normalizedPluginPath = pluginPath.endsWith(path.sep) ? pluginPath : pluginPath + path.sep;
|
|
620
|
+
if (!safePath.startsWith(normalizedPluginPath) && safePath !== pluginPath) {
|
|
621
|
+
return `Ошибка: Доступ запрещен. Папка находится за пределами плагина.`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Защита от удаления корневой директории плагина
|
|
625
|
+
if (safePath === pluginPath) {
|
|
626
|
+
return `Ошибка: Нельзя удалить корневую директорию плагина.`;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Проверяем что путь существует
|
|
630
|
+
if (!await fse.pathExists(safePath)) {
|
|
631
|
+
return `Ошибка: Папка "${args.folderPath}" не найдена.`;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Проверяем что это директория
|
|
635
|
+
const stats = await fse.stat(safePath);
|
|
636
|
+
if (!stats.isDirectory()) {
|
|
637
|
+
return `Ошибка: "${args.folderPath}" является файлом. Используйте deleteFile для удаления файлов.`;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Подсчитываем количество файлов и папок внутри
|
|
641
|
+
const items = await fse.readdir(safePath);
|
|
642
|
+
const itemCount = items.length;
|
|
643
|
+
|
|
644
|
+
// Удаляем папку рекурсивно
|
|
645
|
+
await fse.remove(safePath);
|
|
646
|
+
|
|
647
|
+
// Отправляем событие на фронтенд
|
|
648
|
+
const sseEvent = {
|
|
649
|
+
type: 'folder_deleted',
|
|
650
|
+
folderPath: args.folderPath
|
|
651
|
+
};
|
|
652
|
+
res.write(`data: ${JSON.stringify(sseEvent)}\n\n`);
|
|
653
|
+
|
|
654
|
+
return `Папка "${args.folderPath}" успешно удалена (содержала ${itemCount} элементов).`;
|
|
655
|
+
} catch (error) {
|
|
656
|
+
console.error('Error in deleteFolder:', error);
|
|
657
|
+
return `Ошибка при удалении папки: ${error.message}`;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
];
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* POST /api/bots/:botId/plugins/ide/:pluginName/ai/chat
|
|
666
|
+
* Отправляет сообщение в AI чат с контекстом плагина
|
|
667
|
+
*/
|
|
668
|
+
router.post('/chat', resolvePluginPath, async (req, res) => {
|
|
669
|
+
console.log('Route hit! botId:', req.params.botId, 'pluginName:', req.params.pluginName);
|
|
670
|
+
try {
|
|
671
|
+
const { message, provider, apiKey, apiEndpoint, model, history, includeFiles, proxy } = req.body;
|
|
672
|
+
const { botId, pluginName } = req.params;
|
|
673
|
+
|
|
674
|
+
const aiProvider = provider || 'openrouter';
|
|
675
|
+
console.log('AI Provider:', aiProvider);
|
|
676
|
+
console.log('Proxy config:', proxy);
|
|
677
|
+
|
|
678
|
+
if (!message) {
|
|
679
|
+
return res.status(400).json({ error: 'Message is required.' });
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (!apiKey) {
|
|
683
|
+
return res.status(400).json({ error: 'API key is required.' });
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const systemPromptPath = path.join(__dirname, '../../ai/plugin-assistant-system-prompt.md');
|
|
687
|
+
let systemPrompt = 'Ты - AI помощник для разработки плагинов в BlockMine IDE.';
|
|
688
|
+
|
|
689
|
+
if (await fse.pathExists(systemPromptPath)) {
|
|
690
|
+
systemPrompt = await fse.readFile(systemPromptPath, 'utf8');
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
let context = '';
|
|
694
|
+
|
|
695
|
+
const packageJsonPath = path.join(req.pluginPath, 'package.json');
|
|
696
|
+
if (await fse.pathExists(packageJsonPath)) {
|
|
697
|
+
const packageJson = await fse.readJson(packageJsonPath);
|
|
698
|
+
context += `\n\n## Package.json плагина:\n\`\`\`json\n${JSON.stringify(packageJson, null, 2)}\n\`\`\`\n`;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Добавляем файлы если запрошено
|
|
702
|
+
if (includeFiles && Array.isArray(includeFiles)) {
|
|
703
|
+
for (const fileName of includeFiles) {
|
|
704
|
+
const filePath = path.join(req.pluginPath, fileName);
|
|
705
|
+
if (await fse.pathExists(filePath)) {
|
|
706
|
+
const fileContent = await fse.readFile(filePath, 'utf8');
|
|
707
|
+
context += `\n\n## Файл ${fileName}:\n\`\`\`javascript\n${fileContent}\n\`\`\`\n`;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const proxyConfig = parseProxyString(proxy);
|
|
713
|
+
if (proxyConfig) {
|
|
714
|
+
console.log('Using proxy:', `${proxyConfig.user}:***@${proxyConfig.host}:${proxyConfig.port}`);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
let client;
|
|
718
|
+
if (aiProvider === 'google') {
|
|
719
|
+
const googleConfig = {
|
|
720
|
+
apiKeys: [apiKey],
|
|
721
|
+
defaultModel: model
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
if (proxyConfig) {
|
|
725
|
+
googleConfig.proxy = proxyConfig;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
client = new GeminiClient(googleConfig);
|
|
729
|
+
console.log('Created Google Gemini client');
|
|
730
|
+
} else {
|
|
731
|
+
// OpenRouter
|
|
732
|
+
const clientConfig = {
|
|
733
|
+
apiKey: apiKey,
|
|
734
|
+
model: model,
|
|
735
|
+
historyAdapter: new MemoryHistoryStorage(),
|
|
736
|
+
debug: true
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
// Добавляем кастомный endpoint если указан
|
|
740
|
+
if (apiEndpoint && apiEndpoint !== 'https://openrouter.ai/api/v1') {
|
|
741
|
+
clientConfig.apiEndpoint = apiEndpoint;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Добавляем прокси если указан
|
|
745
|
+
if (proxyConfig) {
|
|
746
|
+
clientConfig.proxy = proxyConfig;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
client = new OpenRouterClient(clientConfig);
|
|
750
|
+
console.log('Created OpenRouter client');
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Получаем или создаем историю для этого бота и плагина
|
|
754
|
+
const chatKey = `${botId}_${pluginName}`;
|
|
755
|
+
if (!chatHistoryStore.has(chatKey)) {
|
|
756
|
+
chatHistoryStore.set(chatKey, []);
|
|
757
|
+
}
|
|
758
|
+
const storedHistory = chatHistoryStore.get(chatKey);
|
|
759
|
+
|
|
760
|
+
// Формируем customMessages
|
|
761
|
+
const customMessages = [
|
|
762
|
+
{
|
|
763
|
+
role: 'system',
|
|
764
|
+
content: systemPrompt + context
|
|
765
|
+
}
|
|
766
|
+
];
|
|
767
|
+
|
|
768
|
+
// Добавляем сохранённую историю
|
|
769
|
+
customMessages.push(...storedHistory);
|
|
770
|
+
|
|
771
|
+
// Добавляем текущее сообщение
|
|
772
|
+
const userMessage = {
|
|
773
|
+
role: 'user',
|
|
774
|
+
content: message
|
|
775
|
+
};
|
|
776
|
+
customMessages.push(userMessage);
|
|
777
|
+
|
|
778
|
+
// Сохраняем сообщение пользователя в историю
|
|
779
|
+
storedHistory.push(userMessage);
|
|
780
|
+
|
|
781
|
+
// Создаем tools для этого плагина (только для OpenRouter пока)
|
|
782
|
+
const pluginTools = createPluginTools(req.pluginPath, res, botId);
|
|
783
|
+
|
|
784
|
+
let fullResponse = '';
|
|
785
|
+
let assistantMessage = { role: 'assistant', content: '' };
|
|
786
|
+
|
|
787
|
+
// Google Gemini использует другой API
|
|
788
|
+
if (aiProvider === 'google') {
|
|
789
|
+
// Устанавливаем SSE заголовки
|
|
790
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
791
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
792
|
+
res.setHeader('Connection', 'keep-alive');
|
|
793
|
+
|
|
794
|
+
try {
|
|
795
|
+
console.log('[Google] Converting history...');
|
|
796
|
+
// Конвертируем историю в формат Gemini
|
|
797
|
+
const geminiHistory = [];
|
|
798
|
+
storedHistory.forEach(msg => {
|
|
799
|
+
if (msg.role === 'user') {
|
|
800
|
+
geminiHistory.push({ role: 'user', parts: [{ text: msg.content }] });
|
|
801
|
+
} else if (msg.role === 'assistant') {
|
|
802
|
+
geminiHistory.push({ role: 'model', parts: [{ text: msg.content }] });
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
console.log('[Google] History length:', geminiHistory.length);
|
|
806
|
+
|
|
807
|
+
// Конвертируем tools в формат Gemini с wrapper для SSE событий
|
|
808
|
+
console.log('[Google] Converting tools...');
|
|
809
|
+
const geminiTools = [{
|
|
810
|
+
functionDeclarations: pluginTools.map(tool => ({
|
|
811
|
+
name: tool.function.name,
|
|
812
|
+
description: tool.function.description,
|
|
813
|
+
parameters: convertToGeminiParameters(tool.function.parameters),
|
|
814
|
+
execute: async (args) => {
|
|
815
|
+
// Отправляем событие начала выполнения tool
|
|
816
|
+
if (!res.writableEnded) {
|
|
817
|
+
res.write(`data: ${JSON.stringify({
|
|
818
|
+
type: 'tool_call',
|
|
819
|
+
toolName: tool.function.name,
|
|
820
|
+
args
|
|
821
|
+
})}\n\n`);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const result = await tool.execute(args);
|
|
825
|
+
|
|
826
|
+
if (!res.writableEnded) {
|
|
827
|
+
res.write(`data: ${JSON.stringify({
|
|
828
|
+
type: 'tool_result',
|
|
829
|
+
toolName: tool.function.name,
|
|
830
|
+
result
|
|
831
|
+
})}\n\n`);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return result;
|
|
835
|
+
}
|
|
836
|
+
}))
|
|
837
|
+
}];
|
|
838
|
+
console.log('[Google] Converted', geminiTools[0].functionDeclarations.length, 'tools');
|
|
839
|
+
|
|
840
|
+
console.log('[Google] Creating chat...');
|
|
841
|
+
const chat = client.chats.create({
|
|
842
|
+
systemInstruction: systemPrompt + context,
|
|
843
|
+
history: geminiHistory,
|
|
844
|
+
tools: geminiTools,
|
|
845
|
+
maxToolCalls: 10
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
console.log('[Google] Sending message...');
|
|
849
|
+
const response = await chat.sendMessage(message);
|
|
850
|
+
const content = response.text();
|
|
851
|
+
console.log('[Google] Response received, length:', content.length);
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
if (!res.writableEnded) {
|
|
855
|
+
res.write(`data: ${JSON.stringify({ type: 'chunk', content })}\n\n`);
|
|
856
|
+
res.write(`data: ${JSON.stringify({ type: 'done', fullResponse: content })}\n\n`);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
assistantMessage.content = content;
|
|
860
|
+
storedHistory.push(assistantMessage);
|
|
861
|
+
|
|
862
|
+
console.log('[Google] Closing SSE stream');
|
|
863
|
+
res.end();
|
|
864
|
+
return;
|
|
865
|
+
} catch (error) {
|
|
866
|
+
console.error('[Google AI Error]:', error);
|
|
867
|
+
if (!res.writableEnded) {
|
|
868
|
+
res.write(`data: ${JSON.stringify({ type: 'error', error: error.message })}\n\n`);
|
|
869
|
+
res.end();
|
|
870
|
+
}
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
876
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
877
|
+
res.setHeader('Connection', 'keep-alive');
|
|
878
|
+
|
|
879
|
+
try {
|
|
880
|
+
await client.chatStream({
|
|
881
|
+
customMessages: customMessages,
|
|
882
|
+
temperature: 0.7,
|
|
883
|
+
maxTokens: 4096,
|
|
884
|
+
tools: pluginTools,
|
|
885
|
+
includeToolResultInReport: true,
|
|
886
|
+
streamCallbacks: {
|
|
887
|
+
onToolCallExecuting: (toolName, args) => {
|
|
888
|
+
console.log(`Executing tool: ${toolName}`, args);
|
|
889
|
+
res.write(`data: ${JSON.stringify({ type: 'tool_call', toolName, args })}\n\n`);
|
|
890
|
+
},
|
|
891
|
+
onToolCallResult: (toolName, result) => {
|
|
892
|
+
console.log(`Tool result: ${toolName}`, typeof result === 'string' ? result.substring(0, 100) : result);
|
|
893
|
+
res.write(`data: ${JSON.stringify({ type: 'tool_result', toolName, result })}\n\n`);
|
|
894
|
+
},
|
|
895
|
+
onContent: (content) => {
|
|
896
|
+
assistantMessage.content += content;
|
|
897
|
+
res.write(`data: ${JSON.stringify({ type: 'chunk', content })}\n\n`);
|
|
898
|
+
},
|
|
899
|
+
onComplete: (fullContent, usage) => {
|
|
900
|
+
fullResponse = fullContent;
|
|
901
|
+
assistantMessage.content = fullContent;
|
|
902
|
+
res.write(`data: ${JSON.stringify({ type: 'done', fullResponse: fullContent })}\n\n`);
|
|
903
|
+
},
|
|
904
|
+
onError: (error) => {
|
|
905
|
+
console.error('[AI Assistant Stream Error]:', error);
|
|
906
|
+
res.write(`data: ${JSON.stringify({ type: 'error', error: error.message })}\n\n`);
|
|
907
|
+
res.end();
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
if (assistantMessage.content) {
|
|
913
|
+
storedHistory.push(assistantMessage);
|
|
914
|
+
console.log(`Saved to history. Total messages: ${storedHistory.length}`);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
console.log('Stream completed, closing connection');
|
|
918
|
+
res.end();
|
|
919
|
+
|
|
920
|
+
} catch (streamError) {
|
|
921
|
+
console.error('[AI Assistant Streaming Error]:', streamError);
|
|
922
|
+
if (!res.headersSent) {
|
|
923
|
+
res.status(500).json({ error: streamError.message });
|
|
924
|
+
} else if (!res.writableEnded) {
|
|
925
|
+
res.write(`data: ${JSON.stringify({ type: 'error', error: streamError.message })}\n\n`);
|
|
926
|
+
res.end();
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
} catch (error) {
|
|
931
|
+
console.error('[AI Assistant Error]:', error);
|
|
932
|
+
|
|
933
|
+
// Если заголовки еще не отправлены, отправляем JSON ошибку
|
|
934
|
+
if (!res.headersSent) {
|
|
935
|
+
res.status(500).json({
|
|
936
|
+
error: error.message || 'Failed to process AI request.'
|
|
937
|
+
});
|
|
938
|
+
} else {
|
|
939
|
+
// Если стриминг уже начался, отправляем ошибку через SSE
|
|
940
|
+
res.write(`data: ${JSON.stringify({ type: 'error', error: error.message })}\n\n`);
|
|
941
|
+
res.end();
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* GET /api/bots/:botId/plugins/ide/:pluginName/ai/chat
|
|
948
|
+
* Получает историю чата для конкретного бота и плагина
|
|
949
|
+
*/
|
|
950
|
+
router.get('/chat', resolvePluginPath, async (req, res) => {
|
|
951
|
+
try {
|
|
952
|
+
const { botId } = req.params;
|
|
953
|
+
const pluginName = req.params.pluginName;
|
|
954
|
+
const chatKey = `${botId}_${pluginName}`;
|
|
955
|
+
|
|
956
|
+
console.log('GET chat - botId:', botId, 'pluginName:', pluginName, 'chatKey:', chatKey);
|
|
957
|
+
|
|
958
|
+
const history = chatHistoryStore.get(chatKey) || [];
|
|
959
|
+
console.log(`Loading history for ${chatKey}: ${history.length} messages`);
|
|
960
|
+
|
|
961
|
+
res.json({ history });
|
|
962
|
+
} catch (error) {
|
|
963
|
+
console.error('Error loading history:', error);
|
|
964
|
+
res.status(500).json({ error: error.message });
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* DELETE /api/bots/:botId/plugins/ide/:pluginName/ai/chat
|
|
970
|
+
* Очищает историю чата для конкретного бота и плагина
|
|
971
|
+
*/
|
|
972
|
+
router.delete('/chat', resolvePluginPath, async (req, res) => {
|
|
973
|
+
try {
|
|
974
|
+
const { botId, pluginName } = req.params;
|
|
975
|
+
|
|
976
|
+
console.log('Clear history request for:', { botId, pluginName });
|
|
977
|
+
|
|
978
|
+
const chatKey = `${botId}_${pluginName}`;
|
|
979
|
+
|
|
980
|
+
if (chatHistoryStore.has(chatKey)) {
|
|
981
|
+
const messageCount = chatHistoryStore.get(chatKey).length;
|
|
982
|
+
chatHistoryStore.delete(chatKey);
|
|
983
|
+
console.log(`Cleared ${messageCount} messages from history`);
|
|
984
|
+
res.json({ success: true, cleared: messageCount });
|
|
985
|
+
} else {
|
|
986
|
+
console.log('No history found to clear');
|
|
987
|
+
res.json({ success: true, cleared: 0 });
|
|
988
|
+
}
|
|
989
|
+
} catch (error) {
|
|
990
|
+
console.error('Error clearing history:', error);
|
|
991
|
+
res.status(500).json({ error: error.message });
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
module.exports = router;
|