@omnizap-system/omnizap 2.5.12

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.
Files changed (425) hide show
  1. package/.clusterfuzzlite/Dockerfile +10 -0
  2. package/.env.example +907 -0
  3. package/.github/codeql/codeql-config.yml +10 -0
  4. package/.github/dependabot.yml +35 -0
  5. package/.github/workflows/ci.yml +73 -0
  6. package/.github/workflows/codeql.yml +106 -0
  7. package/.github/workflows/db-migration-check.yml +98 -0
  8. package/.github/workflows/dependency-review.yml +22 -0
  9. package/.github/workflows/deploy.yml +95 -0
  10. package/.github/workflows/release.yml +106 -0
  11. package/.github/workflows/security-attest-provenance.yml +51 -0
  12. package/.github/workflows/security-gitleaks.yml +34 -0
  13. package/.github/workflows/security-runner-hardening.yml +31 -0
  14. package/.github/workflows/security-scorecard.yml +44 -0
  15. package/.github/workflows/security-zap-baseline.yml +44 -0
  16. package/.github/workflows/security-zap-full-scan.yml +43 -0
  17. package/.github/workflows/security-zizmor.yml +36 -0
  18. package/.github/workflows/wiki-sync.yml +44 -0
  19. package/.gitleaks.toml +15 -0
  20. package/.prettierrc +34 -0
  21. package/CODE_OF_CONDUCT.md +114 -0
  22. package/LICENSE +56 -0
  23. package/README.md +110 -0
  24. package/SECURITY.md +110 -0
  25. package/app/config/index.js +4 -0
  26. package/app/configParts/adminIdentity.js +92 -0
  27. package/app/configParts/baileysConfig.js +1818 -0
  28. package/app/configParts/groupUtils.js +692 -0
  29. package/app/configParts/loggerConfig.js +394 -0
  30. package/app/configParts/messagePersistenceService.js +305 -0
  31. package/app/connection/baileysCompatibility.test.js +40 -0
  32. package/app/connection/baileysDbAuthState.js +344 -0
  33. package/app/connection/socketController.js +2243 -0
  34. package/app/controllers/messageController.js +7 -0
  35. package/app/controllers/messagePipeline/commandMiddleware.js +146 -0
  36. package/app/controllers/messagePipeline/conversationMiddleware.js +183 -0
  37. package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +522 -0
  38. package/app/controllers/messagePipeline/postProcessingMiddleware.js +41 -0
  39. package/app/controllers/messagePipeline/preProcessingMiddlewares.js +166 -0
  40. package/app/controllers/messageProcessingPipeline.js +699 -0
  41. package/app/modules/adminModule/AGENT.md +4056 -0
  42. package/app/modules/adminModule/adminAiHelpService.js +56 -0
  43. package/app/modules/adminModule/adminConfigRuntime.js +177 -0
  44. package/app/modules/adminModule/commandConfig.json +7122 -0
  45. package/app/modules/adminModule/groupCommandHandlers.js +1823 -0
  46. package/app/modules/adminModule/groupCommandHandlers.test.js +350 -0
  47. package/app/modules/adminModule/groupEventHandlers.js +399 -0
  48. package/app/modules/aiModule/AGENT.md +547 -0
  49. package/app/modules/aiModule/aiAiHelpService.js +14 -0
  50. package/app/modules/aiModule/aiConfigRuntime.js +135 -0
  51. package/app/modules/aiModule/catCommand.js +967 -0
  52. package/app/modules/aiModule/commandConfig.json +981 -0
  53. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
  54. package/app/modules/gameModule/AGENT.md +196 -0
  55. package/app/modules/gameModule/commandConfig.json +366 -0
  56. package/app/modules/gameModule/diceCommand.js +42 -0
  57. package/app/modules/gameModule/gameAiHelpService.js +14 -0
  58. package/app/modules/gameModule/gameConfigRuntime.js +68 -0
  59. package/app/modules/menuModule/AGENT.md +205 -0
  60. package/app/modules/menuModule/commandConfig.json +366 -0
  61. package/app/modules/menuModule/common.js +316 -0
  62. package/app/modules/menuModule/menuAiHelpService.js +14 -0
  63. package/app/modules/menuModule/menuConfigRuntime.js +68 -0
  64. package/app/modules/menuModule/menus.js +66 -0
  65. package/app/modules/playModule/AGENT.md +321 -0
  66. package/app/modules/playModule/commandConfig.json +584 -0
  67. package/app/modules/playModule/playAiHelpService.js +14 -0
  68. package/app/modules/playModule/playCommand.js +1417 -0
  69. package/app/modules/playModule/playConfigRuntime.js +68 -0
  70. package/app/modules/quoteModule/AGENT.md +199 -0
  71. package/app/modules/quoteModule/commandConfig.json +366 -0
  72. package/app/modules/quoteModule/quoteAiHelpService.js +14 -0
  73. package/app/modules/quoteModule/quoteCommand.js +842 -0
  74. package/app/modules/quoteModule/quoteConfigRuntime.js +68 -0
  75. package/app/modules/rpgPokemonModule/AGENT.md +229 -0
  76. package/app/modules/rpgPokemonModule/commandConfig.json +386 -0
  77. package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +795 -0
  78. package/app/modules/rpgPokemonModule/rpgBattleService.js +2110 -0
  79. package/app/modules/rpgPokemonModule/rpgBattleService.test.js +770 -0
  80. package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
  81. package/app/modules/rpgPokemonModule/rpgPokemonAiHelpService.js +14 -0
  82. package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +174 -0
  83. package/app/modules/rpgPokemonModule/rpgPokemonConfigRuntime.js +68 -0
  84. package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
  85. package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
  86. package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
  87. package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
  88. package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1847 -0
  89. package/app/modules/rpgPokemonModule/rpgPokemonService.js +6839 -0
  90. package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
  91. package/app/modules/statsModule/AGENT.md +320 -0
  92. package/app/modules/statsModule/commandConfig.json +540 -0
  93. package/app/modules/statsModule/globalRankingCommand.js +64 -0
  94. package/app/modules/statsModule/rankingCommand.js +41 -0
  95. package/app/modules/statsModule/rankingCommon.js +1305 -0
  96. package/app/modules/statsModule/statsAiHelpService.js +14 -0
  97. package/app/modules/statsModule/statsConfigRuntime.js +68 -0
  98. package/app/modules/stickerModule/AGENT.md +692 -0
  99. package/app/modules/stickerModule/addStickerMetadata.js +239 -0
  100. package/app/modules/stickerModule/commandConfig.json +1216 -0
  101. package/app/modules/stickerModule/convertToWebp.js +367 -0
  102. package/app/modules/stickerModule/stickerAiHelpService.js +14 -0
  103. package/app/modules/stickerModule/stickerCommand.js +446 -0
  104. package/app/modules/stickerModule/stickerConfigRuntime.js +68 -0
  105. package/app/modules/stickerModule/stickerConvertCommand.js +159 -0
  106. package/app/modules/stickerModule/stickerTextCommand.js +653 -0
  107. package/app/modules/stickerPackModule/AGENT.md +215 -0
  108. package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
  109. package/app/modules/stickerPackModule/autoPackCollectorService.js +357 -0
  110. package/app/modules/stickerPackModule/commandConfig.json +387 -0
  111. package/app/modules/stickerPackModule/domainEventOutboxRepository.js +227 -0
  112. package/app/modules/stickerPackModule/domainEvents.js +52 -0
  113. package/app/modules/stickerPackModule/semanticReclassificationEngine.js +429 -0
  114. package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +75 -0
  115. package/app/modules/stickerPackModule/semanticThemeClusterService.js +544 -0
  116. package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +400 -0
  117. package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
  118. package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +175 -0
  119. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +3702 -0
  120. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +559 -0
  121. package/app/modules/stickerPackModule/stickerClassificationService.js +557 -0
  122. package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +249 -0
  123. package/app/modules/stickerPackModule/stickerDomainEventBus.js +65 -0
  124. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +208 -0
  125. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +99 -0
  126. package/app/modules/stickerPackModule/stickerObjectStorageService.js +285 -0
  127. package/app/modules/stickerPackModule/stickerPackAiHelpService.js +14 -0
  128. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1148 -0
  129. package/app/modules/stickerPackModule/stickerPackConfigRuntime.js +68 -0
  130. package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +152 -0
  131. package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
  132. package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +101 -0
  133. package/app/modules/stickerPackModule/stickerPackItemRepository.js +432 -0
  134. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +313 -0
  135. package/app/modules/stickerPackModule/stickerPackMessageService.js +268 -0
  136. package/app/modules/stickerPackModule/stickerPackRepository.js +450 -0
  137. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +179 -0
  138. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +271 -0
  139. package/app/modules/stickerPackModule/stickerPackService.js +733 -0
  140. package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +32 -0
  141. package/app/modules/stickerPackModule/stickerPackUtils.js +107 -0
  142. package/app/modules/stickerPackModule/stickerStorageService.js +559 -0
  143. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +242 -0
  144. package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +242 -0
  145. package/app/modules/systemMetricsModule/AGENT.md +193 -0
  146. package/app/modules/systemMetricsModule/commandConfig.json +344 -0
  147. package/app/modules/systemMetricsModule/pingCommand.js +399 -0
  148. package/app/modules/systemMetricsModule/systemMetricsAiHelpService.js +14 -0
  149. package/app/modules/systemMetricsModule/systemMetricsConfigRuntime.js +68 -0
  150. package/app/modules/tiktokModule/AGENT.md +196 -0
  151. package/app/modules/tiktokModule/commandConfig.json +366 -0
  152. package/app/modules/tiktokModule/tiktokAiHelpService.js +14 -0
  153. package/app/modules/tiktokModule/tiktokCommand.js +716 -0
  154. package/app/modules/tiktokModule/tiktokConfigRuntime.js +68 -0
  155. package/app/modules/userModule/AGENT.md +200 -0
  156. package/app/modules/userModule/commandConfig.json +386 -0
  157. package/app/modules/userModule/userAiHelpService.js +14 -0
  158. package/app/modules/userModule/userCommand.js +1155 -0
  159. package/app/modules/userModule/userConfigRuntime.js +68 -0
  160. package/app/modules/waifuPicsModule/AGENT.md +431 -0
  161. package/app/modules/waifuPicsModule/commandConfig.json +780 -0
  162. package/app/modules/waifuPicsModule/waifuPicsAiHelpService.js +14 -0
  163. package/app/modules/waifuPicsModule/waifuPicsCommand.js +586 -0
  164. package/app/modules/waifuPicsModule/waifuPicsConfigRuntime.js +68 -0
  165. package/app/observability/metrics.js +766 -0
  166. package/app/services/ai/aiHelpResponseCacheRepository.js +280 -0
  167. package/app/services/ai/aiLearningRepository.js +400 -0
  168. package/app/services/ai/commandConfigEnrichmentRepository.js +769 -0
  169. package/app/services/ai/commandConfigEnrichmentService.js +452 -0
  170. package/app/services/ai/commandConfigValidationService.js +443 -0
  171. package/app/services/ai/commandToolBuilderService.js +192 -0
  172. package/app/services/ai/conversationRouterService.js +516 -0
  173. package/app/services/ai/geminiService.js +115 -0
  174. package/app/services/ai/geminiService.test.js +87 -0
  175. package/app/services/ai/globalModuleAiHelpService.js +1412 -0
  176. package/app/services/ai/globalToolCallingService.js +203 -0
  177. package/app/services/ai/messageCommandExecutionService.js +391 -0
  178. package/app/services/ai/moduleAiHelpCoreService.js +1099 -0
  179. package/app/services/ai/moduleAiHelpWrapperFactory.js +65 -0
  180. package/app/services/ai/moduleCommandConfigRuntimeService.js +113 -0
  181. package/app/services/ai/moduleToolExecutorService.js +464 -0
  182. package/app/services/ai/moduleToolRegistryService.js +178 -0
  183. package/app/services/ai/toolCandidateSelectorService.js +781 -0
  184. package/app/services/auth/googleWebLinkService.js +80 -0
  185. package/app/services/auth/whatsappLoginLinkService.js +230 -0
  186. package/app/services/external/pokeApiService.js +398 -0
  187. package/app/services/group/groupMetadataService.js +311 -0
  188. package/app/services/infra/dbWriteQueue.js +874 -0
  189. package/app/services/infra/featureFlagService.js +131 -0
  190. package/app/services/infra/queueUtils.js +55 -0
  191. package/app/services/messaging/captchaService.js +491 -0
  192. package/app/services/messaging/messagePersistenceService.js +1 -0
  193. package/app/services/messaging/newsBroadcastService.js +347 -0
  194. package/app/services/sticker/stickerFocusService.js +347 -0
  195. package/app/services/sticker/stickerFocusService.test.js +43 -0
  196. package/app/store/aiPromptStore.js +38 -0
  197. package/app/store/conversationSessionStore.js +131 -0
  198. package/app/store/groupConfigStore.js +58 -0
  199. package/app/store/premiumUserStore.js +54 -0
  200. package/app/utils/antiLink/antiLinkModule.js +700 -0
  201. package/app/utils/http/getImageBufferModule.js +18 -0
  202. package/app/utils/json/jsonSanitizer.js +113 -0
  203. package/app/utils/json/jsonSanitizer.test.js +40 -0
  204. package/app/utils/systemMetrics/systemMetricsModule.js +88 -0
  205. package/app/workers/aiLearningWorker.js +605 -0
  206. package/app/workers/commandConfigEnrichmentWorker.js +242 -0
  207. package/database/index.js +2075 -0
  208. package/database/init.js +151 -0
  209. package/database/migrations/.gitkeep +0 -0
  210. package/database/migrations/20260307_d0_hardening_down.sql +64 -0
  211. package/database/migrations/20260307_d0_hardening_up.sql +79 -0
  212. package/database/migrations/20260307_d1_terms_acceptance_down.sql +11 -0
  213. package/database/migrations/20260307_d1_terms_acceptance_up.sql +37 -0
  214. package/database/migrations/20260307_d2_auth_hardening_down.sql +75 -0
  215. package/database/migrations/20260307_d2_auth_hardening_up.sql +100 -0
  216. package/database/migrations/20260314_d7_canonical_sender_down.sql +53 -0
  217. package/database/migrations/20260314_d7_canonical_sender_up.sql +114 -0
  218. package/database/migrations/20260406_d30_security_analytics_down.sql +95 -0
  219. package/database/migrations/20260406_d30_security_analytics_up.sql +292 -0
  220. package/database/migrations/20260407_d31_web_google_session_token_hardening_down.sql +2 -0
  221. package/database/migrations/20260407_d31_web_google_session_token_hardening_up.sql +17 -0
  222. package/database/migrations/20260408_d32_ai_help_response_cache_down.sql +1 -0
  223. package/database/migrations/20260408_d32_ai_help_response_cache_up.sql +22 -0
  224. package/database/migrations/20260409_d33_ai_learning_tables_down.sql +4 -0
  225. package/database/migrations/20260409_d33_ai_learning_tables_up.sql +52 -0
  226. package/database/migrations/20260410_d34_command_config_enrichment_down.sql +3 -0
  227. package/database/migrations/20260410_d34_command_config_enrichment_up.sql +48 -0
  228. package/database/schema.sql +1186 -0
  229. package/docker-compose.yml +104 -0
  230. package/docs/audits/stickerCatalogController-out-of-scope.md +103 -0
  231. package/docs/audits/stickerCatalogController-symbols.md +58 -0
  232. package/docs/compliance/acceptable-use-policy-2026-03-07.md +35 -0
  233. package/docs/compliance/dpa-b2b-standard-2026-03-07.md +80 -0
  234. package/docs/compliance/monthly-compliance-checklist-2026-03-07.md +88 -0
  235. package/docs/compliance/notice-and-takedown-policy-2026-03-07.md +34 -0
  236. package/docs/compliance/privacy-policy-2026-03-07.md +75 -0
  237. package/docs/compliance/subprocessors-inventory-2026-03-07.md +16 -0
  238. package/docs/database/production-db-evolution-runbook-2026q1.md +365 -0
  239. package/docs/security/dsar-lgpd-runbook-2026-03-07.md +86 -0
  240. package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +77 -0
  241. package/docs/security/network-hardening-runbook-2026-03-07.md +137 -0
  242. package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +238 -0
  243. package/docs/seo/satellite-page-template.md +116 -0
  244. package/docs/seo/satellite-pages-phase1.json +364 -0
  245. package/docs/wiki/Home.md +120 -0
  246. package/docs/wiki/pair-extraordinaire-2026-03-08.md +3 -0
  247. package/docs/wiki/recent-changes-2026-03-08.md +47 -0
  248. package/ecosystem.prod.config.cjs +135 -0
  249. package/eslint.config.js +89 -0
  250. package/index.js +488 -0
  251. package/ml/clip_classifier/Dockerfile +18 -0
  252. package/ml/clip_classifier/README.md +118 -0
  253. package/ml/clip_classifier/adaptive_scoring.py +40 -0
  254. package/ml/clip_classifier/classifier.py +654 -0
  255. package/ml/clip_classifier/embedding_store.py +481 -0
  256. package/ml/clip_classifier/env_loader.py +15 -0
  257. package/ml/clip_classifier/llm_label_expander.py +144 -0
  258. package/ml/clip_classifier/main.py +213 -0
  259. package/ml/clip_classifier/requirements.txt +10 -0
  260. package/ml/clip_classifier/similarity_engine.py +74 -0
  261. package/new-logo.png +0 -0
  262. package/observability/alert-rules.yml +60 -0
  263. package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
  264. package/observability/grafana/dashboards/omnizap-overview.json +170 -0
  265. package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
  266. package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
  267. package/observability/loki-config.yml +38 -0
  268. package/observability/mysql-setup.sql +46 -0
  269. package/observability/prometheus.yml +35 -0
  270. package/observability/promtail-config.yml +84 -0
  271. package/observability/sticker-catalog-slo.md +83 -0
  272. package/observability/sticker-scale-hardening-rollout.md +128 -0
  273. package/package.json +144 -0
  274. package/public/apple-touch-icon.png +0 -0
  275. package/public/assets/css/commands-react.input.css +71 -0
  276. package/public/assets/css/create-pack-react.input.css +31 -0
  277. package/public/assets/css/home-react.input.css +106 -0
  278. package/public/assets/css/login-react.input.css +58 -0
  279. package/public/assets/css/stickers-react.input.css +18 -0
  280. package/public/assets/css/terms-react.input.css +115 -0
  281. package/public/assets/css/user-react.input.css +57 -0
  282. package/public/assets/images/brand-icon-192.png +0 -0
  283. package/public/assets/images/brand-logo-128.webp +0 -0
  284. package/public/assets/images/hero-banner-1280.jpg +0 -0
  285. package/public/comandos/commands-catalog.json +4517 -0
  286. package/public/css/api-docs.css +161 -0
  287. package/public/css/stickers-admin.css +1288 -0
  288. package/public/css/styles.css +679 -0
  289. package/public/css/systemadm/admin.css +474 -0
  290. package/public/css/systemadm/base.css +73 -0
  291. package/public/css/systemadm/components.css +662 -0
  292. package/public/css/systemadm/layout.css +229 -0
  293. package/public/css/systemadm/tokens.css +56 -0
  294. package/public/favicon-16x16.png +0 -0
  295. package/public/favicon-32x32.png +0 -0
  296. package/public/favicon.ico +0 -0
  297. package/public/js/apps/apiDocsApp.js +235 -0
  298. package/public/js/apps/commandsReactApp.js +528 -0
  299. package/public/js/apps/createPackApp.js +1646 -0
  300. package/public/js/apps/homeReactApp.js +942 -0
  301. package/public/js/apps/loginReactApp.js +496 -0
  302. package/public/js/apps/stickersAdminApp.js +1753 -0
  303. package/public/js/apps/stickersApp.js +3797 -0
  304. package/public/js/apps/termsReactApp.js +528 -0
  305. package/public/js/apps/userApp.js +2540 -0
  306. package/public/js/apps/userProfile/actions.js +66 -0
  307. package/public/js/apps/userReactApp.js +547 -0
  308. package/public/js/catalog.js +950 -0
  309. package/public/pages/api-docs.html +40 -0
  310. package/public/pages/aup.html +158 -0
  311. package/public/pages/comandos.html +41 -0
  312. package/public/pages/dpa.html +227 -0
  313. package/public/pages/home.html +45 -0
  314. package/public/pages/licenca.html +182 -0
  315. package/public/pages/login.html +40 -0
  316. package/public/pages/notice-and-takedown.html +234 -0
  317. package/public/pages/politica-de-privacidade.html +251 -0
  318. package/public/pages/seo-bot-whatsapp-para-grupo.html +350 -0
  319. package/public/pages/seo-bot-whatsapp-sem-programar.html +350 -0
  320. package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +350 -0
  321. package/public/pages/seo-como-criar-comandos-whatsapp.html +350 -0
  322. package/public/pages/seo-como-evitar-spam-no-whatsapp.html +350 -0
  323. package/public/pages/seo-como-moderar-grupo-whatsapp.html +350 -0
  324. package/public/pages/seo-como-organizar-comunidade-whatsapp.html +350 -0
  325. package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +350 -0
  326. package/public/pages/stickers-admin.html +31 -0
  327. package/public/pages/stickers-create.html +41 -0
  328. package/public/pages/stickers.html +45 -0
  329. package/public/pages/suboperadores.html +237 -0
  330. package/public/pages/termos-de-uso-texto-integral.html +241 -0
  331. package/public/pages/termos-de-uso.html +41 -0
  332. package/public/pages/user-password-reset.html +32 -0
  333. package/public/pages/user-systemadm.html +508 -0
  334. package/public/pages/user.html +39 -0
  335. package/public/robots.txt +9 -0
  336. package/public/site.webmanifest +24 -0
  337. package/public/sitemap.xml +98 -0
  338. package/schemas/command-config.schema.json +582 -0
  339. package/scripts/baileys-compat-smoke.mjs +12 -0
  340. package/scripts/cache-bust.mjs +142 -0
  341. package/scripts/deploy.sh +916 -0
  342. package/scripts/email-broadcast-terms-update.mjs +170 -0
  343. package/scripts/enrich-command-discovery-fields.mjs +286 -0
  344. package/scripts/generate-command-config-schema.mjs +273 -0
  345. package/scripts/generate-commands-catalog.mjs +308 -0
  346. package/scripts/generate-module-agents.mjs +631 -0
  347. package/scripts/generate-seo-satellite-pages.mjs +400 -0
  348. package/scripts/github-deploy-notify.mjs +174 -0
  349. package/scripts/github-release-notify.mjs +219 -0
  350. package/scripts/release.sh +599 -0
  351. package/scripts/run-codeql-local.sh +116 -0
  352. package/scripts/run-prettier-all.mjs +25 -0
  353. package/scripts/security-smoketest.mjs +581 -0
  354. package/scripts/sticker-catalog-loadtest.mjs +210 -0
  355. package/scripts/sticker-worker-task.mjs +119 -0
  356. package/scripts/sync-readme-snapshot.mjs +133 -0
  357. package/scripts/validate-command-config-schema.mjs +130 -0
  358. package/scripts/validate-command-configs.mjs +15 -0
  359. package/scripts/wiki-sync.sh +191 -0
  360. package/server/auth/googleWebAuth/googleWebAuthRuntime.js +62 -0
  361. package/server/auth/googleWebAuth/googleWebAuthService.js +807 -0
  362. package/server/auth/jwt/webJwtService.js +147 -0
  363. package/server/auth/stickerCatalogAuthContext.js +165 -0
  364. package/server/auth/termsAcceptance/termsAcceptanceHandler.js +189 -0
  365. package/server/auth/userPassword/index.js +14 -0
  366. package/server/auth/userPassword/userPasswordAuthService.js +422 -0
  367. package/server/auth/userPassword/userPasswordCrypto.js +199 -0
  368. package/server/auth/userPassword/userPasswordCrypto.test.js +76 -0
  369. package/server/auth/userPassword/userPasswordRecoveryService.js +728 -0
  370. package/server/auth/validation/authSchemas.js +236 -0
  371. package/server/auth/webAccount/webAccountHandlers.js +1434 -0
  372. package/server/controllers/admin/adminBanService.js +138 -0
  373. package/server/controllers/admin/adminPanelHandlers.js +2083 -0
  374. package/server/controllers/admin/stickerCatalogAdminContext.js +17 -0
  375. package/server/controllers/admin/systemAdminController.js +201 -0
  376. package/server/controllers/email/emailAutomationController.js +239 -0
  377. package/server/controllers/metricsController.js +21 -0
  378. package/server/controllers/seo/stickerCatalogSeoContext.js +514 -0
  379. package/server/controllers/sticker/nonCatalogHandlers.js +303 -0
  380. package/server/controllers/sticker/stickerCatalogController.js +4700 -0
  381. package/server/controllers/system/contactController.js +115 -0
  382. package/server/controllers/system/githubController.js +137 -0
  383. package/server/controllers/system/stickerCatalogSystemContext.js +758 -0
  384. package/server/controllers/system/storageController.js +154 -0
  385. package/server/controllers/system/systemController.js +135 -0
  386. package/server/controllers/system/systemMetricsController.js +156 -0
  387. package/server/controllers/system/visitController.js +90 -0
  388. package/server/controllers/userController.js +145 -0
  389. package/server/email/emailAutomationRuntime.js +225 -0
  390. package/server/email/emailAutomationService.js +125 -0
  391. package/server/email/emailOutboxRepository.js +282 -0
  392. package/server/email/emailTemplateService.js +480 -0
  393. package/server/email/emailTransportService.js +156 -0
  394. package/server/http/clientIp.js +95 -0
  395. package/server/http/httpRequestUtils.js +262 -0
  396. package/server/http/httpRequestUtils.test.js +80 -0
  397. package/server/http/httpServer.js +180 -0
  398. package/server/http/requestContext.js +20 -0
  399. package/server/http/siteRoutingUtils.js +87 -0
  400. package/server/index.js +1 -0
  401. package/server/middleware/cachePolicy.js +26 -0
  402. package/server/middleware/cachePolicyHelpers.js +1 -0
  403. package/server/middleware/endpointRateLimit.js +181 -0
  404. package/server/middleware/rateLimit.js +70 -0
  405. package/server/middleware/requireAdminAuth.js +48 -0
  406. package/server/middleware/securityHeaders.js +97 -0
  407. package/server/routes/admin/systemAdminRouter.js +64 -0
  408. package/server/routes/email/emailAutomationRouter.js +46 -0
  409. package/server/routes/health/healthRouter.js +41 -0
  410. package/server/routes/indexRouter.js +234 -0
  411. package/server/routes/metrics/metricsRouter.js +58 -0
  412. package/server/routes/static/staticPageRouter.js +134 -0
  413. package/server/routes/sticker/catalogHandlers/catalogAdminHttp.js +105 -0
  414. package/server/routes/sticker/catalogHandlers/catalogAuthHttp.js +77 -0
  415. package/server/routes/sticker/catalogHandlers/catalogPublicHttp.js +120 -0
  416. package/server/routes/sticker/catalogHandlers/catalogUploadHttp.js +83 -0
  417. package/server/routes/sticker/catalogRouter.js +77 -0
  418. package/server/routes/sticker/stickerApiRouter.js +84 -0
  419. package/server/routes/sticker/stickerDataRouter.js +145 -0
  420. package/server/routes/sticker/stickerSiteRouter.js +43 -0
  421. package/server/routes/user/userApiPaths.js +66 -0
  422. package/server/routes/user/userRouter.js +65 -0
  423. package/server/utils/safePath.js +26 -0
  424. package/utils/logger/loggerModule.js +35 -0
  425. package/vite.config.mjs +38 -0
@@ -0,0 +1,605 @@
1
+ import OpenAI from 'openai';
2
+ import logger from '#logger';
3
+ import { insertLearnedKeywords, insertLearnedPatterns, listPendingLearningEvents, markLearningEventsProcessed } from '../services/ai/aiLearningRepository.js';
4
+ import { getAllToolRecords, getToolRegistryStats } from '../services/ai/moduleToolRegistryService.js';
5
+ import { markToolCandidateLearningCacheDirty } from '../services/ai/toolCandidateSelectorService.js';
6
+
7
+ const DEFAULT_INTERVAL_MS = 10 * 60 * 1000;
8
+ const DEFAULT_BATCH_SIZE = 50;
9
+ const DEFAULT_TIMEOUT_MS = 20_000;
10
+ const DEFAULT_MODEL = 'gpt-4o-mini';
11
+ const DEFAULT_MAX_PATTERNS_PER_EVENT = 5;
12
+ const DEFAULT_MAX_KEYWORDS_PER_EVENT = 12;
13
+ const DEFAULT_CONFIG_SEED_ENABLED = true;
14
+ const DEFAULT_CONFIG_SEED_INTERVAL_MS = 6 * 60 * 60 * 1000;
15
+ const DEFAULT_CONFIG_SEED_MAX_PATTERNS_PER_TOOL = 24;
16
+ const DEFAULT_CONFIG_SEED_MAX_KEYWORDS_PER_TOOL = 40;
17
+ const CONFIG_SEED_EVENT_ID_BASE = 9_000_000_000;
18
+
19
+ const parseEnvBool = (value, fallback) => {
20
+ if (value === undefined || value === null || value === '') return fallback;
21
+ const normalized = String(value).trim().toLowerCase();
22
+ if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;
23
+ if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;
24
+ return fallback;
25
+ };
26
+
27
+ const parseEnvInt = (value, fallback, min, max) => {
28
+ const parsed = Number.parseInt(String(value ?? ''), 10);
29
+ if (!Number.isFinite(parsed)) return fallback;
30
+ return Math.max(min, Math.min(max, parsed));
31
+ };
32
+
33
+ const AI_LEARNING_WORKER_ENABLED = parseEnvBool(process.env.AI_LEARNING_WORKER_ENABLED, true);
34
+ const AI_LEARNING_WORKER_INTERVAL_MS = parseEnvInt(process.env.AI_LEARNING_WORKER_INTERVAL_MS, DEFAULT_INTERVAL_MS, 60_000, 24 * 60 * 60 * 1000);
35
+ const AI_LEARNING_WORKER_BATCH_SIZE = parseEnvInt(process.env.AI_LEARNING_WORKER_BATCH_SIZE, DEFAULT_BATCH_SIZE, 1, 200);
36
+ const AI_LEARNING_WORKER_TIMEOUT_MS = parseEnvInt(process.env.AI_LEARNING_WORKER_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, 5_000, 60_000);
37
+ const AI_LEARNING_WORKER_MODEL = String(process.env.AI_LEARNING_WORKER_MODEL || DEFAULT_MODEL).trim() || DEFAULT_MODEL;
38
+ const AI_LEARNING_WORKER_MAX_PATTERNS = parseEnvInt(process.env.AI_LEARNING_WORKER_MAX_PATTERNS, DEFAULT_MAX_PATTERNS_PER_EVENT, 1, 12);
39
+ const AI_LEARNING_WORKER_MAX_KEYWORDS = parseEnvInt(process.env.AI_LEARNING_WORKER_MAX_KEYWORDS, DEFAULT_MAX_KEYWORDS_PER_EVENT, 3, 30);
40
+ const AI_LEARNING_WORKER_CONFIG_SEED_ENABLED = parseEnvBool(process.env.AI_LEARNING_WORKER_CONFIG_SEED_ENABLED, DEFAULT_CONFIG_SEED_ENABLED);
41
+ const AI_LEARNING_WORKER_CONFIG_SEED_INTERVAL_MS = parseEnvInt(process.env.AI_LEARNING_WORKER_CONFIG_SEED_INTERVAL_MS, DEFAULT_CONFIG_SEED_INTERVAL_MS, 5 * 60 * 1000, 7 * 24 * 60 * 60 * 1000);
42
+ const AI_LEARNING_WORKER_CONFIG_SEED_MAX_PATTERNS = parseEnvInt(process.env.AI_LEARNING_WORKER_CONFIG_SEED_MAX_PATTERNS, DEFAULT_CONFIG_SEED_MAX_PATTERNS_PER_TOOL, 4, 96);
43
+ const AI_LEARNING_WORKER_CONFIG_SEED_MAX_KEYWORDS = parseEnvInt(process.env.AI_LEARNING_WORKER_CONFIG_SEED_MAX_KEYWORDS, DEFAULT_CONFIG_SEED_MAX_KEYWORDS_PER_TOOL, 8, 140);
44
+
45
+ let schedulerHandle = null;
46
+ let schedulerStarted = false;
47
+ let cycleInProgress = false;
48
+ let cachedClient = null;
49
+ let lastConfigSeedSignature = '';
50
+ let nextConfigSeedAt = 0;
51
+
52
+ const hasOpenAiApiKey = () => Boolean(String(process.env.OPENAI_API_KEY || '').trim());
53
+
54
+ const normalizeText = (value) =>
55
+ String(value || '')
56
+ .trim()
57
+ .toLowerCase()
58
+ .normalize('NFD')
59
+ .replace(/[\u0300-\u036f]/g, '')
60
+ .replace(/[^a-z0-9\s/_.-]/g, ' ')
61
+ .replace(/\s+/g, ' ')
62
+ .trim();
63
+
64
+ const ensureArray = (value) => (Array.isArray(value) ? value : []);
65
+
66
+ const readCommandUsage = (entry = {}) => {
67
+ const usageV2 = ensureArray(entry?.usage);
68
+ if (usageV2.length) return usageV2;
69
+ const docsUsage = ensureArray(entry?.docs?.usage_examples);
70
+ if (docsUsage.length) return docsUsage;
71
+ return ensureArray(entry?.metodos_de_uso);
72
+ };
73
+
74
+ const readCommandDiscovery = (entry = {}) => {
75
+ if (!entry?.discovery || typeof entry.discovery !== 'object' || Array.isArray(entry.discovery)) {
76
+ return {};
77
+ }
78
+ return entry.discovery;
79
+ };
80
+
81
+ const readCommandFaqPatterns = (entry = {}) => {
82
+ const discovery = readCommandDiscovery(entry);
83
+ const source = ensureArray(discovery.faq_queries).length ? discovery.faq_queries : entry?.faq_patterns;
84
+ return ensureArray(source);
85
+ };
86
+
87
+ const readCommandUserPhrasings = (entry = {}) => {
88
+ const discovery = readCommandDiscovery(entry);
89
+ const source = ensureArray(discovery.user_phrasings).length ? discovery.user_phrasings : entry?.user_phrasings;
90
+ return ensureArray(source);
91
+ };
92
+
93
+ const readCommandKeywords = (entry = {}) => {
94
+ const discovery = readCommandDiscovery(entry);
95
+ const source = ensureArray(discovery.keywords).length ? discovery.keywords : entry?.capability_keywords;
96
+ return ensureArray(source);
97
+ };
98
+
99
+ const readCommandCategory = (entry = {}) => String(entry?.category || entry?.categoria || '').trim();
100
+
101
+ const readCommandContexts = (entry = {}) => {
102
+ const contextsV2 = ensureArray(entry?.contexts);
103
+ if (contextsV2.length) return contextsV2;
104
+ return ensureArray(entry?.local_de_uso);
105
+ };
106
+
107
+ const normalizeCommandToken = (value) => normalizeText(value).replace(/\s+/g, '').slice(0, 64);
108
+
109
+ const uniqueList = (items = []) =>
110
+ Array.from(new Set((Array.isArray(items) ? items : []).map((item) => normalizeText(item))))
111
+ .filter(Boolean)
112
+ .map((item) => item.trim());
113
+
114
+ const uniqueTokenList = (items = []) => {
115
+ const tokens = [];
116
+ for (const value of Array.isArray(items) ? items : []) {
117
+ const normalized = normalizeText(value);
118
+ if (!normalized) continue;
119
+ for (const token of normalized.split(/\s+/)) {
120
+ if (!token || token.length < 3) continue;
121
+ if (!tokens.includes(token)) tokens.push(token);
122
+ }
123
+ }
124
+ return tokens;
125
+ };
126
+
127
+ const sanitizePattern = (value) => {
128
+ const normalized = String(value || '')
129
+ .trim()
130
+ .replace(/\s+/g, ' ')
131
+ .slice(0, 512);
132
+ return normalized || null;
133
+ };
134
+
135
+ const sanitizeKeyword = (value) => {
136
+ const normalized = normalizeText(value).slice(0, 128);
137
+ return normalized || null;
138
+ };
139
+
140
+ const clamp01 = (value) => Math.max(0, Math.min(1, Number(value) || 0));
141
+
142
+ const hashToolNameToUint32 = (value) => {
143
+ const input = String(value || '');
144
+ let hash = 2166136261;
145
+ for (let index = 0; index < input.length; index += 1) {
146
+ hash ^= input.charCodeAt(index);
147
+ hash = Math.imul(hash, 16777619) >>> 0;
148
+ }
149
+ return hash >>> 0;
150
+ };
151
+
152
+ const toConfigSeedEventId = (toolName) => CONFIG_SEED_EVENT_ID_BASE + hashToolNameToUint32(toolName);
153
+
154
+ const extractTextFromAssistantMessage = (message = {}) => {
155
+ if (typeof message?.content === 'string') return message.content.trim();
156
+ if (!Array.isArray(message?.content)) return '';
157
+
158
+ const parts = [];
159
+ for (const chunk of message.content) {
160
+ if (!chunk || typeof chunk !== 'object') continue;
161
+ if (typeof chunk.text === 'string') {
162
+ parts.push(chunk.text.trim());
163
+ continue;
164
+ }
165
+ if (chunk.type === 'text' && typeof chunk?.text?.value === 'string') {
166
+ parts.push(chunk.text.value.trim());
167
+ }
168
+ }
169
+
170
+ return parts.filter(Boolean).join('\n').trim();
171
+ };
172
+
173
+ const parseJsonSafe = (value) => {
174
+ if (!value) return null;
175
+ if (typeof value === 'object') return value;
176
+ try {
177
+ return JSON.parse(String(value));
178
+ } catch {
179
+ return null;
180
+ }
181
+ };
182
+
183
+ const getOpenAIClient = () => {
184
+ if (!cachedClient) {
185
+ cachedClient = new OpenAI({
186
+ apiKey: process.env.OPENAI_API_KEY,
187
+ timeout: AI_LEARNING_WORKER_TIMEOUT_MS,
188
+ maxRetries: 0,
189
+ });
190
+ }
191
+ return cachedClient;
192
+ };
193
+
194
+ const isWorkerReady = () => {
195
+ if (!AI_LEARNING_WORKER_ENABLED) return false;
196
+ if (AI_LEARNING_WORKER_CONFIG_SEED_ENABLED) return true;
197
+ if (!hasOpenAiApiKey()) return false;
198
+ return true;
199
+ };
200
+
201
+ const buildWorkerSystemPrompt = () => ['Voce recebe um comando existente e a pergunta real do usuario.', 'Sua tarefa e minerar variacoes semanticas para aprendizado.', 'Nunca invente novos comandos, nomes de tools ou parametros de execucao.', 'Retorne SOMENTE JSON valido no formato:', '{"alternative_phrases":[],"semantic_keywords":[],"intent_description":"","possible_intents":[],"confidence":0.0}', 'alternative_phrases: frases curtas no idioma do usuario.', 'semantic_keywords: termos importantes (sem comandos).', 'possible_intents: intencoes curtas e genericas.', 'confidence: numero de 0.0 a 1.0 para qualidade do aprendizado.'].join(' ');
202
+
203
+ const generateLearningArtifacts = async (event) => {
204
+ const client = getOpenAIClient();
205
+ const completion = await client.chat.completions.create({
206
+ model: AI_LEARNING_WORKER_MODEL,
207
+ temperature: 0.2,
208
+ response_format: { type: 'json_object' },
209
+ messages: [
210
+ {
211
+ role: 'system',
212
+ content: buildWorkerSystemPrompt(),
213
+ },
214
+ {
215
+ role: 'user',
216
+ content: [`Command: ${event.tool_executed}`, `User question: "${event.user_question}"`, `Normalized question: "${event.normalized_question}"`, '', `Generate up to ${AI_LEARNING_WORKER_MAX_PATTERNS} alternative user phrases,`, `up to ${AI_LEARNING_WORKER_MAX_KEYWORDS} semantic keywords,`, 'and a short intent description.'].join('\n'),
217
+ },
218
+ ],
219
+ });
220
+
221
+ const message = completion?.choices?.[0]?.message || {};
222
+ const rawJson = extractTextFromAssistantMessage(message);
223
+ const parsed = parseJsonSafe(rawJson) || {};
224
+
225
+ const rawPatterns = uniqueList(parsed?.alternative_phrases || []).slice(0, AI_LEARNING_WORKER_MAX_PATTERNS);
226
+ const rawKeywords = uniqueList(parsed?.semantic_keywords || []).slice(0, AI_LEARNING_WORKER_MAX_KEYWORDS);
227
+
228
+ const patterns = rawPatterns.map((value) => sanitizePattern(value)).filter(Boolean);
229
+ const keywords = rawKeywords.map((value) => sanitizeKeyword(value)).filter(Boolean);
230
+ const possibleIntents = uniqueList(parsed?.possible_intents || []).slice(0, 6);
231
+ const intentDescription = sanitizePattern(parsed?.intent_description || '');
232
+ const confidence = clamp01(parsed?.confidence);
233
+
234
+ return {
235
+ patterns,
236
+ keywords,
237
+ possibleIntents,
238
+ intentDescription,
239
+ confidence,
240
+ rawJson: rawJson.slice(0, 2000),
241
+ };
242
+ };
243
+
244
+ const buildConfigSeedRowsFromRecord = (record = {}) => {
245
+ const commandEntry = record?.commandEntry && typeof record.commandEntry === 'object' ? record.commandEntry : {};
246
+ const toolName = normalizeCommandToken(record?.toolName);
247
+ const commandName = normalizeCommandToken(record?.commandName || commandEntry?.name);
248
+ if (!toolName || !commandName) {
249
+ return {
250
+ patternRows: [],
251
+ keywordRows: [],
252
+ };
253
+ }
254
+
255
+ const aliases = ensureArray(commandEntry?.aliases)
256
+ .map((alias) => normalizeCommandToken(alias))
257
+ .filter(Boolean);
258
+ const usageHints = readCommandUsage(commandEntry);
259
+ const faqPatterns = readCommandFaqPatterns(commandEntry);
260
+ const userPhrasings = readCommandUserPhrasings(commandEntry);
261
+
262
+ const seedPatternCandidates = uniqueList([...faqPatterns, ...userPhrasings, ...usageHints, `como usar ${commandName}`, `o que faz ${commandName}`, `comando ${commandName}`, ...aliases.map((alias) => `comando ${alias}`), ...aliases.map((alias) => `como usar ${alias}`)]).slice(0, AI_LEARNING_WORKER_CONFIG_SEED_MAX_PATTERNS);
263
+
264
+ const category = readCommandCategory(commandEntry);
265
+ const contexts = readCommandContexts(commandEntry);
266
+ const keywordTokens = uniqueTokenList([...readCommandKeywords(commandEntry), ...faqPatterns, ...userPhrasings, ...usageHints, commandName, ...aliases, category, ...contexts]).slice(0, AI_LEARNING_WORKER_CONFIG_SEED_MAX_KEYWORDS);
267
+
268
+ const sourceEventId = toConfigSeedEventId(toolName);
269
+ const patternRows = seedPatternCandidates
270
+ .map((pattern) => sanitizePattern(pattern))
271
+ .filter(Boolean)
272
+ .map((pattern) => ({
273
+ pattern,
274
+ tool: toolName,
275
+ confidence: 0.62,
276
+ sourceEventId,
277
+ }));
278
+
279
+ const keywordRows = keywordTokens
280
+ .map((keyword) => sanitizeKeyword(keyword))
281
+ .filter(Boolean)
282
+ .map((keyword) => ({
283
+ keyword,
284
+ tool: toolName,
285
+ weight: 0.68,
286
+ sourceEventId,
287
+ }));
288
+
289
+ return {
290
+ patternRows,
291
+ keywordRows,
292
+ };
293
+ };
294
+
295
+ const seedLearningFromCommandConfig = async ({ reason = 'scheduler' } = {}) => {
296
+ if (!AI_LEARNING_WORKER_CONFIG_SEED_ENABLED) {
297
+ return {
298
+ executed: false,
299
+ skipped: 'disabled',
300
+ insertedPatterns: 0,
301
+ insertedKeywords: 0,
302
+ toolCount: 0,
303
+ candidatePatterns: 0,
304
+ candidateKeywords: 0,
305
+ };
306
+ }
307
+
308
+ const nowMs = Date.now();
309
+ const registryStats = getToolRegistryStats();
310
+ const registrySignature = String(registryStats?.signature || '');
311
+
312
+ if (registrySignature && registrySignature === lastConfigSeedSignature && nextConfigSeedAt > nowMs) {
313
+ return {
314
+ executed: false,
315
+ skipped: 'cooldown',
316
+ insertedPatterns: 0,
317
+ insertedKeywords: 0,
318
+ toolCount: Number(registryStats?.toolCount || 0),
319
+ candidatePatterns: 0,
320
+ candidateKeywords: 0,
321
+ nextRunAt: new Date(nextConfigSeedAt).toISOString(),
322
+ };
323
+ }
324
+
325
+ const records = getAllToolRecords();
326
+ if (!records.length) {
327
+ lastConfigSeedSignature = registrySignature;
328
+ nextConfigSeedAt = nowMs + AI_LEARNING_WORKER_CONFIG_SEED_INTERVAL_MS;
329
+ return {
330
+ executed: false,
331
+ skipped: 'empty_registry',
332
+ insertedPatterns: 0,
333
+ insertedKeywords: 0,
334
+ toolCount: 0,
335
+ candidatePatterns: 0,
336
+ candidateKeywords: 0,
337
+ };
338
+ }
339
+
340
+ const allPatternRows = [];
341
+ const allKeywordRows = [];
342
+ for (const record of records) {
343
+ const rows = buildConfigSeedRowsFromRecord(record);
344
+ allPatternRows.push(...rows.patternRows);
345
+ allKeywordRows.push(...rows.keywordRows);
346
+ }
347
+
348
+ const insertedPatterns = await insertLearnedPatterns(allPatternRows);
349
+ const insertedKeywords = await insertLearnedKeywords(allKeywordRows);
350
+
351
+ if (insertedPatterns > 0 || insertedKeywords > 0) {
352
+ markToolCandidateLearningCacheDirty();
353
+ }
354
+
355
+ lastConfigSeedSignature = registrySignature;
356
+ nextConfigSeedAt = nowMs + AI_LEARNING_WORKER_CONFIG_SEED_INTERVAL_MS;
357
+
358
+ logger.info('Seed de aprendizado IA por commandConfig processado.', {
359
+ action: 'ai_learning_config_seed_processed',
360
+ reason,
361
+ registry_signature: registrySignature || null,
362
+ tool_count: records.length,
363
+ candidate_patterns: allPatternRows.length,
364
+ candidate_keywords: allKeywordRows.length,
365
+ inserted_patterns: insertedPatterns,
366
+ inserted_keywords: insertedKeywords,
367
+ next_run_at: new Date(nextConfigSeedAt).toISOString(),
368
+ });
369
+
370
+ return {
371
+ executed: true,
372
+ skipped: null,
373
+ insertedPatterns,
374
+ insertedKeywords,
375
+ toolCount: records.length,
376
+ candidatePatterns: allPatternRows.length,
377
+ candidateKeywords: allKeywordRows.length,
378
+ nextRunAt: new Date(nextConfigSeedAt).toISOString(),
379
+ };
380
+ };
381
+
382
+ const processLearningBatch = async ({ reason = 'scheduler' } = {}) => {
383
+ if (cycleInProgress) return;
384
+ if (!isWorkerReady()) return;
385
+
386
+ cycleInProgress = true;
387
+ try {
388
+ const startedAt = Date.now();
389
+ logger.info('Worker de aprendizado IA iniciado.', {
390
+ action: 'ai_learning_worker_started',
391
+ reason,
392
+ model: AI_LEARNING_WORKER_MODEL,
393
+ batch_size: AI_LEARNING_WORKER_BATCH_SIZE,
394
+ });
395
+
396
+ let events = [];
397
+ try {
398
+ events = await listPendingLearningEvents({ limit: AI_LEARNING_WORKER_BATCH_SIZE });
399
+ } catch (error) {
400
+ logger.warn('Falha ao buscar eventos pendentes de aprendizado.', {
401
+ action: 'ai_learning_batch_fetch_failed',
402
+ error: error?.message,
403
+ });
404
+ return;
405
+ }
406
+
407
+ let configSeedResult = {
408
+ executed: false,
409
+ skipped: 'disabled',
410
+ insertedPatterns: 0,
411
+ insertedKeywords: 0,
412
+ toolCount: 0,
413
+ };
414
+ try {
415
+ configSeedResult = await seedLearningFromCommandConfig({ reason });
416
+ } catch (error) {
417
+ logger.warn('Falha ao executar seed de aprendizado IA por commandConfig.', {
418
+ action: 'ai_learning_config_seed_failed',
419
+ reason,
420
+ error: error?.message,
421
+ });
422
+ }
423
+
424
+ if (!events.length) {
425
+ logger.info('Nenhum evento pendente para aprendizado IA.', {
426
+ action: 'ai_learning_batch_processed',
427
+ reason,
428
+ fetched_events: 0,
429
+ processed_events: 0,
430
+ generated_patterns: configSeedResult.insertedPatterns,
431
+ generated_keywords: configSeedResult.insertedKeywords,
432
+ config_seed_enabled: AI_LEARNING_WORKER_CONFIG_SEED_ENABLED,
433
+ config_seed_executed: configSeedResult.executed,
434
+ config_seed_skipped: configSeedResult.skipped,
435
+ config_seed_tool_count: configSeedResult.toolCount,
436
+ duration_ms: Date.now() - startedAt,
437
+ });
438
+ return;
439
+ }
440
+
441
+ if (!hasOpenAiApiKey()) {
442
+ logger.warn('Eventos pendentes de aprendizado IA foram ignorados por ausencia de OPENAI_API_KEY.', {
443
+ action: 'ai_learning_pending_events_skipped_no_api_key',
444
+ reason,
445
+ pending_events: events.length,
446
+ });
447
+ logger.info('Batch de aprendizado IA processado com seed de commandConfig e sem processamento LLM.', {
448
+ action: 'ai_learning_batch_processed',
449
+ reason,
450
+ fetched_events: events.length,
451
+ processed_events: 0,
452
+ successful_events: 0,
453
+ generated_patterns: configSeedResult.insertedPatterns,
454
+ generated_keywords: configSeedResult.insertedKeywords,
455
+ skipped_events_no_api_key: events.length,
456
+ config_seed_enabled: AI_LEARNING_WORKER_CONFIG_SEED_ENABLED,
457
+ config_seed_executed: configSeedResult.executed,
458
+ config_seed_skipped: configSeedResult.skipped,
459
+ config_seed_tool_count: configSeedResult.toolCount,
460
+ duration_ms: Date.now() - startedAt,
461
+ });
462
+ return;
463
+ }
464
+
465
+ const processedEventIds = [];
466
+ let generatedPatterns = 0;
467
+ let generatedKeywords = 0;
468
+ let successfulEvents = 0;
469
+
470
+ for (const event of events) {
471
+ try {
472
+ const artifacts = await generateLearningArtifacts(event);
473
+
474
+ const patternRows = artifacts.patterns.map((pattern) => ({
475
+ pattern,
476
+ tool: event.tool_executed,
477
+ confidence: artifacts.confidence || event.confidence || 0.5,
478
+ sourceEventId: event.id,
479
+ }));
480
+ const keywordRows = artifacts.keywords.map((keyword) => ({
481
+ keyword,
482
+ tool: event.tool_executed,
483
+ weight: artifacts.confidence || event.confidence || 0.7,
484
+ sourceEventId: event.id,
485
+ }));
486
+
487
+ const insertedPatterns = await insertLearnedPatterns(patternRows);
488
+ const insertedKeywords = await insertLearnedKeywords(keywordRows);
489
+
490
+ generatedPatterns += insertedPatterns;
491
+ generatedKeywords += insertedKeywords;
492
+ successfulEvents += 1;
493
+ processedEventIds.push(event.id);
494
+
495
+ logger.info('Padroes de aprendizado IA gerados.', {
496
+ action: 'ai_learning_patterns_generated',
497
+ source_event_id: event.id,
498
+ tool: event.tool_executed,
499
+ generated_count: insertedPatterns,
500
+ intent_description: artifacts.intentDescription || null,
501
+ possible_intents: artifacts.possibleIntents,
502
+ });
503
+ logger.info('Keywords de aprendizado IA geradas.', {
504
+ action: 'ai_learning_keywords_generated',
505
+ source_event_id: event.id,
506
+ tool: event.tool_executed,
507
+ generated_count: insertedKeywords,
508
+ });
509
+ } catch (error) {
510
+ logger.warn('Falha ao gerar aprendizado IA para evento.', {
511
+ action: 'ai_learning_event_process_failed',
512
+ source_event_id: event?.id || null,
513
+ tool: event?.tool_executed || null,
514
+ error: error?.message,
515
+ });
516
+ }
517
+ }
518
+
519
+ if (processedEventIds.length > 0) {
520
+ await markLearningEventsProcessed(processedEventIds);
521
+ markToolCandidateLearningCacheDirty();
522
+ }
523
+
524
+ logger.info('Batch de aprendizado IA processado.', {
525
+ action: 'ai_learning_batch_processed',
526
+ reason,
527
+ fetched_events: events.length,
528
+ processed_events: processedEventIds.length,
529
+ successful_events: successfulEvents,
530
+ generated_patterns: generatedPatterns + configSeedResult.insertedPatterns,
531
+ generated_keywords: generatedKeywords + configSeedResult.insertedKeywords,
532
+ generated_patterns_from_events: generatedPatterns,
533
+ generated_keywords_from_events: generatedKeywords,
534
+ generated_patterns_from_config_seed: configSeedResult.insertedPatterns,
535
+ generated_keywords_from_config_seed: configSeedResult.insertedKeywords,
536
+ config_seed_enabled: AI_LEARNING_WORKER_CONFIG_SEED_ENABLED,
537
+ config_seed_executed: configSeedResult.executed,
538
+ config_seed_skipped: configSeedResult.skipped,
539
+ config_seed_tool_count: configSeedResult.toolCount,
540
+ duration_ms: Date.now() - startedAt,
541
+ });
542
+ } finally {
543
+ cycleInProgress = false;
544
+ }
545
+ };
546
+
547
+ export const startAiLearningWorker = () => {
548
+ if (schedulerStarted) return;
549
+
550
+ if (!isWorkerReady()) {
551
+ logger.info('Worker de aprendizado IA desativado ou sem condicao minima de execucao.', {
552
+ action: 'ai_learning_worker_disabled',
553
+ enabled: AI_LEARNING_WORKER_ENABLED,
554
+ has_openai_api_key: hasOpenAiApiKey(),
555
+ config_seed_enabled: AI_LEARNING_WORKER_CONFIG_SEED_ENABLED,
556
+ });
557
+ return;
558
+ }
559
+
560
+ schedulerStarted = true;
561
+ void processLearningBatch({ reason: 'startup' });
562
+
563
+ schedulerHandle = setInterval(() => {
564
+ void processLearningBatch({ reason: 'scheduler' });
565
+ }, AI_LEARNING_WORKER_INTERVAL_MS);
566
+ if (typeof schedulerHandle?.unref === 'function') {
567
+ schedulerHandle.unref();
568
+ }
569
+
570
+ logger.info('Scheduler do worker de aprendizado IA iniciado.', {
571
+ action: 'ai_learning_worker_scheduler_started',
572
+ interval_ms: AI_LEARNING_WORKER_INTERVAL_MS,
573
+ batch_size: AI_LEARNING_WORKER_BATCH_SIZE,
574
+ model: AI_LEARNING_WORKER_MODEL,
575
+ config_seed_enabled: AI_LEARNING_WORKER_CONFIG_SEED_ENABLED,
576
+ config_seed_interval_ms: AI_LEARNING_WORKER_CONFIG_SEED_INTERVAL_MS,
577
+ });
578
+ };
579
+
580
+ export const stopAiLearningWorker = () => {
581
+ if (schedulerHandle) {
582
+ clearInterval(schedulerHandle);
583
+ schedulerHandle = null;
584
+ }
585
+ schedulerStarted = false;
586
+ };
587
+
588
+ export const runAiLearningWorkerOnce = async (reason = 'manual') => {
589
+ await processLearningBatch({ reason });
590
+ };
591
+
592
+ export const getAiLearningWorkerConfig = () => ({
593
+ enabled: AI_LEARNING_WORKER_ENABLED,
594
+ intervalMs: AI_LEARNING_WORKER_INTERVAL_MS,
595
+ batchSize: AI_LEARNING_WORKER_BATCH_SIZE,
596
+ model: AI_LEARNING_WORKER_MODEL,
597
+ timeoutMs: AI_LEARNING_WORKER_TIMEOUT_MS,
598
+ maxPatternsPerEvent: AI_LEARNING_WORKER_MAX_PATTERNS,
599
+ maxKeywordsPerEvent: AI_LEARNING_WORKER_MAX_KEYWORDS,
600
+ configSeedEnabled: AI_LEARNING_WORKER_CONFIG_SEED_ENABLED,
601
+ configSeedIntervalMs: AI_LEARNING_WORKER_CONFIG_SEED_INTERVAL_MS,
602
+ configSeedMaxPatternsPerTool: AI_LEARNING_WORKER_CONFIG_SEED_MAX_PATTERNS,
603
+ configSeedMaxKeywordsPerTool: AI_LEARNING_WORKER_CONFIG_SEED_MAX_KEYWORDS,
604
+ hasApiKey: Boolean(process.env.OPENAI_API_KEY),
605
+ });