raxe 0.4.6__py3-none-any.whl

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 (668) hide show
  1. raxe/__init__.py +101 -0
  2. raxe/application/__init__.py +48 -0
  3. raxe/application/ab_testing.py +170 -0
  4. raxe/application/analytics/__init__.py +30 -0
  5. raxe/application/analytics/achievement_service.py +444 -0
  6. raxe/application/analytics/repositories.py +172 -0
  7. raxe/application/analytics/retention_service.py +267 -0
  8. raxe/application/analytics/statistics_service.py +419 -0
  9. raxe/application/analytics/streak_service.py +283 -0
  10. raxe/application/apply_policy.py +291 -0
  11. raxe/application/eager_l2.py +503 -0
  12. raxe/application/preloader.py +353 -0
  13. raxe/application/scan_merger.py +321 -0
  14. raxe/application/scan_pipeline.py +1059 -0
  15. raxe/application/scan_pipeline_async.py +403 -0
  16. raxe/application/session_tracker.py +458 -0
  17. raxe/application/telemetry_manager.py +357 -0
  18. raxe/application/telemetry_orchestrator.py +1210 -0
  19. raxe/async_sdk/__init__.py +34 -0
  20. raxe/async_sdk/cache.py +286 -0
  21. raxe/async_sdk/client.py +556 -0
  22. raxe/async_sdk/wrappers/__init__.py +23 -0
  23. raxe/async_sdk/wrappers/openai.py +238 -0
  24. raxe/cli/__init__.py +21 -0
  25. raxe/cli/auth.py +1047 -0
  26. raxe/cli/branding.py +235 -0
  27. raxe/cli/config.py +334 -0
  28. raxe/cli/custom_rules.py +458 -0
  29. raxe/cli/doctor.py +686 -0
  30. raxe/cli/error_handler.py +665 -0
  31. raxe/cli/event.py +648 -0
  32. raxe/cli/exit_codes.py +57 -0
  33. raxe/cli/expiry_warning.py +302 -0
  34. raxe/cli/export.py +183 -0
  35. raxe/cli/history.py +247 -0
  36. raxe/cli/l2_formatter.py +872 -0
  37. raxe/cli/main.py +1137 -0
  38. raxe/cli/models.py +590 -0
  39. raxe/cli/output.py +403 -0
  40. raxe/cli/privacy.py +84 -0
  41. raxe/cli/profiler.py +262 -0
  42. raxe/cli/progress.py +379 -0
  43. raxe/cli/progress_context.py +101 -0
  44. raxe/cli/repl.py +394 -0
  45. raxe/cli/rules.py +542 -0
  46. raxe/cli/setup_wizard.py +721 -0
  47. raxe/cli/stats.py +292 -0
  48. raxe/cli/suppress.py +501 -0
  49. raxe/cli/telemetry.py +1384 -0
  50. raxe/cli/test.py +130 -0
  51. raxe/cli/tune.py +315 -0
  52. raxe/cli/validate.py +218 -0
  53. raxe/domain/__init__.py +30 -0
  54. raxe/domain/analytics/__init__.py +97 -0
  55. raxe/domain/analytics/achievements.py +306 -0
  56. raxe/domain/analytics/models.py +120 -0
  57. raxe/domain/analytics/retention.py +168 -0
  58. raxe/domain/analytics/statistics.py +207 -0
  59. raxe/domain/analytics/streaks.py +173 -0
  60. raxe/domain/engine/__init__.py +15 -0
  61. raxe/domain/engine/executor.py +396 -0
  62. raxe/domain/engine/matcher.py +212 -0
  63. raxe/domain/inline_suppression.py +176 -0
  64. raxe/domain/ml/__init__.py +133 -0
  65. raxe/domain/ml/embedding_cache.py +309 -0
  66. raxe/domain/ml/gemma_detector.py +921 -0
  67. raxe/domain/ml/gemma_models.py +346 -0
  68. raxe/domain/ml/l2_config.py +428 -0
  69. raxe/domain/ml/l2_output_schema.py +443 -0
  70. raxe/domain/ml/manifest_loader.py +309 -0
  71. raxe/domain/ml/manifest_schema.py +345 -0
  72. raxe/domain/ml/model_metadata.py +263 -0
  73. raxe/domain/ml/model_registry.py +786 -0
  74. raxe/domain/ml/protocol.py +282 -0
  75. raxe/domain/ml/scoring_models.py +419 -0
  76. raxe/domain/ml/stub_detector.py +397 -0
  77. raxe/domain/ml/threat_scorer.py +757 -0
  78. raxe/domain/ml/tokenizer_registry.py +372 -0
  79. raxe/domain/ml/voting/__init__.py +89 -0
  80. raxe/domain/ml/voting/config.py +595 -0
  81. raxe/domain/ml/voting/engine.py +465 -0
  82. raxe/domain/ml/voting/head_voters.py +378 -0
  83. raxe/domain/ml/voting/models.py +222 -0
  84. raxe/domain/models.py +82 -0
  85. raxe/domain/packs/__init__.py +17 -0
  86. raxe/domain/packs/models.py +304 -0
  87. raxe/domain/policies/__init__.py +20 -0
  88. raxe/domain/policies/evaluator.py +212 -0
  89. raxe/domain/policies/models.py +223 -0
  90. raxe/domain/rules/__init__.py +32 -0
  91. raxe/domain/rules/custom.py +286 -0
  92. raxe/domain/rules/models.py +273 -0
  93. raxe/domain/rules/schema.py +166 -0
  94. raxe/domain/rules/validator.py +556 -0
  95. raxe/domain/suppression.py +801 -0
  96. raxe/domain/suppression_factory.py +174 -0
  97. raxe/domain/telemetry/__init__.py +116 -0
  98. raxe/domain/telemetry/backpressure.py +424 -0
  99. raxe/domain/telemetry/event_creator.py +362 -0
  100. raxe/domain/telemetry/events.py +1282 -0
  101. raxe/domain/telemetry/priority.py +263 -0
  102. raxe/domain/telemetry/scan_telemetry_builder.py +670 -0
  103. raxe/infrastructure/__init__.py +25 -0
  104. raxe/infrastructure/analytics/__init__.py +18 -0
  105. raxe/infrastructure/analytics/aggregator.py +484 -0
  106. raxe/infrastructure/analytics/aggregator_optimized.py +184 -0
  107. raxe/infrastructure/analytics/engine.py +748 -0
  108. raxe/infrastructure/analytics/repository.py +409 -0
  109. raxe/infrastructure/analytics/streaks.py +467 -0
  110. raxe/infrastructure/analytics/views.py +178 -0
  111. raxe/infrastructure/cloud/__init__.py +9 -0
  112. raxe/infrastructure/config/__init__.py +56 -0
  113. raxe/infrastructure/config/endpoints.py +641 -0
  114. raxe/infrastructure/config/scan_config.py +352 -0
  115. raxe/infrastructure/config/yaml_config.py +459 -0
  116. raxe/infrastructure/database/__init__.py +10 -0
  117. raxe/infrastructure/database/connection.py +200 -0
  118. raxe/infrastructure/database/models.py +325 -0
  119. raxe/infrastructure/database/scan_history.py +764 -0
  120. raxe/infrastructure/ml/__init__.py +0 -0
  121. raxe/infrastructure/ml/download_progress.py +438 -0
  122. raxe/infrastructure/ml/model_downloader.py +457 -0
  123. raxe/infrastructure/models/__init__.py +16 -0
  124. raxe/infrastructure/models/discovery.py +461 -0
  125. raxe/infrastructure/packs/__init__.py +13 -0
  126. raxe/infrastructure/packs/loader.py +407 -0
  127. raxe/infrastructure/packs/registry.py +381 -0
  128. raxe/infrastructure/policies/__init__.py +16 -0
  129. raxe/infrastructure/policies/api_client.py +256 -0
  130. raxe/infrastructure/policies/validator.py +227 -0
  131. raxe/infrastructure/policies/yaml_loader.py +250 -0
  132. raxe/infrastructure/rules/__init__.py +18 -0
  133. raxe/infrastructure/rules/custom_loader.py +224 -0
  134. raxe/infrastructure/rules/versioning.py +222 -0
  135. raxe/infrastructure/rules/yaml_loader.py +286 -0
  136. raxe/infrastructure/security/__init__.py +31 -0
  137. raxe/infrastructure/security/auth.py +145 -0
  138. raxe/infrastructure/security/policy_validator.py +124 -0
  139. raxe/infrastructure/security/signatures.py +171 -0
  140. raxe/infrastructure/suppression/__init__.py +36 -0
  141. raxe/infrastructure/suppression/composite_repository.py +154 -0
  142. raxe/infrastructure/suppression/sqlite_repository.py +231 -0
  143. raxe/infrastructure/suppression/yaml_composite_repository.py +156 -0
  144. raxe/infrastructure/suppression/yaml_repository.py +510 -0
  145. raxe/infrastructure/telemetry/__init__.py +79 -0
  146. raxe/infrastructure/telemetry/acquisition.py +179 -0
  147. raxe/infrastructure/telemetry/config.py +254 -0
  148. raxe/infrastructure/telemetry/credential_store.py +947 -0
  149. raxe/infrastructure/telemetry/dual_queue.py +1123 -0
  150. raxe/infrastructure/telemetry/flush_helper.py +343 -0
  151. raxe/infrastructure/telemetry/flush_scheduler.py +776 -0
  152. raxe/infrastructure/telemetry/health_client.py +394 -0
  153. raxe/infrastructure/telemetry/hook.py +347 -0
  154. raxe/infrastructure/telemetry/queue.py +520 -0
  155. raxe/infrastructure/telemetry/sender.py +476 -0
  156. raxe/infrastructure/tracking/__init__.py +13 -0
  157. raxe/infrastructure/tracking/usage.py +389 -0
  158. raxe/integrations/__init__.py +55 -0
  159. raxe/integrations/availability.py +143 -0
  160. raxe/integrations/registry.py +122 -0
  161. raxe/integrations/utils.py +135 -0
  162. raxe/mcp/__init__.py +62 -0
  163. raxe/mcp/cli.py +97 -0
  164. raxe/mcp/server.py +409 -0
  165. raxe/monitoring/__init__.py +51 -0
  166. raxe/monitoring/metrics.py +372 -0
  167. raxe/monitoring/profiler.py +388 -0
  168. raxe/monitoring/server.py +136 -0
  169. raxe/packs/core/v1.0.0/pack.yaml +1394 -0
  170. raxe/packs/core/v1.0.0/rules/PI/pi-001@1.0.0.yaml +49 -0
  171. raxe/packs/core/v1.0.0/rules/PI/pi-006@1.0.0.yaml +48 -0
  172. raxe/packs/core/v1.0.0/rules/PI/pi-014@1.0.0.yaml +54 -0
  173. raxe/packs/core/v1.0.0/rules/PI/pi-017@1.0.0.yaml +52 -0
  174. raxe/packs/core/v1.0.0/rules/PI/pi-022@1.0.0.yaml +67 -0
  175. raxe/packs/core/v1.0.0/rules/PI/pi-023@1.0.0.yaml +91 -0
  176. raxe/packs/core/v1.0.0/rules/PI/pi-024@1.0.0.yaml +80 -0
  177. raxe/packs/core/v1.0.0/rules/PI/pi-025@1.0.0.yaml +81 -0
  178. raxe/packs/core/v1.0.0/rules/PI/pi-026@1.0.0.yaml +50 -0
  179. raxe/packs/core/v1.0.0/rules/PI/pi-027@1.0.0.yaml +77 -0
  180. raxe/packs/core/v1.0.0/rules/PI/pi-028@1.0.0.yaml +52 -0
  181. raxe/packs/core/v1.0.0/rules/PI/pi-029@1.0.0.yaml +51 -0
  182. raxe/packs/core/v1.0.0/rules/PI/pi-030@1.0.0.yaml +55 -0
  183. raxe/packs/core/v1.0.0/rules/PI/pi-033@1.0.0.yaml +50 -0
  184. raxe/packs/core/v1.0.0/rules/PI/pi-034@1.0.0.yaml +50 -0
  185. raxe/packs/core/v1.0.0/rules/PI/pi-035@1.0.0.yaml +50 -0
  186. raxe/packs/core/v1.0.0/rules/PI/pi-046@1.0.0.yaml +50 -0
  187. raxe/packs/core/v1.0.0/rules/PI/pi-047@1.0.0.yaml +50 -0
  188. raxe/packs/core/v1.0.0/rules/PI/pi-048@1.0.0.yaml +50 -0
  189. raxe/packs/core/v1.0.0/rules/PI/pi-049@1.0.0.yaml +50 -0
  190. raxe/packs/core/v1.0.0/rules/PI/pi-050@1.0.0.yaml +50 -0
  191. raxe/packs/core/v1.0.0/rules/PI/pi-068@1.0.0.yaml +50 -0
  192. raxe/packs/core/v1.0.0/rules/PI/pi-078@1.0.0.yaml +50 -0
  193. raxe/packs/core/v1.0.0/rules/PI/pi-2001@1.0.0.yaml +35 -0
  194. raxe/packs/core/v1.0.0/rules/PI/pi-2004@1.0.0.yaml +39 -0
  195. raxe/packs/core/v1.0.0/rules/PI/pi-201@1.0.0.yaml +43 -0
  196. raxe/packs/core/v1.0.0/rules/PI/pi-202@1.0.0.yaml +47 -0
  197. raxe/packs/core/v1.0.0/rules/PI/pi-203@1.0.0.yaml +46 -0
  198. raxe/packs/core/v1.0.0/rules/PI/pi-3007@1.0.0.yaml +44 -0
  199. raxe/packs/core/v1.0.0/rules/PI/pi-3016@1.0.0.yaml +44 -0
  200. raxe/packs/core/v1.0.0/rules/PI/pi-3026@1.0.0.yaml +39 -0
  201. raxe/packs/core/v1.0.0/rules/PI/pi-3027@1.0.0.yaml +64 -0
  202. raxe/packs/core/v1.0.0/rules/PI/pi-3028@1.0.0.yaml +51 -0
  203. raxe/packs/core/v1.0.0/rules/PI/pi-3029@1.0.0.yaml +53 -0
  204. raxe/packs/core/v1.0.0/rules/PI/pi-3030@1.0.0.yaml +50 -0
  205. raxe/packs/core/v1.0.0/rules/PI/pi-3031@1.0.0.yaml +50 -0
  206. raxe/packs/core/v1.0.0/rules/PI/pi-3032@1.0.0.yaml +50 -0
  207. raxe/packs/core/v1.0.0/rules/PI/pi-3033@1.0.0.yaml +56 -0
  208. raxe/packs/core/v1.0.0/rules/PI/pi-3034@1.0.0.yaml +50 -0
  209. raxe/packs/core/v1.0.0/rules/PI/pi-79@1.0.0.yaml +38 -0
  210. raxe/packs/core/v1.0.0/rules/PI/pi-80@1.0.0.yaml +38 -0
  211. raxe/packs/core/v1.0.0/rules/PI/pi-81@1.0.0.yaml +38 -0
  212. raxe/packs/core/v1.0.0/rules/PI/pi-82@1.0.0.yaml +38 -0
  213. raxe/packs/core/v1.0.0/rules/PI/pi-83@1.0.0.yaml +38 -0
  214. raxe/packs/core/v1.0.0/rules/PI/pi-84@1.0.0.yaml +38 -0
  215. raxe/packs/core/v1.0.0/rules/PI/pi-85@1.0.0.yaml +38 -0
  216. raxe/packs/core/v1.0.0/rules/PI/pi-86@1.0.0.yaml +38 -0
  217. raxe/packs/core/v1.0.0/rules/PI/pi-87@1.0.0.yaml +38 -0
  218. raxe/packs/core/v1.0.0/rules/PI/pi-88@1.0.0.yaml +38 -0
  219. raxe/packs/core/v1.0.0/rules/PI/pi-89@1.0.0.yaml +38 -0
  220. raxe/packs/core/v1.0.0/rules/PI/pi-90@1.0.0.yaml +38 -0
  221. raxe/packs/core/v1.0.0/rules/PI/pi-91@1.0.0.yaml +38 -0
  222. raxe/packs/core/v1.0.0/rules/PI/pi-92@1.0.0.yaml +38 -0
  223. raxe/packs/core/v1.0.0/rules/PI/pi-93@1.0.0.yaml +38 -0
  224. raxe/packs/core/v1.0.0/rules/PI/pi-94@1.0.0.yaml +38 -0
  225. raxe/packs/core/v1.0.0/rules/PI/pi-95@1.0.0.yaml +38 -0
  226. raxe/packs/core/v1.0.0/rules/PI/pi-96@1.0.0.yaml +38 -0
  227. raxe/packs/core/v1.0.0/rules/PI/pi-97@1.0.0.yaml +38 -0
  228. raxe/packs/core/v1.0.0/rules/PI/pi-98@1.0.0.yaml +38 -0
  229. raxe/packs/core/v1.0.0/rules/cmd/cmd-001@1.0.0.yaml +48 -0
  230. raxe/packs/core/v1.0.0/rules/cmd/cmd-007@1.0.0.yaml +48 -0
  231. raxe/packs/core/v1.0.0/rules/cmd/cmd-015@1.0.0.yaml +56 -0
  232. raxe/packs/core/v1.0.0/rules/cmd/cmd-016@1.0.0.yaml +46 -0
  233. raxe/packs/core/v1.0.0/rules/cmd/cmd-017@1.0.0.yaml +57 -0
  234. raxe/packs/core/v1.0.0/rules/cmd/cmd-021@1.0.0.yaml +46 -0
  235. raxe/packs/core/v1.0.0/rules/cmd/cmd-022@1.0.0.yaml +46 -0
  236. raxe/packs/core/v1.0.0/rules/cmd/cmd-023@1.0.0.yaml +78 -0
  237. raxe/packs/core/v1.0.0/rules/cmd/cmd-024@1.0.0.yaml +46 -0
  238. raxe/packs/core/v1.0.0/rules/cmd/cmd-025@1.0.0.yaml +93 -0
  239. raxe/packs/core/v1.0.0/rules/cmd/cmd-026@1.0.0.yaml +81 -0
  240. raxe/packs/core/v1.0.0/rules/cmd/cmd-027@1.0.0.yaml +82 -0
  241. raxe/packs/core/v1.0.0/rules/cmd/cmd-028@1.0.0.yaml +46 -0
  242. raxe/packs/core/v1.0.0/rules/cmd/cmd-033@1.0.0.yaml +48 -0
  243. raxe/packs/core/v1.0.0/rules/cmd/cmd-036@1.0.0.yaml +47 -0
  244. raxe/packs/core/v1.0.0/rules/cmd/cmd-037@1.0.0.yaml +44 -0
  245. raxe/packs/core/v1.0.0/rules/cmd/cmd-052@1.0.0.yaml +43 -0
  246. raxe/packs/core/v1.0.0/rules/cmd/cmd-054@1.0.0.yaml +44 -0
  247. raxe/packs/core/v1.0.0/rules/cmd/cmd-056@1.0.0.yaml +43 -0
  248. raxe/packs/core/v1.0.0/rules/cmd/cmd-065@1.0.0.yaml +46 -0
  249. raxe/packs/core/v1.0.0/rules/cmd/cmd-075@1.0.0.yaml +45 -0
  250. raxe/packs/core/v1.0.0/rules/cmd/cmd-079@1.0.0.yaml +44 -0
  251. raxe/packs/core/v1.0.0/rules/cmd/cmd-1080@1.0.0.yaml +41 -0
  252. raxe/packs/core/v1.0.0/rules/cmd/cmd-1090@1.0.0.yaml +41 -0
  253. raxe/packs/core/v1.0.0/rules/cmd/cmd-1104@1.0.0.yaml +44 -0
  254. raxe/packs/core/v1.0.0/rules/cmd/cmd-1105@1.0.0.yaml +41 -0
  255. raxe/packs/core/v1.0.0/rules/cmd/cmd-1112@1.0.0.yaml +44 -0
  256. raxe/packs/core/v1.0.0/rules/cmd/cmd-201@1.0.0.yaml +47 -0
  257. raxe/packs/core/v1.0.0/rules/cmd/cmd-202@1.0.0.yaml +42 -0
  258. raxe/packs/core/v1.0.0/rules/cmd/cmd-203@1.0.0.yaml +43 -0
  259. raxe/packs/core/v1.0.0/rules/cmd/cmd-204@1.0.0.yaml +47 -0
  260. raxe/packs/core/v1.0.0/rules/cmd/cmd-205@1.0.0.yaml +44 -0
  261. raxe/packs/core/v1.0.0/rules/cmd/cmd-206@1.0.0.yaml +47 -0
  262. raxe/packs/core/v1.0.0/rules/cmd/cmd-207@1.0.0.yaml +46 -0
  263. raxe/packs/core/v1.0.0/rules/cmd/cmd-208@1.0.0.yaml +42 -0
  264. raxe/packs/core/v1.0.0/rules/cmd/cmd-209@1.0.0.yaml +38 -0
  265. raxe/packs/core/v1.0.0/rules/cmd/cmd-210@1.0.0.yaml +38 -0
  266. raxe/packs/core/v1.0.0/rules/cmd/cmd-211@1.0.0.yaml +38 -0
  267. raxe/packs/core/v1.0.0/rules/cmd/cmd-212@1.0.0.yaml +38 -0
  268. raxe/packs/core/v1.0.0/rules/cmd/cmd-213@1.0.0.yaml +38 -0
  269. raxe/packs/core/v1.0.0/rules/cmd/cmd-214@1.0.0.yaml +38 -0
  270. raxe/packs/core/v1.0.0/rules/cmd/cmd-215@1.0.0.yaml +38 -0
  271. raxe/packs/core/v1.0.0/rules/cmd/cmd-216@1.0.0.yaml +38 -0
  272. raxe/packs/core/v1.0.0/rules/cmd/cmd-217@1.0.0.yaml +38 -0
  273. raxe/packs/core/v1.0.0/rules/cmd/cmd-218@1.0.0.yaml +38 -0
  274. raxe/packs/core/v1.0.0/rules/cmd/cmd-219@1.0.0.yaml +38 -0
  275. raxe/packs/core/v1.0.0/rules/cmd/cmd-220@1.0.0.yaml +38 -0
  276. raxe/packs/core/v1.0.0/rules/cmd/cmd-221@1.0.0.yaml +38 -0
  277. raxe/packs/core/v1.0.0/rules/cmd/cmd-222@1.0.0.yaml +38 -0
  278. raxe/packs/core/v1.0.0/rules/cmd/cmd-223@1.0.0.yaml +38 -0
  279. raxe/packs/core/v1.0.0/rules/cmd/cmd-224@1.0.0.yaml +38 -0
  280. raxe/packs/core/v1.0.0/rules/cmd/cmd-225@1.0.0.yaml +38 -0
  281. raxe/packs/core/v1.0.0/rules/cmd/cmd-226@1.0.0.yaml +38 -0
  282. raxe/packs/core/v1.0.0/rules/cmd/cmd-227@1.0.0.yaml +38 -0
  283. raxe/packs/core/v1.0.0/rules/cmd/cmd-228@1.0.0.yaml +38 -0
  284. raxe/packs/core/v1.0.0/rules/cmd/cmd-229@1.0.0.yaml +38 -0
  285. raxe/packs/core/v1.0.0/rules/cmd/cmd-230@1.0.0.yaml +38 -0
  286. raxe/packs/core/v1.0.0/rules/cmd/cmd-231@1.0.0.yaml +38 -0
  287. raxe/packs/core/v1.0.0/rules/cmd/cmd-232@1.0.0.yaml +38 -0
  288. raxe/packs/core/v1.0.0/rules/cmd/cmd-233@1.0.0.yaml +38 -0
  289. raxe/packs/core/v1.0.0/rules/cmd/cmd-234@1.0.0.yaml +38 -0
  290. raxe/packs/core/v1.0.0/rules/cmd/cmd-235@1.0.0.yaml +38 -0
  291. raxe/packs/core/v1.0.0/rules/cmd/cmd-236@1.0.0.yaml +38 -0
  292. raxe/packs/core/v1.0.0/rules/cmd/cmd-237@1.0.0.yaml +38 -0
  293. raxe/packs/core/v1.0.0/rules/cmd/cmd-238@1.0.0.yaml +38 -0
  294. raxe/packs/core/v1.0.0/rules/enc/enc-001@1.0.0.yaml +48 -0
  295. raxe/packs/core/v1.0.0/rules/enc/enc-013@1.0.0.yaml +46 -0
  296. raxe/packs/core/v1.0.0/rules/enc/enc-019@1.0.0.yaml +43 -0
  297. raxe/packs/core/v1.0.0/rules/enc/enc-020@1.0.0.yaml +47 -0
  298. raxe/packs/core/v1.0.0/rules/enc/enc-024@1.0.0.yaml +46 -0
  299. raxe/packs/core/v1.0.0/rules/enc/enc-029@1.0.0.yaml +44 -0
  300. raxe/packs/core/v1.0.0/rules/enc/enc-038@1.0.0.yaml +44 -0
  301. raxe/packs/core/v1.0.0/rules/enc/enc-044@1.0.0.yaml +46 -0
  302. raxe/packs/core/v1.0.0/rules/enc/enc-067@1.0.0.yaml +42 -0
  303. raxe/packs/core/v1.0.0/rules/enc/enc-069@1.0.0.yaml +42 -0
  304. raxe/packs/core/v1.0.0/rules/enc/enc-100@1.0.0.yaml +38 -0
  305. raxe/packs/core/v1.0.0/rules/enc/enc-101@1.0.0.yaml +38 -0
  306. raxe/packs/core/v1.0.0/rules/enc/enc-102@1.0.0.yaml +38 -0
  307. raxe/packs/core/v1.0.0/rules/enc/enc-103@1.0.0.yaml +38 -0
  308. raxe/packs/core/v1.0.0/rules/enc/enc-104@1.0.0.yaml +38 -0
  309. raxe/packs/core/v1.0.0/rules/enc/enc-105@1.0.0.yaml +38 -0
  310. raxe/packs/core/v1.0.0/rules/enc/enc-106@1.0.0.yaml +38 -0
  311. raxe/packs/core/v1.0.0/rules/enc/enc-107@1.0.0.yaml +38 -0
  312. raxe/packs/core/v1.0.0/rules/enc/enc-108@1.0.0.yaml +38 -0
  313. raxe/packs/core/v1.0.0/rules/enc/enc-109@1.0.0.yaml +38 -0
  314. raxe/packs/core/v1.0.0/rules/enc/enc-110@1.0.0.yaml +38 -0
  315. raxe/packs/core/v1.0.0/rules/enc/enc-111@1.0.0.yaml +38 -0
  316. raxe/packs/core/v1.0.0/rules/enc/enc-112@1.0.0.yaml +38 -0
  317. raxe/packs/core/v1.0.0/rules/enc/enc-113@1.0.0.yaml +38 -0
  318. raxe/packs/core/v1.0.0/rules/enc/enc-114@1.0.0.yaml +38 -0
  319. raxe/packs/core/v1.0.0/rules/enc/enc-115@1.0.0.yaml +38 -0
  320. raxe/packs/core/v1.0.0/rules/enc/enc-116@1.0.0.yaml +38 -0
  321. raxe/packs/core/v1.0.0/rules/enc/enc-117@1.0.0.yaml +38 -0
  322. raxe/packs/core/v1.0.0/rules/enc/enc-118@1.0.0.yaml +38 -0
  323. raxe/packs/core/v1.0.0/rules/enc/enc-119@1.0.0.yaml +38 -0
  324. raxe/packs/core/v1.0.0/rules/enc/enc-120@1.0.0.yaml +38 -0
  325. raxe/packs/core/v1.0.0/rules/enc/enc-201@1.0.0.yaml +37 -0
  326. raxe/packs/core/v1.0.0/rules/enc/enc-202@1.0.0.yaml +41 -0
  327. raxe/packs/core/v1.0.0/rules/enc/enc-203@1.0.0.yaml +41 -0
  328. raxe/packs/core/v1.0.0/rules/enc/enc-3004@1.0.0.yaml +40 -0
  329. raxe/packs/core/v1.0.0/rules/enc/enc-3006@1.0.0.yaml +40 -0
  330. raxe/packs/core/v1.0.0/rules/enc/enc-3011@1.0.0.yaml +40 -0
  331. raxe/packs/core/v1.0.0/rules/enc/enc-5016@1.0.0.yaml +46 -0
  332. raxe/packs/core/v1.0.0/rules/enc/enc-6001@1.0.0.yaml +53 -0
  333. raxe/packs/core/v1.0.0/rules/enc/enc-6002@1.0.0.yaml +41 -0
  334. raxe/packs/core/v1.0.0/rules/enc/enc-70@1.0.0.yaml +38 -0
  335. raxe/packs/core/v1.0.0/rules/enc/enc-71@1.0.0.yaml +38 -0
  336. raxe/packs/core/v1.0.0/rules/enc/enc-72@1.0.0.yaml +38 -0
  337. raxe/packs/core/v1.0.0/rules/enc/enc-73@1.0.0.yaml +38 -0
  338. raxe/packs/core/v1.0.0/rules/enc/enc-74@1.0.0.yaml +38 -0
  339. raxe/packs/core/v1.0.0/rules/enc/enc-75@1.0.0.yaml +38 -0
  340. raxe/packs/core/v1.0.0/rules/enc/enc-76@1.0.0.yaml +38 -0
  341. raxe/packs/core/v1.0.0/rules/enc/enc-77@1.0.0.yaml +38 -0
  342. raxe/packs/core/v1.0.0/rules/enc/enc-78@1.0.0.yaml +38 -0
  343. raxe/packs/core/v1.0.0/rules/enc/enc-79@1.0.0.yaml +38 -0
  344. raxe/packs/core/v1.0.0/rules/enc/enc-80@1.0.0.yaml +38 -0
  345. raxe/packs/core/v1.0.0/rules/enc/enc-81@1.0.0.yaml +38 -0
  346. raxe/packs/core/v1.0.0/rules/enc/enc-82@1.0.0.yaml +38 -0
  347. raxe/packs/core/v1.0.0/rules/enc/enc-83@1.0.0.yaml +38 -0
  348. raxe/packs/core/v1.0.0/rules/enc/enc-84@1.0.0.yaml +38 -0
  349. raxe/packs/core/v1.0.0/rules/enc/enc-85@1.0.0.yaml +38 -0
  350. raxe/packs/core/v1.0.0/rules/enc/enc-86@1.0.0.yaml +38 -0
  351. raxe/packs/core/v1.0.0/rules/enc/enc-87@1.0.0.yaml +38 -0
  352. raxe/packs/core/v1.0.0/rules/enc/enc-88@1.0.0.yaml +38 -0
  353. raxe/packs/core/v1.0.0/rules/enc/enc-89@1.0.0.yaml +38 -0
  354. raxe/packs/core/v1.0.0/rules/enc/enc-90@1.0.0.yaml +38 -0
  355. raxe/packs/core/v1.0.0/rules/enc/enc-91@1.0.0.yaml +38 -0
  356. raxe/packs/core/v1.0.0/rules/enc/enc-92@1.0.0.yaml +38 -0
  357. raxe/packs/core/v1.0.0/rules/enc/enc-93@1.0.0.yaml +38 -0
  358. raxe/packs/core/v1.0.0/rules/enc/enc-94@1.0.0.yaml +38 -0
  359. raxe/packs/core/v1.0.0/rules/enc/enc-95@1.0.0.yaml +38 -0
  360. raxe/packs/core/v1.0.0/rules/enc/enc-96@1.0.0.yaml +38 -0
  361. raxe/packs/core/v1.0.0/rules/enc/enc-97@1.0.0.yaml +38 -0
  362. raxe/packs/core/v1.0.0/rules/enc/enc-98@1.0.0.yaml +38 -0
  363. raxe/packs/core/v1.0.0/rules/enc/enc-99@1.0.0.yaml +38 -0
  364. raxe/packs/core/v1.0.0/rules/hc/hc-001@1.0.0.yaml +73 -0
  365. raxe/packs/core/v1.0.0/rules/hc/hc-002@1.0.0.yaml +71 -0
  366. raxe/packs/core/v1.0.0/rules/hc/hc-003@1.0.0.yaml +65 -0
  367. raxe/packs/core/v1.0.0/rules/hc/hc-004@1.0.0.yaml +73 -0
  368. raxe/packs/core/v1.0.0/rules/hc/hc-101@1.0.0.yaml +47 -0
  369. raxe/packs/core/v1.0.0/rules/hc/hc-102@1.0.0.yaml +47 -0
  370. raxe/packs/core/v1.0.0/rules/hc/hc-103@1.0.0.yaml +47 -0
  371. raxe/packs/core/v1.0.0/rules/hc/hc-104@1.0.0.yaml +47 -0
  372. raxe/packs/core/v1.0.0/rules/hc/hc-105@1.0.0.yaml +48 -0
  373. raxe/packs/core/v1.0.0/rules/hc/hc-106@1.0.0.yaml +40 -0
  374. raxe/packs/core/v1.0.0/rules/hc/hc-107@1.0.0.yaml +47 -0
  375. raxe/packs/core/v1.0.0/rules/hc/hc-108@1.0.0.yaml +47 -0
  376. raxe/packs/core/v1.0.0/rules/hc/hc-109@1.0.0.yaml +50 -0
  377. raxe/packs/core/v1.0.0/rules/hc/hc-110@1.0.0.yaml +56 -0
  378. raxe/packs/core/v1.0.0/rules/hc/hc-111@1.0.0.yaml +49 -0
  379. raxe/packs/core/v1.0.0/rules/hc/hc-112@1.0.0.yaml +53 -0
  380. raxe/packs/core/v1.0.0/rules/hc/hc-113@1.0.0.yaml +52 -0
  381. raxe/packs/core/v1.0.0/rules/hc/hc-114@1.0.0.yaml +52 -0
  382. raxe/packs/core/v1.0.0/rules/hc/hc-115@1.0.0.yaml +52 -0
  383. raxe/packs/core/v1.0.0/rules/hc/hc-116@1.0.0.yaml +53 -0
  384. raxe/packs/core/v1.0.0/rules/hc/hc-117@1.0.0.yaml +54 -0
  385. raxe/packs/core/v1.0.0/rules/hc/hc-118@1.0.0.yaml +52 -0
  386. raxe/packs/core/v1.0.0/rules/hc/hc-119@1.0.0.yaml +51 -0
  387. raxe/packs/core/v1.0.0/rules/hc/hc-120@1.0.0.yaml +52 -0
  388. raxe/packs/core/v1.0.0/rules/hc/hc-121@1.0.0.yaml +51 -0
  389. raxe/packs/core/v1.0.0/rules/hc/hc-122@1.0.0.yaml +51 -0
  390. raxe/packs/core/v1.0.0/rules/hc/hc-123@1.0.0.yaml +52 -0
  391. raxe/packs/core/v1.0.0/rules/hc/hc-124@1.0.0.yaml +53 -0
  392. raxe/packs/core/v1.0.0/rules/hc/hc-125@1.0.0.yaml +53 -0
  393. raxe/packs/core/v1.0.0/rules/hc/hc-126@1.0.0.yaml +53 -0
  394. raxe/packs/core/v1.0.0/rules/hc/hc-127@1.0.0.yaml +53 -0
  395. raxe/packs/core/v1.0.0/rules/hc/hc-128@1.0.0.yaml +53 -0
  396. raxe/packs/core/v1.0.0/rules/hc/hc-129@1.0.0.yaml +51 -0
  397. raxe/packs/core/v1.0.0/rules/hc/hc-130@1.0.0.yaml +51 -0
  398. raxe/packs/core/v1.0.0/rules/hc/hc-131@1.0.0.yaml +51 -0
  399. raxe/packs/core/v1.0.0/rules/hc/hc-132@1.0.0.yaml +51 -0
  400. raxe/packs/core/v1.0.0/rules/hc/hc-133@1.0.0.yaml +53 -0
  401. raxe/packs/core/v1.0.0/rules/hc/hc-134@1.0.0.yaml +51 -0
  402. raxe/packs/core/v1.0.0/rules/hc/hc-135@1.0.0.yaml +51 -0
  403. raxe/packs/core/v1.0.0/rules/hc/hc-136@1.0.0.yaml +51 -0
  404. raxe/packs/core/v1.0.0/rules/hc/hc-137@1.0.0.yaml +51 -0
  405. raxe/packs/core/v1.0.0/rules/hc/hc-138@1.0.0.yaml +51 -0
  406. raxe/packs/core/v1.0.0/rules/hc/hc-139@1.0.0.yaml +51 -0
  407. raxe/packs/core/v1.0.0/rules/hc/hc-140@1.0.0.yaml +51 -0
  408. raxe/packs/core/v1.0.0/rules/hc/hc-141@1.0.0.yaml +41 -0
  409. raxe/packs/core/v1.0.0/rules/hc/hc-142@1.0.0.yaml +37 -0
  410. raxe/packs/core/v1.0.0/rules/hc/hc-143@1.0.0.yaml +37 -0
  411. raxe/packs/core/v1.0.0/rules/hc/hc-144@1.0.0.yaml +37 -0
  412. raxe/packs/core/v1.0.0/rules/hc/hc-145@1.0.0.yaml +37 -0
  413. raxe/packs/core/v1.0.0/rules/hc/hc-146@1.0.0.yaml +37 -0
  414. raxe/packs/core/v1.0.0/rules/hc/hc-147@1.0.0.yaml +37 -0
  415. raxe/packs/core/v1.0.0/rules/hc/hc-148@1.0.0.yaml +37 -0
  416. raxe/packs/core/v1.0.0/rules/hc/hc-149@1.0.0.yaml +37 -0
  417. raxe/packs/core/v1.0.0/rules/hc/hc-150@1.0.0.yaml +37 -0
  418. raxe/packs/core/v1.0.0/rules/hc/hc-151@1.0.0.yaml +37 -0
  419. raxe/packs/core/v1.0.0/rules/hc/hc-152@1.0.0.yaml +37 -0
  420. raxe/packs/core/v1.0.0/rules/hc/hc-153@1.0.0.yaml +37 -0
  421. raxe/packs/core/v1.0.0/rules/hc/hc-154@1.0.0.yaml +37 -0
  422. raxe/packs/core/v1.0.0/rules/hc/hc-155@1.0.0.yaml +37 -0
  423. raxe/packs/core/v1.0.0/rules/hc/hc-156@1.0.0.yaml +37 -0
  424. raxe/packs/core/v1.0.0/rules/hc/hc-157@1.0.0.yaml +37 -0
  425. raxe/packs/core/v1.0.0/rules/hc/hc-158@1.0.0.yaml +37 -0
  426. raxe/packs/core/v1.0.0/rules/hc/hc-159@1.0.0.yaml +37 -0
  427. raxe/packs/core/v1.0.0/rules/hc/hc-160@1.0.0.yaml +37 -0
  428. raxe/packs/core/v1.0.0/rules/hc/hc-161@1.0.0.yaml +37 -0
  429. raxe/packs/core/v1.0.0/rules/jb/jb-001@1.0.0.yaml +47 -0
  430. raxe/packs/core/v1.0.0/rules/jb/jb-009@1.0.0.yaml +47 -0
  431. raxe/packs/core/v1.0.0/rules/jb/jb-020@1.0.0.yaml +47 -0
  432. raxe/packs/core/v1.0.0/rules/jb/jb-021@1.0.0.yaml +46 -0
  433. raxe/packs/core/v1.0.0/rules/jb/jb-022@1.0.0.yaml +47 -0
  434. raxe/packs/core/v1.0.0/rules/jb/jb-028@1.0.0.yaml +43 -0
  435. raxe/packs/core/v1.0.0/rules/jb/jb-033@1.0.0.yaml +46 -0
  436. raxe/packs/core/v1.0.0/rules/jb/jb-034@1.0.0.yaml +46 -0
  437. raxe/packs/core/v1.0.0/rules/jb/jb-036@1.0.0.yaml +41 -0
  438. raxe/packs/core/v1.0.0/rules/jb/jb-039@1.0.0.yaml +41 -0
  439. raxe/packs/core/v1.0.0/rules/jb/jb-056@1.0.0.yaml +38 -0
  440. raxe/packs/core/v1.0.0/rules/jb/jb-066@1.0.0.yaml +37 -0
  441. raxe/packs/core/v1.0.0/rules/jb/jb-076@1.0.0.yaml +37 -0
  442. raxe/packs/core/v1.0.0/rules/jb/jb-098@1.0.0.yaml +46 -0
  443. raxe/packs/core/v1.0.0/rules/jb/jb-103@1.0.0.yaml +47 -0
  444. raxe/packs/core/v1.0.0/rules/jb/jb-104@1.0.0.yaml +52 -0
  445. raxe/packs/core/v1.0.0/rules/jb/jb-105@1.0.0.yaml +56 -0
  446. raxe/packs/core/v1.0.0/rules/jb/jb-110@1.0.0.yaml +56 -0
  447. raxe/packs/core/v1.0.0/rules/jb/jb-111@1.0.0.yaml +57 -0
  448. raxe/packs/core/v1.0.0/rules/jb/jb-112@1.0.0.yaml +38 -0
  449. raxe/packs/core/v1.0.0/rules/jb/jb-113@1.0.0.yaml +38 -0
  450. raxe/packs/core/v1.0.0/rules/jb/jb-114@1.0.0.yaml +38 -0
  451. raxe/packs/core/v1.0.0/rules/jb/jb-115@1.0.0.yaml +38 -0
  452. raxe/packs/core/v1.0.0/rules/jb/jb-116@1.0.0.yaml +38 -0
  453. raxe/packs/core/v1.0.0/rules/jb/jb-117@1.0.0.yaml +38 -0
  454. raxe/packs/core/v1.0.0/rules/jb/jb-118@1.0.0.yaml +38 -0
  455. raxe/packs/core/v1.0.0/rules/jb/jb-119@1.0.0.yaml +38 -0
  456. raxe/packs/core/v1.0.0/rules/jb/jb-120@1.0.0.yaml +38 -0
  457. raxe/packs/core/v1.0.0/rules/jb/jb-121@1.0.0.yaml +38 -0
  458. raxe/packs/core/v1.0.0/rules/jb/jb-122@1.0.0.yaml +38 -0
  459. raxe/packs/core/v1.0.0/rules/jb/jb-123@1.0.0.yaml +38 -0
  460. raxe/packs/core/v1.0.0/rules/jb/jb-124@1.0.0.yaml +38 -0
  461. raxe/packs/core/v1.0.0/rules/jb/jb-125@1.0.0.yaml +38 -0
  462. raxe/packs/core/v1.0.0/rules/jb/jb-126@1.0.0.yaml +38 -0
  463. raxe/packs/core/v1.0.0/rules/jb/jb-127@1.0.0.yaml +38 -0
  464. raxe/packs/core/v1.0.0/rules/jb/jb-128@1.0.0.yaml +38 -0
  465. raxe/packs/core/v1.0.0/rules/jb/jb-129@1.0.0.yaml +38 -0
  466. raxe/packs/core/v1.0.0/rules/jb/jb-130@1.0.0.yaml +38 -0
  467. raxe/packs/core/v1.0.0/rules/jb/jb-131@1.0.0.yaml +38 -0
  468. raxe/packs/core/v1.0.0/rules/jb/jb-132@1.0.0.yaml +38 -0
  469. raxe/packs/core/v1.0.0/rules/jb/jb-133@1.0.0.yaml +38 -0
  470. raxe/packs/core/v1.0.0/rules/jb/jb-134@1.0.0.yaml +38 -0
  471. raxe/packs/core/v1.0.0/rules/jb/jb-135@1.0.0.yaml +38 -0
  472. raxe/packs/core/v1.0.0/rules/jb/jb-136@1.0.0.yaml +38 -0
  473. raxe/packs/core/v1.0.0/rules/jb/jb-137@1.0.0.yaml +38 -0
  474. raxe/packs/core/v1.0.0/rules/jb/jb-138@1.0.0.yaml +38 -0
  475. raxe/packs/core/v1.0.0/rules/jb/jb-139@1.0.0.yaml +38 -0
  476. raxe/packs/core/v1.0.0/rules/jb/jb-140@1.0.0.yaml +38 -0
  477. raxe/packs/core/v1.0.0/rules/jb/jb-141@1.0.0.yaml +38 -0
  478. raxe/packs/core/v1.0.0/rules/jb/jb-142@1.0.0.yaml +38 -0
  479. raxe/packs/core/v1.0.0/rules/jb/jb-143@1.0.0.yaml +38 -0
  480. raxe/packs/core/v1.0.0/rules/jb/jb-144@1.0.0.yaml +38 -0
  481. raxe/packs/core/v1.0.0/rules/jb/jb-145@1.0.0.yaml +38 -0
  482. raxe/packs/core/v1.0.0/rules/jb/jb-146@1.0.0.yaml +38 -0
  483. raxe/packs/core/v1.0.0/rules/jb/jb-147@1.0.0.yaml +38 -0
  484. raxe/packs/core/v1.0.0/rules/jb/jb-148@1.0.0.yaml +38 -0
  485. raxe/packs/core/v1.0.0/rules/jb/jb-149@1.0.0.yaml +38 -0
  486. raxe/packs/core/v1.0.0/rules/jb/jb-150@1.0.0.yaml +38 -0
  487. raxe/packs/core/v1.0.0/rules/jb/jb-151@1.0.0.yaml +38 -0
  488. raxe/packs/core/v1.0.0/rules/jb/jb-152@1.0.0.yaml +38 -0
  489. raxe/packs/core/v1.0.0/rules/jb/jb-153@1.0.0.yaml +38 -0
  490. raxe/packs/core/v1.0.0/rules/jb/jb-154@1.0.0.yaml +38 -0
  491. raxe/packs/core/v1.0.0/rules/jb/jb-155@1.0.0.yaml +38 -0
  492. raxe/packs/core/v1.0.0/rules/jb/jb-156@1.0.0.yaml +38 -0
  493. raxe/packs/core/v1.0.0/rules/jb/jb-157@1.0.0.yaml +38 -0
  494. raxe/packs/core/v1.0.0/rules/jb/jb-158@1.0.0.yaml +38 -0
  495. raxe/packs/core/v1.0.0/rules/jb/jb-159@1.0.0.yaml +38 -0
  496. raxe/packs/core/v1.0.0/rules/jb/jb-160@1.0.0.yaml +38 -0
  497. raxe/packs/core/v1.0.0/rules/jb/jb-161@1.0.0.yaml +38 -0
  498. raxe/packs/core/v1.0.0/rules/jb/jb-162@1.0.0.yaml +38 -0
  499. raxe/packs/core/v1.0.0/rules/jb/jb-201@1.0.0.yaml +40 -0
  500. raxe/packs/core/v1.0.0/rules/jb/jb-202@1.0.0.yaml +41 -0
  501. raxe/packs/core/v1.0.0/rules/jb/jb-203@1.0.0.yaml +51 -0
  502. raxe/packs/core/v1.0.0/rules/jb/jb-204@1.0.0.yaml +50 -0
  503. raxe/packs/core/v1.0.0/rules/jb/jb-205@1.0.0.yaml +50 -0
  504. raxe/packs/core/v1.0.0/rules/jb/jb-206@1.0.0.yaml +50 -0
  505. raxe/packs/core/v1.0.0/rules/jb/jb-207@1.0.0.yaml +49 -0
  506. raxe/packs/core/v1.0.0/rules/pii/pii-001@1.0.0.yaml +48 -0
  507. raxe/packs/core/v1.0.0/rules/pii/pii-009@1.0.0.yaml +48 -0
  508. raxe/packs/core/v1.0.0/rules/pii/pii-012@1.0.0.yaml +48 -0
  509. raxe/packs/core/v1.0.0/rules/pii/pii-017@1.0.0.yaml +48 -0
  510. raxe/packs/core/v1.0.0/rules/pii/pii-022@1.0.0.yaml +47 -0
  511. raxe/packs/core/v1.0.0/rules/pii/pii-025@1.0.0.yaml +47 -0
  512. raxe/packs/core/v1.0.0/rules/pii/pii-027@1.0.0.yaml +47 -0
  513. raxe/packs/core/v1.0.0/rules/pii/pii-028@1.0.0.yaml +47 -0
  514. raxe/packs/core/v1.0.0/rules/pii/pii-034@1.0.0.yaml +47 -0
  515. raxe/packs/core/v1.0.0/rules/pii/pii-037@1.0.0.yaml +47 -0
  516. raxe/packs/core/v1.0.0/rules/pii/pii-040@1.0.0.yaml +47 -0
  517. raxe/packs/core/v1.0.0/rules/pii/pii-041@1.0.0.yaml +47 -0
  518. raxe/packs/core/v1.0.0/rules/pii/pii-044@1.0.0.yaml +47 -0
  519. raxe/packs/core/v1.0.0/rules/pii/pii-050@1.0.0.yaml +57 -0
  520. raxe/packs/core/v1.0.0/rules/pii/pii-051@1.0.0.yaml +53 -0
  521. raxe/packs/core/v1.0.0/rules/pii/pii-052@1.0.0.yaml +52 -0
  522. raxe/packs/core/v1.0.0/rules/pii/pii-053@1.0.0.yaml +56 -0
  523. raxe/packs/core/v1.0.0/rules/pii/pii-054@1.0.0.yaml +53 -0
  524. raxe/packs/core/v1.0.0/rules/pii/pii-055@1.0.0.yaml +51 -0
  525. raxe/packs/core/v1.0.0/rules/pii/pii-056@1.0.0.yaml +51 -0
  526. raxe/packs/core/v1.0.0/rules/pii/pii-058@1.0.0.yaml +47 -0
  527. raxe/packs/core/v1.0.0/rules/pii/pii-2015@1.0.0.yaml +41 -0
  528. raxe/packs/core/v1.0.0/rules/pii/pii-2025@1.0.0.yaml +35 -0
  529. raxe/packs/core/v1.0.0/rules/pii/pii-2026@1.0.0.yaml +39 -0
  530. raxe/packs/core/v1.0.0/rules/pii/pii-2035@1.0.0.yaml +39 -0
  531. raxe/packs/core/v1.0.0/rules/pii/pii-2037@1.0.0.yaml +39 -0
  532. raxe/packs/core/v1.0.0/rules/pii/pii-2042@1.0.0.yaml +39 -0
  533. raxe/packs/core/v1.0.0/rules/pii/pii-3001@1.0.0.yaml +39 -0
  534. raxe/packs/core/v1.0.0/rules/pii/pii-3002@1.0.0.yaml +41 -0
  535. raxe/packs/core/v1.0.0/rules/pii/pii-3003@1.0.0.yaml +36 -0
  536. raxe/packs/core/v1.0.0/rules/pii/pii-3004@1.0.0.yaml +41 -0
  537. raxe/packs/core/v1.0.0/rules/pii/pii-3005@1.0.0.yaml +39 -0
  538. raxe/packs/core/v1.0.0/rules/pii/pii-3006@1.0.0.yaml +35 -0
  539. raxe/packs/core/v1.0.0/rules/pii/pii-3007@1.0.0.yaml +37 -0
  540. raxe/packs/core/v1.0.0/rules/pii/pii-3008@1.0.0.yaml +35 -0
  541. raxe/packs/core/v1.0.0/rules/pii/pii-3009@1.0.0.yaml +42 -0
  542. raxe/packs/core/v1.0.0/rules/pii/pii-3010@1.0.0.yaml +39 -0
  543. raxe/packs/core/v1.0.0/rules/pii/pii-3011@1.0.0.yaml +35 -0
  544. raxe/packs/core/v1.0.0/rules/pii/pii-3012@1.0.0.yaml +35 -0
  545. raxe/packs/core/v1.0.0/rules/pii/pii-3013@1.0.0.yaml +36 -0
  546. raxe/packs/core/v1.0.0/rules/pii/pii-3014@1.0.0.yaml +36 -0
  547. raxe/packs/core/v1.0.0/rules/pii/pii-3015@1.0.0.yaml +42 -0
  548. raxe/packs/core/v1.0.0/rules/pii/pii-3016@1.0.0.yaml +42 -0
  549. raxe/packs/core/v1.0.0/rules/pii/pii-3017@1.0.0.yaml +40 -0
  550. raxe/packs/core/v1.0.0/rules/pii/pii-3018@1.0.0.yaml +38 -0
  551. raxe/packs/core/v1.0.0/rules/pii/pii-3019@1.0.0.yaml +40 -0
  552. raxe/packs/core/v1.0.0/rules/pii/pii-3020@1.0.0.yaml +40 -0
  553. raxe/packs/core/v1.0.0/rules/pii/pii-3021@1.0.0.yaml +39 -0
  554. raxe/packs/core/v1.0.0/rules/pii/pii-3022@1.0.0.yaml +36 -0
  555. raxe/packs/core/v1.0.0/rules/pii/pii-3023@1.0.0.yaml +41 -0
  556. raxe/packs/core/v1.0.0/rules/pii/pii-3024@1.0.0.yaml +37 -0
  557. raxe/packs/core/v1.0.0/rules/pii/pii-3025@1.0.0.yaml +38 -0
  558. raxe/packs/core/v1.0.0/rules/pii/pii-3026@1.0.0.yaml +42 -0
  559. raxe/packs/core/v1.0.0/rules/pii/pii-3027@1.0.0.yaml +38 -0
  560. raxe/packs/core/v1.0.0/rules/pii/pii-3028@1.0.0.yaml +42 -0
  561. raxe/packs/core/v1.0.0/rules/pii/pii-3029@1.0.0.yaml +36 -0
  562. raxe/packs/core/v1.0.0/rules/pii/pii-3030@1.0.0.yaml +42 -0
  563. raxe/packs/core/v1.0.0/rules/pii/pii-3031@1.0.0.yaml +37 -0
  564. raxe/packs/core/v1.0.0/rules/pii/pii-3032@1.0.0.yaml +42 -0
  565. raxe/packs/core/v1.0.0/rules/pii/pii-3033@1.0.0.yaml +39 -0
  566. raxe/packs/core/v1.0.0/rules/pii/pii-3034@1.0.0.yaml +40 -0
  567. raxe/packs/core/v1.0.0/rules/pii/pii-3035@1.0.0.yaml +43 -0
  568. raxe/packs/core/v1.0.0/rules/pii/pii-3036@1.0.0.yaml +41 -0
  569. raxe/packs/core/v1.0.0/rules/pii/pii-3037@1.0.0.yaml +35 -0
  570. raxe/packs/core/v1.0.0/rules/pii/pii-3038@1.0.0.yaml +35 -0
  571. raxe/packs/core/v1.0.0/rules/pii/pii-3039@1.0.0.yaml +35 -0
  572. raxe/packs/core/v1.0.0/rules/pii/pii-3040@1.0.0.yaml +41 -0
  573. raxe/packs/core/v1.0.0/rules/pii/pii-3041@1.0.0.yaml +39 -0
  574. raxe/packs/core/v1.0.0/rules/pii/pii-3042@1.0.0.yaml +36 -0
  575. raxe/packs/core/v1.0.0/rules/pii/pii-3043@1.0.0.yaml +35 -0
  576. raxe/packs/core/v1.0.0/rules/pii/pii-3044@1.0.0.yaml +43 -0
  577. raxe/packs/core/v1.0.0/rules/pii/pii-3045@1.0.0.yaml +36 -0
  578. raxe/packs/core/v1.0.0/rules/pii/pii-3046@1.0.0.yaml +37 -0
  579. raxe/packs/core/v1.0.0/rules/pii/pii-3047@1.0.0.yaml +36 -0
  580. raxe/packs/core/v1.0.0/rules/pii/pii-3048@1.0.0.yaml +36 -0
  581. raxe/packs/core/v1.0.0/rules/pii/pii-3049@1.0.0.yaml +38 -0
  582. raxe/packs/core/v1.0.0/rules/pii/pii-3050@1.0.0.yaml +44 -0
  583. raxe/packs/core/v1.0.0/rules/pii/pii-3051@1.0.0.yaml +35 -0
  584. raxe/packs/core/v1.0.0/rules/pii/pii-3052@1.0.0.yaml +36 -0
  585. raxe/packs/core/v1.0.0/rules/pii/pii-3053@1.0.0.yaml +35 -0
  586. raxe/packs/core/v1.0.0/rules/pii/pii-3054@1.0.0.yaml +35 -0
  587. raxe/packs/core/v1.0.0/rules/pii/pii-3055@1.0.0.yaml +40 -0
  588. raxe/packs/core/v1.0.0/rules/pii/pii-3056@1.0.0.yaml +38 -0
  589. raxe/packs/core/v1.0.0/rules/pii/pii-3057@1.0.0.yaml +40 -0
  590. raxe/packs/core/v1.0.0/rules/pii/pii-3058@1.0.0.yaml +43 -0
  591. raxe/packs/core/v1.0.0/rules/pii/pii-3059@1.0.0.yaml +42 -0
  592. raxe/packs/core/v1.0.0/rules/pii/pii-3060@1.0.0.yaml +42 -0
  593. raxe/packs/core/v1.0.0/rules/pii/pii-3061@1.0.0.yaml +50 -0
  594. raxe/packs/core/v1.0.0/rules/pii/pii-3062@1.0.0.yaml +50 -0
  595. raxe/packs/core/v1.0.0/rules/pii/pii-3063@1.0.0.yaml +54 -0
  596. raxe/packs/core/v1.0.0/rules/pii/pii-3064@1.0.0.yaml +78 -0
  597. raxe/packs/core/v1.0.0/rules/pii/pii-3065@1.0.0.yaml +84 -0
  598. raxe/packs/core/v1.0.0/rules/pii/pii-3066@1.0.0.yaml +84 -0
  599. raxe/packs/core/v1.0.0/rules/pii/pii-3067@1.0.0.yaml +88 -0
  600. raxe/packs/core/v1.0.0/rules/pii/pii-3068@1.0.0.yaml +94 -0
  601. raxe/packs/core/v1.0.0/rules/pii/pii-3069@1.0.0.yaml +90 -0
  602. raxe/packs/core/v1.0.0/rules/pii/pii-3070@1.0.0.yaml +99 -0
  603. raxe/packs/core/v1.0.0/rules/pii/pii-3071@1.0.0.yaml +91 -0
  604. raxe/packs/core/v1.0.0/rules/pii/pii-3072@1.0.0.yaml +38 -0
  605. raxe/packs/core/v1.0.0/rules/pii/pii-3073@1.0.0.yaml +38 -0
  606. raxe/packs/core/v1.0.0/rules/pii/pii-3074@1.0.0.yaml +38 -0
  607. raxe/packs/core/v1.0.0/rules/pii/pii-3075@1.0.0.yaml +38 -0
  608. raxe/packs/core/v1.0.0/rules/pii/pii-3076@1.0.0.yaml +38 -0
  609. raxe/packs/core/v1.0.0/rules/pii/pii-3077@1.0.0.yaml +38 -0
  610. raxe/packs/core/v1.0.0/rules/pii/pii-3078@1.0.0.yaml +38 -0
  611. raxe/packs/core/v1.0.0/rules/pii/pii-3079@1.0.0.yaml +38 -0
  612. raxe/packs/core/v1.0.0/rules/pii/pii-3080@1.0.0.yaml +38 -0
  613. raxe/packs/core/v1.0.0/rules/pii/pii-3081@1.0.0.yaml +38 -0
  614. raxe/packs/core/v1.0.0/rules/pii/pii-3082@1.0.0.yaml +38 -0
  615. raxe/packs/core/v1.0.0/rules/pii/pii-3083@1.0.0.yaml +38 -0
  616. raxe/packs/core/v1.0.0/rules/pii/pii-3084@1.0.0.yaml +38 -0
  617. raxe/packs/core/v1.0.0/rules/pii/pii-3085@1.0.0.yaml +38 -0
  618. raxe/packs/core/v1.0.0/rules/rag/rag-016@1.0.0.yaml +47 -0
  619. raxe/packs/core/v1.0.0/rules/rag/rag-028@1.0.0.yaml +47 -0
  620. raxe/packs/core/v1.0.0/rules/rag/rag-042@1.0.0.yaml +47 -0
  621. raxe/packs/core/v1.0.0/rules/rag/rag-044@1.0.0.yaml +47 -0
  622. raxe/packs/core/v1.0.0/rules/rag/rag-045@1.0.0.yaml +47 -0
  623. raxe/packs/core/v1.0.0/rules/rag/rag-050@1.0.0.yaml +47 -0
  624. raxe/packs/core/v1.0.0/rules/rag/rag-201@1.0.0.yaml +41 -0
  625. raxe/packs/core/v1.0.0/rules/rag/rag-202@1.0.0.yaml +41 -0
  626. raxe/packs/core/v1.0.0/rules/rag/rag-3001@1.0.0.yaml +41 -0
  627. raxe/packs/core/v1.0.0/rules/rag/rag-3006@1.0.0.yaml +41 -0
  628. raxe/packs/core/v1.0.0/rules/rag/rag-3009@1.0.0.yaml +41 -0
  629. raxe/packs/core/v1.0.0/rules/rag/rag-3012@1.0.0.yaml +41 -0
  630. raxe/plugins/__init__.py +98 -0
  631. raxe/plugins/custom_rules.py +380 -0
  632. raxe/plugins/loader.py +389 -0
  633. raxe/plugins/manager.py +538 -0
  634. raxe/plugins/protocol.py +428 -0
  635. raxe/py.typed +0 -0
  636. raxe/sdk/__init__.py +77 -0
  637. raxe/sdk/agent_scanner.py +1918 -0
  638. raxe/sdk/client.py +1603 -0
  639. raxe/sdk/decorator.py +175 -0
  640. raxe/sdk/exceptions.py +859 -0
  641. raxe/sdk/integrations/__init__.py +277 -0
  642. raxe/sdk/integrations/agent_scanner.py +71 -0
  643. raxe/sdk/integrations/autogen.py +872 -0
  644. raxe/sdk/integrations/crewai.py +1368 -0
  645. raxe/sdk/integrations/dspy.py +845 -0
  646. raxe/sdk/integrations/extractors.py +363 -0
  647. raxe/sdk/integrations/huggingface.py +395 -0
  648. raxe/sdk/integrations/langchain.py +948 -0
  649. raxe/sdk/integrations/litellm.py +484 -0
  650. raxe/sdk/integrations/llamaindex.py +1049 -0
  651. raxe/sdk/integrations/portkey.py +831 -0
  652. raxe/sdk/suppression_context.py +215 -0
  653. raxe/sdk/wrappers/__init__.py +163 -0
  654. raxe/sdk/wrappers/anthropic.py +310 -0
  655. raxe/sdk/wrappers/openai.py +221 -0
  656. raxe/sdk/wrappers/vertexai.py +484 -0
  657. raxe/utils/__init__.py +12 -0
  658. raxe/utils/error_sanitizer.py +135 -0
  659. raxe/utils/logging.py +241 -0
  660. raxe/utils/performance.py +414 -0
  661. raxe/utils/profiler.py +339 -0
  662. raxe/utils/validators.py +170 -0
  663. raxe-0.4.6.dist-info/METADATA +471 -0
  664. raxe-0.4.6.dist-info/RECORD +668 -0
  665. raxe-0.4.6.dist-info/WHEEL +5 -0
  666. raxe-0.4.6.dist-info/entry_points.txt +2 -0
  667. raxe-0.4.6.dist-info/licenses/LICENSE +56 -0
  668. raxe-0.4.6.dist-info/top_level.txt +1 -0
raxe/cli/telemetry.py ADDED
@@ -0,0 +1,1384 @@
1
+ """
2
+ CLI commands for telemetry management including DLQ operations.
3
+
4
+ Provides commands for:
5
+ - Viewing telemetry status
6
+ - Managing the Dead Letter Queue (DLQ)
7
+ - Flushing queues
8
+ - Enabling/disabling telemetry
9
+
10
+ Example usage:
11
+ raxe telemetry status
12
+ raxe telemetry dlq list
13
+ raxe telemetry flush
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import re
19
+ from datetime import datetime, timezone
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ import click
24
+ from rich.panel import Panel
25
+ from rich.table import Table
26
+ from rich.text import Text
27
+
28
+ from raxe.cli.error_handler import handle_cli_error
29
+ from raxe.cli.output import console, display_error, display_success, display_warning
30
+ from raxe.infrastructure.config.yaml_config import RaxeConfig
31
+ from raxe.infrastructure.telemetry.dual_queue import DualQueue
32
+ from raxe.infrastructure.telemetry.sender import BatchSender
33
+
34
+
35
+ def _get_api_credentials() -> tuple[str | None, str | None, str | None]:
36
+ """Get API key and installation_id with consistent priority chain.
37
+
38
+ Priority for API key:
39
+ 1. RAXE_API_KEY environment variable (highest priority)
40
+ 2. credentials.json file
41
+ 3. config.yaml
42
+
43
+ Installation ID is ALWAYS loaded from credentials (machine-specific).
44
+
45
+ Returns:
46
+ Tuple of (api_key, installation_id, source) where source indicates
47
+ where the API key came from ("environment", "credentials", "config", or None).
48
+ """
49
+ import os
50
+ from raxe.infrastructure.telemetry.credential_store import CredentialStore
51
+
52
+ api_key: str | None = None
53
+ installation_id: str | None = None
54
+ source: str | None = None
55
+
56
+ # Always get installation_id from credentials (machine-specific, not key-specific)
57
+ try:
58
+ credential_store = CredentialStore()
59
+ credentials = credential_store.get_or_create(raise_on_expired=False)
60
+ installation_id = credentials.installation_id
61
+ except Exception:
62
+ pass
63
+
64
+ # Priority 1: Environment variable for API key
65
+ env_api_key = os.environ.get("RAXE_API_KEY", "").strip()
66
+ if env_api_key:
67
+ api_key = env_api_key
68
+ source = "environment"
69
+
70
+ # Priority 2: Credentials file for API key (if not from env)
71
+ if not api_key:
72
+ try:
73
+ credential_store = CredentialStore()
74
+ credentials = credential_store.load()
75
+ if credentials and credentials.api_key:
76
+ api_key = credentials.api_key
77
+ source = "credentials"
78
+ except Exception:
79
+ pass
80
+
81
+ # Priority 3: Config file
82
+ if not api_key:
83
+ try:
84
+ config = RaxeConfig.load()
85
+ if config.core.api_key:
86
+ api_key = config.core.api_key
87
+ source = "config"
88
+ except Exception:
89
+ pass
90
+
91
+ return api_key, installation_id, source
92
+
93
+
94
+ def _parse_duration(duration_str: str) -> int | None:
95
+ """Parse a duration string (e.g., '7d', '24h', '30m') to days.
96
+
97
+ Args:
98
+ duration_str: Duration string with unit suffix (d=days, h=hours, m=minutes)
99
+
100
+ Returns:
101
+ Number of days (rounded up for partial days), or None if invalid
102
+
103
+ Examples:
104
+ >>> _parse_duration("7d")
105
+ 7
106
+ >>> _parse_duration("24h")
107
+ 1
108
+ >>> _parse_duration("30m")
109
+ 1
110
+ """
111
+ match = re.match(r"^(\d+)([dhm])$", duration_str.strip().lower())
112
+ if not match:
113
+ return None
114
+
115
+ value = int(match.group(1))
116
+ unit = match.group(2)
117
+
118
+ if unit == "d":
119
+ return value
120
+ elif unit == "h":
121
+ # Round up to at least 1 day
122
+ return max(1, value // 24)
123
+ elif unit == "m":
124
+ # Round up to at least 1 day
125
+ return max(1, value // (24 * 60))
126
+
127
+ return None
128
+
129
+
130
+ def _format_relative_time(timestamp_str: str | None) -> str:
131
+ """Format a timestamp as a relative time string.
132
+
133
+ Args:
134
+ timestamp_str: ISO format timestamp string or None
135
+
136
+ Returns:
137
+ Human-readable relative time string (e.g., "3s ago", "4m ago", "2h ago")
138
+ """
139
+ if not timestamp_str:
140
+ return "unknown"
141
+
142
+ try:
143
+ timestamp = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
144
+ if timestamp.tzinfo is None:
145
+ timestamp = timestamp.replace(tzinfo=timezone.utc)
146
+
147
+ now = datetime.now(timezone.utc)
148
+ delta = now - timestamp
149
+
150
+ seconds = int(delta.total_seconds())
151
+
152
+ if seconds < 0:
153
+ return "in the future"
154
+ elif seconds < 60:
155
+ return f"{seconds}s ago"
156
+ elif seconds < 3600:
157
+ minutes = seconds // 60
158
+ return f"{minutes}m ago"
159
+ elif seconds < 86400:
160
+ hours = seconds // 3600
161
+ return f"{hours}h ago"
162
+ else:
163
+ days = seconds // 86400
164
+ return f"{days}d ago"
165
+
166
+ except (ValueError, TypeError):
167
+ return "unknown"
168
+
169
+
170
+ def _mask_api_key(api_key: str | None) -> str:
171
+ """Mask API key for display, showing only last 3 characters.
172
+
173
+ Args:
174
+ api_key: The API key to mask
175
+
176
+ Returns:
177
+ Masked API key string (e.g., "raxe_live_***abc")
178
+ """
179
+ if not api_key:
180
+ return "(not configured)"
181
+
182
+ if len(api_key) <= 6:
183
+ return "***"
184
+
185
+ # Show prefix and last 3 characters
186
+ if api_key.startswith("raxe_"):
187
+ parts = api_key.split("_")
188
+ if len(parts) >= 3:
189
+ return f"raxe_{parts[1]}_***{api_key[-3:]}"
190
+ return f"raxe_***{api_key[-3:]}"
191
+
192
+ return f"***{api_key[-3:]}"
193
+
194
+
195
+ def _get_tier_name(api_key: str | None) -> str:
196
+ """Determine the tier name from the API key prefix.
197
+
198
+ Args:
199
+ api_key: The API key to analyze
200
+
201
+ Returns:
202
+ Tier name string (e.g., "Pro tier", "Enterprise tier", "Free tier")
203
+ """
204
+ if not api_key:
205
+ return "Free tier"
206
+
207
+ if "enterprise" in api_key.lower():
208
+ return "Enterprise tier"
209
+ elif "pro" in api_key.lower():
210
+ return "Pro tier"
211
+ elif "test" in api_key.lower():
212
+ return "Test tier"
213
+
214
+ return "Pro tier"
215
+
216
+
217
+ def _get_queue_instance() -> DualQueue | None:
218
+ """Get the singleton DualQueue instance from the orchestrator.
219
+
220
+ This ensures all telemetry operations (enqueueing and flushing)
221
+ use the SAME queue instance, preventing events from being stuck.
222
+
223
+ Returns:
224
+ The orchestrator's DualQueue instance (singleton), or None if
225
+ telemetry is disabled or initialization failed.
226
+ """
227
+ from raxe.application.telemetry_orchestrator import get_orchestrator
228
+
229
+ orchestrator = get_orchestrator()
230
+ # Ensure orchestrator is initialized (creates the queue)
231
+ if orchestrator._queue is None:
232
+ if not orchestrator._ensure_initialized():
233
+ # Telemetry disabled or initialization failed
234
+ return None
235
+ return orchestrator._queue
236
+
237
+
238
+ def _get_config() -> RaxeConfig:
239
+ """Load RAXE configuration.
240
+
241
+ Returns:
242
+ Loaded RaxeConfig instance
243
+ """
244
+ return RaxeConfig.load()
245
+
246
+
247
+ def _check_telemetry_disable_permission() -> bool:
248
+ """Check if telemetry can be disabled based on cached server permissions.
249
+
250
+ Reads the cached permissions from the credential store. If no cached
251
+ permissions exist or they are stale, defaults to denying disable
252
+ (fail-safe behavior for CLI).
253
+
254
+ Returns:
255
+ True if telemetry can be disabled, False if tier does not allow it.
256
+
257
+ Note:
258
+ This uses cached server permissions from the last health check.
259
+ If the cache is stale (>24 hours), we deny disable as a safety measure.
260
+ """
261
+ try:
262
+ from raxe.infrastructure.telemetry.credential_store import CredentialStore
263
+
264
+ store = CredentialStore()
265
+ credentials = store.load()
266
+
267
+ if credentials is None:
268
+ # No credentials - deny (new users should not disable)
269
+ return False
270
+
271
+ # Check if we have cached permissions
272
+ if credentials.last_health_check is None:
273
+ # Never done health check - check tier from key type
274
+ # Temporary keys cannot disable telemetry
275
+ if credentials.key_type == "temporary":
276
+ return False
277
+ # Live/test keys might be able to - allow but server will enforce
278
+ return True
279
+
280
+ # If health check is stale (>24h), deny as safety measure
281
+ if credentials.is_health_check_stale(max_age_hours=24):
282
+ # Stale cache - deny to be safe
283
+ return False
284
+
285
+ # Use cached permission
286
+ return credentials.can_disable_telemetry
287
+
288
+ except Exception:
289
+ # On error, deny (fail-safe)
290
+ return False
291
+
292
+
293
+ def _get_cached_tier() -> str:
294
+ """Get the cached tier name from credentials.
295
+
296
+ Returns:
297
+ Tier name string (e.g., "Community", "Pro", "Enterprise")
298
+ """
299
+ try:
300
+ from raxe.infrastructure.telemetry.credential_store import CredentialStore
301
+
302
+ store = CredentialStore()
303
+ credentials = store.load()
304
+
305
+ if credentials is None:
306
+ return "Free"
307
+
308
+ # Capitalize tier for display
309
+ return credentials.tier.capitalize()
310
+
311
+ except Exception:
312
+ return "Unknown"
313
+
314
+
315
+ @click.group()
316
+ def telemetry() -> None:
317
+ """Manage telemetry settings and view status."""
318
+ pass
319
+
320
+
321
+ @telemetry.command("status")
322
+ @click.option(
323
+ "--format",
324
+ "output_format",
325
+ type=click.Choice(["text", "json"]),
326
+ default="text",
327
+ help="Output format (default: text)",
328
+ )
329
+ @click.option(
330
+ "--verbose",
331
+ "-v",
332
+ is_flag=True,
333
+ default=False,
334
+ help="Show total events (including telemetry) instead of just scans",
335
+ )
336
+ @handle_cli_error
337
+ def status(output_format: str, verbose: bool) -> None:
338
+ """Display telemetry status and queue statistics.
339
+
340
+ Shows comprehensive telemetry information including:
341
+ - Connection status and endpoint
342
+ - API key and tier information
343
+ - Circuit breaker state
344
+ - Queue depths and statistics
345
+ - Recent shipping activity
346
+
347
+ Examples:
348
+ raxe telemetry status
349
+ raxe telemetry status --format json
350
+ """
351
+ # Load configuration
352
+ config = _get_config()
353
+
354
+ # Get API key and installation_id using consistent helper
355
+ api_key, installation_id, api_key_source = _get_api_credentials()
356
+
357
+ # Get tier from credentials if available
358
+ tier_from_credentials: str | None = None
359
+ if api_key_source == "credentials":
360
+ try:
361
+ from raxe.infrastructure.telemetry.credential_store import CredentialStore
362
+ credential_store = CredentialStore()
363
+ credentials = credential_store.load()
364
+ if credentials:
365
+ tier_from_credentials = credentials.tier
366
+ except Exception:
367
+ pass
368
+
369
+ # Get queue stats
370
+ queue = _get_queue_instance()
371
+ stats = queue.get_stats()
372
+
373
+ # Resolve endpoint (fallback to centralized config if empty)
374
+ endpoint = config.telemetry.endpoint
375
+ if not endpoint:
376
+ from raxe.infrastructure.config.endpoints import get_telemetry_endpoint
377
+ endpoint = get_telemetry_endpoint()
378
+
379
+ # Get circuit breaker state
380
+ try:
381
+ sender = BatchSender(
382
+ endpoint=endpoint,
383
+ api_key=api_key,
384
+ installation_id=installation_id,
385
+ )
386
+ circuit_state = sender.get_circuit_state()
387
+ except Exception:
388
+ circuit_state = "unknown"
389
+
390
+ # Determine tier display
391
+ if tier_from_credentials:
392
+ tier_display = tier_from_credentials.capitalize() + " tier"
393
+ else:
394
+ tier_display = _get_tier_name(api_key)
395
+
396
+ # Build status data
397
+ status_data: dict[str, Any] = {
398
+ "endpoint": endpoint,
399
+ "schema_version": "0.0.1",
400
+ "api_key": _mask_api_key(api_key),
401
+ "tier": tier_display,
402
+ "telemetry_enabled": config.telemetry.enabled,
403
+ "circuit_breaker_state": circuit_state.upper(),
404
+ "queues": {
405
+ "critical": {
406
+ "count": stats.get("critical_count", 0),
407
+ "oldest": stats.get("oldest_critical"),
408
+ },
409
+ "standard": {
410
+ "count": stats.get("standard_count", 0),
411
+ "oldest": stats.get("oldest_standard"),
412
+ },
413
+ "dlq": {
414
+ "count": stats.get("dlq_count", 0),
415
+ },
416
+ },
417
+ "retry_pending": stats.get("retry_pending", 0),
418
+ "total_queued": stats.get("total_queued", 0),
419
+ "lifetime_stats": {
420
+ "scans_sent_total": stats.get("scans_sent_total", 0),
421
+ "events_sent_total": stats.get("events_sent_total", 0),
422
+ "batches_sent_total": stats.get("batches_sent_total", 0),
423
+ },
424
+ }
425
+
426
+ queue.close()
427
+
428
+ if output_format == "json":
429
+ console.print_json(data=status_data)
430
+ return
431
+
432
+ # Text output with tree-style formatting
433
+ from raxe.cli.branding import print_logo
434
+
435
+ print_logo(console, compact=True)
436
+ console.print()
437
+
438
+ console.print("[bold cyan]Telemetry Status[/bold cyan]")
439
+ console.print()
440
+
441
+ # Connection info
442
+ connection_status = "connected" if circuit_state == "closed" else "degraded"
443
+ endpoint_display = endpoint.replace("https://", "").split("/")[0]
444
+
445
+ content = Text()
446
+ content.append(f"Endpoint: {endpoint_display} ({connection_status})\n", style="white")
447
+ content.append("Schema Version: 1.0.0\n", style="white")
448
+ api_key_display = _mask_api_key(api_key)
449
+ content.append(f"API Key: {api_key_display} ({tier_display})\n", style="white")
450
+ telemetry_status = "Enabled" if config.telemetry.enabled else "Disabled"
451
+ telemetry_color = "green" if config.telemetry.enabled else "yellow"
452
+ content.append(f"Telemetry: {telemetry_status}\n", style=telemetry_color)
453
+ content.append("\n")
454
+
455
+ # Circuit breaker
456
+ if circuit_state == "closed":
457
+ cb_color = "green"
458
+ elif circuit_state == "half_open":
459
+ cb_color = "yellow"
460
+ else:
461
+ cb_color = "red"
462
+ content.append("Circuit Breaker: ", style="white")
463
+ content.append(f"{circuit_state.upper()}\n", style=cb_color)
464
+
465
+ # Queue depths
466
+ critical_count = stats.get("critical_count", 0)
467
+ standard_count = stats.get("standard_count", 0)
468
+ dlq_count = stats.get("dlq_count", 0)
469
+
470
+ oldest_critical = _format_relative_time(stats.get("oldest_critical"))
471
+ oldest_standard = _format_relative_time(stats.get("oldest_standard"))
472
+
473
+ critical_info = f"{critical_count:,} events"
474
+ if critical_count > 0:
475
+ critical_info += f" (oldest: {oldest_critical})"
476
+
477
+ standard_info = f"{standard_count:,} events"
478
+ if standard_count > 0:
479
+ standard_info += f" (oldest: {oldest_standard})"
480
+
481
+ content.append(f"Critical Queue: {critical_info}\n", style="white")
482
+ content.append(f"Standard Queue: {standard_info}\n", style="white")
483
+ dlq_style = "white" if dlq_count == 0 else "yellow"
484
+ content.append(f"Dead Letter Queue: {dlq_count} events\n", style=dlq_style)
485
+
486
+ # Lifetime stats
487
+ events_sent_total = stats.get("events_sent_total", 0)
488
+ scans_sent_total = stats.get("scans_sent_total", 0)
489
+ batches_sent_total = stats.get("batches_sent_total", 0)
490
+ content.append("\n")
491
+
492
+ # Show scans by default (matches Portal), show all events with --verbose
493
+ if verbose:
494
+ content.append(f"Events Sent (total): {events_sent_total:,}\n", style="green")
495
+ content.append(f" - Scans: {scans_sent_total:,}\n", style="dim")
496
+ content.append(f" - Telemetry: {events_sent_total - scans_sent_total:,}\n", style="dim")
497
+ else:
498
+ content.append(f"Scans Sent (total): {scans_sent_total:,}\n", style="green")
499
+ content.append(f"Batches Sent (total): {batches_sent_total:,}\n", style="dim")
500
+
501
+ console.print(Panel(content, border_style="cyan", padding=(1, 2)))
502
+ console.print()
503
+
504
+
505
+ @telemetry.group("dlq")
506
+ def dlq() -> None:
507
+ """Manage the Dead Letter Queue (DLQ)."""
508
+ pass
509
+
510
+
511
+ @dlq.command("list")
512
+ @click.option(
513
+ "--format",
514
+ "output_format",
515
+ type=click.Choice(["text", "json"]),
516
+ default="text",
517
+ help="Output format (default: text)",
518
+ )
519
+ @click.option(
520
+ "--limit",
521
+ type=int,
522
+ default=100,
523
+ help="Maximum number of events to list (default: 100)",
524
+ )
525
+ @handle_cli_error
526
+ def dlq_list(output_format: str, limit: int) -> None:
527
+ """List events in the Dead Letter Queue.
528
+
529
+ Shows failed events that have exceeded maximum retry attempts.
530
+ Use this to inspect failed events and decide whether to retry or clear them.
531
+
532
+ Examples:
533
+ raxe telemetry dlq list
534
+ raxe telemetry dlq list --format json
535
+ raxe telemetry dlq list --limit 50
536
+ """
537
+ queue = _get_queue_instance()
538
+ events = queue.get_dlq_events(limit=limit)
539
+ queue.close()
540
+
541
+ if output_format == "json":
542
+ console.print_json(data={"events": events, "count": len(events)})
543
+ return
544
+
545
+ # Text output
546
+ from raxe.cli.branding import print_logo
547
+
548
+ print_logo(console, compact=True)
549
+ console.print()
550
+
551
+ if not events:
552
+ console.print("[green]Dead Letter Queue is empty[/green]")
553
+ console.print()
554
+ return
555
+
556
+ console.print(f"[bold cyan]Dead Letter Queue ({len(events)} events)[/bold cyan]")
557
+ console.print()
558
+
559
+ # Create table
560
+ table = Table(show_header=True, header_style="bold cyan", border_style="dim")
561
+ table.add_column("Event ID", style="cyan", no_wrap=True, width=18)
562
+ table.add_column("Type", style="white", width=12)
563
+ table.add_column("Failed At", style="yellow", width=12)
564
+ table.add_column("Reason", style="red")
565
+
566
+ for event in events:
567
+ event_id = event.get("event_id", "unknown")
568
+ event_type = event.get("event_type", "unknown")
569
+ failed_at = _format_relative_time(event.get("failed_at"))
570
+ reason = event.get("failure_reason", "Unknown error")
571
+
572
+ # Build reason string
573
+ server_code = event.get("server_error_code")
574
+ server_msg = event.get("server_error_message")
575
+
576
+ if server_code:
577
+ if server_msg:
578
+ reason = f"{server_code}: {server_msg}"
579
+ else:
580
+ reason = f"HTTP {server_code}"
581
+ elif event.get("retry_count", 0) >= 3:
582
+ reason = f"Max retries exceeded ({event.get('retry_count', 0)})"
583
+
584
+ # Truncate event_id for display
585
+ event_id_display = f"{event_id[:12]}..." if len(event_id) > 15 else event_id
586
+
587
+ # Truncate reason if too long
588
+ if len(reason) > 40:
589
+ reason = reason[:37] + "..."
590
+
591
+ table.add_row(event_id_display, event_type, failed_at, reason)
592
+
593
+ console.print(table)
594
+ console.print()
595
+
596
+
597
+ @dlq.command("show")
598
+ @click.argument("event_id")
599
+ @click.option(
600
+ "--format",
601
+ "output_format",
602
+ type=click.Choice(["text", "json"]),
603
+ default="text",
604
+ help="Output format (default: text)",
605
+ )
606
+ @handle_cli_error
607
+ def dlq_show(event_id: str, output_format: str) -> None:
608
+ """Show details of a specific DLQ event.
609
+
610
+ Displays full information about a failed event including:
611
+ - Event metadata (ID, type, timestamps)
612
+ - Failure details (reason, error codes)
613
+ - Sanitized payload
614
+
615
+ Examples:
616
+ raxe telemetry dlq show evt_abc123
617
+ raxe telemetry dlq show evt_abc123 --format json
618
+ """
619
+ queue = _get_queue_instance()
620
+ events = queue.get_dlq_events(limit=1000)
621
+ queue.close()
622
+
623
+ # Find the matching event
624
+ event = None
625
+ for e in events:
626
+ if e.get("event_id", "").startswith(event_id) or e.get("event_id") == event_id:
627
+ event = e
628
+ break
629
+
630
+ if not event:
631
+ display_error("Event not found", f"No event found with ID: {event_id}")
632
+ return
633
+
634
+ if output_format == "json":
635
+ console.print_json(data=event)
636
+ return
637
+
638
+ # Text output
639
+ from raxe.cli.branding import print_logo
640
+
641
+ print_logo(console, compact=True)
642
+ console.print()
643
+
644
+ console.print(f"[bold cyan]Event: {event.get('event_id')}[/bold cyan]")
645
+ console.print()
646
+
647
+ # Event details
648
+ content = Text()
649
+ content.append(f"Type: {event.get('event_type', 'unknown')}\n", style="white")
650
+ content.append(f"Priority: {event.get('priority', 'unknown')}\n", style="white")
651
+ content.append(f"Created: {event.get('created_at', 'unknown')}\n", style="white")
652
+ content.append(f"Failed: {event.get('failed_at', 'unknown')}\n", style="yellow")
653
+ content.append(f"Retries: {event.get('retry_count', 0)}\n", style="white")
654
+ content.append("\n")
655
+
656
+ # Error details
657
+ server_code = event.get("server_error_code")
658
+ server_msg = event.get("server_error_message")
659
+ reason = event.get("failure_reason", "Unknown error")
660
+
661
+ content.append("[bold]Error Details:[/bold]\n", style="red")
662
+ if server_code:
663
+ content.append(f" HTTP Code: {server_code}\n", style="red")
664
+ if server_msg:
665
+ content.append(f" Server Message: {server_msg}\n", style="red")
666
+ content.append(f" Reason: {reason}\n", style="red")
667
+
668
+ console.print(Panel(content, border_style="cyan", padding=(1, 2)))
669
+ console.print()
670
+
671
+ # Sanitized payload
672
+ payload = event.get("payload", {})
673
+ if payload:
674
+ console.print("[bold cyan]Payload (sanitized):[/bold cyan]")
675
+ console.print_json(data=payload)
676
+ console.print()
677
+
678
+
679
+ @dlq.command("clear")
680
+ @click.option(
681
+ "--older-than",
682
+ "older_than",
683
+ type=str,
684
+ help="Only clear events older than this duration (e.g., 7d, 24h)",
685
+ )
686
+ @click.option(
687
+ "--force",
688
+ is_flag=True,
689
+ help="Skip confirmation prompt",
690
+ )
691
+ @handle_cli_error
692
+ def dlq_clear(older_than: str | None, force: bool) -> None:
693
+ """Clear events from the Dead Letter Queue.
694
+
695
+ Permanently removes failed events from the DLQ.
696
+ Use --older-than to selectively remove old events.
697
+
698
+ Examples:
699
+ raxe telemetry dlq clear
700
+ raxe telemetry dlq clear --older-than 7d
701
+ raxe telemetry dlq clear --force
702
+ """
703
+ queue = _get_queue_instance()
704
+
705
+ # Get current count
706
+ stats = queue.get_stats()
707
+ current_count = stats.get("dlq_count", 0)
708
+
709
+ if current_count == 0:
710
+ console.print("[green]Dead Letter Queue is already empty[/green]")
711
+ queue.close()
712
+ return
713
+
714
+ # Parse older_than if provided
715
+ older_than_days: int | None = None
716
+ if older_than:
717
+ older_than_days = _parse_duration(older_than)
718
+ if older_than_days is None:
719
+ display_error(
720
+ "Invalid duration format",
721
+ f"Got '{older_than}', expected format like '7d', '24h', or '30m'",
722
+ )
723
+ queue.close()
724
+ return
725
+
726
+ # Confirm action
727
+ if not force:
728
+ if older_than_days:
729
+ msg = f"Are you sure you want to delete events older than {older_than} from DLQ?"
730
+ else:
731
+ msg = f"Are you sure you want to permanently delete {current_count} failed events?"
732
+
733
+ if not click.confirm(msg):
734
+ console.print("[yellow]Operation cancelled[/yellow]")
735
+ queue.close()
736
+ return
737
+
738
+ # Clear events
739
+ cleared = queue.clear_dlq(older_than_days=older_than_days)
740
+ queue.close()
741
+
742
+ if older_than_days:
743
+ display_success(f"Cleared {cleared} events older than {older_than} from Dead Letter Queue.")
744
+ else:
745
+ display_success(f"Cleared {cleared} events from Dead Letter Queue.")
746
+
747
+
748
+ @dlq.command("retry")
749
+ @click.argument("event_id", required=False)
750
+ @click.option(
751
+ "--all",
752
+ "retry_all",
753
+ is_flag=True,
754
+ help="Retry all events in the DLQ",
755
+ )
756
+ @handle_cli_error
757
+ def dlq_retry(event_id: str | None, retry_all: bool) -> None:
758
+ """Retry failed events from the Dead Letter Queue.
759
+
760
+ Moves events back to the main queue for reprocessing.
761
+ Specify an event ID or use --all to retry all events.
762
+
763
+ Examples:
764
+ raxe telemetry dlq retry evt_abc123
765
+ raxe telemetry dlq retry --all
766
+ """
767
+ if not event_id and not retry_all:
768
+ display_error(
769
+ "No events specified",
770
+ "Provide an event ID or use --all to retry all events",
771
+ )
772
+ return
773
+
774
+ queue = _get_queue_instance()
775
+
776
+ if retry_all:
777
+ # Retry all events
778
+ count = queue.retry_dlq_events()
779
+ queue.close()
780
+
781
+ if count == 0:
782
+ console.print("[yellow]No events in Dead Letter Queue to retry[/yellow]")
783
+ else:
784
+ display_success(f"Moved {count} events from DLQ back to queue for retry.")
785
+ return
786
+
787
+ # Retry specific event
788
+ # First find the full event ID
789
+ events = queue.get_dlq_events(limit=1000)
790
+ full_event_id = None
791
+ for e in events:
792
+ if e.get("event_id", "").startswith(event_id) or e.get("event_id") == event_id:
793
+ full_event_id = e.get("event_id")
794
+ break
795
+
796
+ if not full_event_id:
797
+ display_error("Event not found", f"No event found with ID: {event_id}")
798
+ queue.close()
799
+ return
800
+
801
+ count = queue.retry_dlq_events([full_event_id])
802
+ queue.close()
803
+
804
+ if count > 0:
805
+ display_success(f"Moved event {full_event_id} back to queue for retry.")
806
+ else:
807
+ display_warning(f"Failed to retry event {full_event_id}.")
808
+
809
+
810
+ @telemetry.command("flush")
811
+ @click.option(
812
+ "--format",
813
+ "output_format",
814
+ type=click.Choice(["text", "json"]),
815
+ default="text",
816
+ help="Output format (default: text)",
817
+ )
818
+ @handle_cli_error
819
+ def flush(output_format: str) -> None:
820
+ """Flush telemetry queues immediately.
821
+
822
+ Forces immediate processing of all queued telemetry events.
823
+ Use this before shutting down or when you need immediate delivery.
824
+
825
+ Examples:
826
+ raxe telemetry flush
827
+ raxe telemetry flush --format json
828
+ """
829
+ import time
830
+
831
+ start_time = time.time()
832
+
833
+ # Get queue stats before flush
834
+ queue = _get_queue_instance()
835
+ stats_before = queue.get_stats()
836
+
837
+ critical_before = stats_before.get("critical_count", 0)
838
+ standard_before = stats_before.get("standard_count", 0)
839
+ total_before = critical_before + standard_before
840
+
841
+ if total_before == 0:
842
+ if output_format == "json":
843
+ console.print_json(
844
+ data={
845
+ "status": "ok",
846
+ "message": "No events to flush",
847
+ "critical_shipped": 0,
848
+ "standard_shipped": 0,
849
+ "total_shipped": 0,
850
+ "duration_seconds": 0,
851
+ }
852
+ )
853
+ else:
854
+ console.print("[green]No events to flush - queues are empty[/green]")
855
+ queue.close()
856
+ return
857
+
858
+ # Load config for sender
859
+ config = _get_config()
860
+
861
+ if not config.telemetry.enabled:
862
+ if output_format == "json":
863
+ console.print_json(
864
+ data={
865
+ "status": "error",
866
+ "message": "Telemetry is disabled",
867
+ }
868
+ )
869
+ else:
870
+ display_warning("Telemetry is disabled. Enable it to flush events.")
871
+ queue.close()
872
+ return
873
+
874
+ # Get API key and installation_id using consistent helper
875
+ api_key, installation_id, _ = _get_api_credentials()
876
+
877
+ # Get endpoint (fall back to centralized endpoints if config is empty)
878
+ endpoint = config.telemetry.endpoint
879
+ if not endpoint:
880
+ from raxe.infrastructure.config.endpoints import get_telemetry_endpoint
881
+ endpoint = get_telemetry_endpoint()
882
+
883
+ # Process critical events
884
+ critical_shipped = 0
885
+ standard_shipped = 0
886
+ errors: list[str] = []
887
+
888
+ try:
889
+ # NOTE: Don't pass api_key_id - let the sender compute it from api_key.
890
+ # This ensures events are tagged with the CURRENT key's ID, not a stale
891
+ # ID from queue state. The backend uses the authenticated key's ID for
892
+ # event correlation (key_info.key_id).
893
+ sender = BatchSender(
894
+ endpoint=endpoint,
895
+ api_key=api_key,
896
+ installation_id=installation_id,
897
+ )
898
+
899
+ # Flush critical queue
900
+ while True:
901
+ events = queue.dequeue_critical(batch_size=100)
902
+ if not events:
903
+ break
904
+
905
+ try:
906
+ sender.send_batch(events)
907
+ event_ids: list[str] = [
908
+ str(e.get("event_id")) for e in events if e.get("event_id") is not None
909
+ ]
910
+ queue.mark_batch_sent(event_ids)
911
+ critical_shipped += len(events)
912
+ except Exception as e:
913
+ errors.append(f"Critical batch failed: {e}")
914
+ event_ids_failed: list[str] = [
915
+ str(ev.get("event_id")) for ev in events if ev.get("event_id") is not None
916
+ ]
917
+ queue.mark_batch_failed(event_ids_failed, str(e), retry_delay_seconds=60)
918
+ break
919
+
920
+ # Flush standard queue
921
+ while True:
922
+ events = queue.dequeue_standard(batch_size=100)
923
+ if not events:
924
+ break
925
+
926
+ try:
927
+ sender.send_batch(events)
928
+ event_ids = [
929
+ str(e.get("event_id")) for e in events if e.get("event_id") is not None
930
+ ]
931
+ queue.mark_batch_sent(event_ids)
932
+ standard_shipped += len(events)
933
+ except Exception as e:
934
+ errors.append(f"Standard batch failed: {e}")
935
+ event_ids_failed = [
936
+ str(ev.get("event_id")) for ev in events if ev.get("event_id") is not None
937
+ ]
938
+ queue.mark_batch_failed(event_ids_failed, str(e), retry_delay_seconds=60)
939
+ break
940
+
941
+ except Exception as e:
942
+ errors.append(f"Sender initialization failed: {e}")
943
+
944
+ queue.close()
945
+ duration = time.time() - start_time
946
+
947
+ total_shipped = critical_shipped + standard_shipped
948
+
949
+ if output_format == "json":
950
+ console.print_json(
951
+ data={
952
+ "status": "ok" if not errors else "partial",
953
+ "critical_shipped": critical_shipped,
954
+ "standard_shipped": standard_shipped,
955
+ "total_shipped": total_shipped,
956
+ "duration_seconds": round(duration, 2),
957
+ "errors": errors if errors else None,
958
+ }
959
+ )
960
+ return
961
+
962
+ # Text output
963
+ from raxe.cli.branding import print_logo
964
+
965
+ print_logo(console, compact=True)
966
+ console.print()
967
+
968
+ console.print("[bold cyan]Flushing telemetry queues...[/bold cyan]")
969
+ console.print()
970
+
971
+ content = Text()
972
+ content.append(f"Critical queue: {critical_shipped} events shipped\n", style="white")
973
+ content.append(f"Standard queue: {standard_shipped} events shipped\n", style="white")
974
+ content.append(f"Total: {total_shipped} events shipped in {duration:.1f}s\n", style="green")
975
+
976
+ if errors:
977
+ content.append("\n")
978
+ content.append("[bold yellow]Warnings:[/bold yellow]\n", style="yellow")
979
+ for error in errors:
980
+ content.append(f" - {error}\n", style="yellow")
981
+
982
+ console.print(Panel(content, border_style="cyan" if not errors else "yellow", padding=(1, 2)))
983
+ console.print()
984
+
985
+
986
+ @telemetry.command("disable")
987
+ @handle_cli_error
988
+ def disable() -> None:
989
+ """Disable telemetry collection.
990
+
991
+ Note: Telemetry cannot be disabled on the free tier.
992
+ Telemetry helps improve RAXE for everyone and contains no personal data.
993
+
994
+ Examples:
995
+ raxe telemetry disable
996
+ """
997
+ config = _get_config()
998
+
999
+ # Check cached server permissions first
1000
+ can_disable = _check_telemetry_disable_permission()
1001
+
1002
+ if not can_disable:
1003
+ # Get tier for display
1004
+ tier = _get_cached_tier()
1005
+
1006
+ console.print()
1007
+ console.print(
1008
+ "[red bold]Error: Telemetry cannot be disabled on your current tier.[/red bold]"
1009
+ )
1010
+ console.print()
1011
+ console.print(f"[dim]Your tier: {tier}[/dim]")
1012
+ console.print()
1013
+ console.print("Telemetry helps improve RAXE for everyone and contains no personal data.")
1014
+ console.print(
1015
+ "Only anonymized detection metadata is collected (rule IDs, severity levels)."
1016
+ )
1017
+ console.print()
1018
+ from raxe.infrastructure.config.endpoints import get_console_url
1019
+ console_url = get_console_url()
1020
+ console.print(
1021
+ "[cyan]Upgrade to Pro at:[/cyan] "
1022
+ f"[blue underline]{console_url}/upgrade[/blue underline]"
1023
+ )
1024
+ console.print()
1025
+ return
1026
+
1027
+ # Update configuration
1028
+ config_path = Path.home() / ".raxe" / "config.yaml"
1029
+ config.telemetry.enabled = False
1030
+ config.save(config_path)
1031
+
1032
+ display_success("Telemetry disabled.")
1033
+ console.print()
1034
+ console.print("[dim]Note: No further telemetry events will be sent.[/dim]")
1035
+ console.print("[dim]Existing queued events will be preserved but not shipped.[/dim]")
1036
+ console.print()
1037
+
1038
+
1039
+ @telemetry.command("enable")
1040
+ @handle_cli_error
1041
+ def enable() -> None:
1042
+ """Enable telemetry collection.
1043
+
1044
+ Telemetry helps improve RAXE detection accuracy across the community.
1045
+ Only anonymized metadata is collected - no prompts, responses, or PII.
1046
+
1047
+ Examples:
1048
+ raxe telemetry enable
1049
+ """
1050
+ config_path = Path.home() / ".raxe" / "config.yaml"
1051
+ config = _get_config()
1052
+
1053
+ if config.telemetry.enabled:
1054
+ console.print("[green]Telemetry is already enabled.[/green]")
1055
+ return
1056
+
1057
+ config.telemetry.enabled = True
1058
+ config.save(config_path)
1059
+
1060
+ display_success("Telemetry enabled.")
1061
+ console.print()
1062
+ console.print("[dim]Privacy guarantee: Only anonymized detection metadata is collected.[/dim]")
1063
+ console.print("[dim]No prompts, responses, or personal data is ever transmitted.[/dim]")
1064
+ console.print()
1065
+
1066
+
1067
+ # Alias for 'set' command to match naming convention
1068
+ @telemetry.command("config")
1069
+ @click.argument("key")
1070
+ @click.argument("value")
1071
+ @handle_cli_error
1072
+ def telemetry_config(key: str, value: str) -> None:
1073
+ """Set telemetry configuration values.
1074
+
1075
+ Allows adjusting telemetry-specific settings without editing the config file.
1076
+
1077
+ Available keys:
1078
+ batch_size: Number of events per batch (default: 50)
1079
+ flush_interval: Seconds between flushes (default: 300)
1080
+
1081
+ Examples:
1082
+ raxe telemetry config batch_size 100
1083
+ raxe telemetry config flush_interval 60
1084
+ """
1085
+ config_path = Path.home() / ".raxe" / "config.yaml"
1086
+ config = _get_config()
1087
+
1088
+ # Map keys to config attributes
1089
+ key_map = {
1090
+ "batch_size": ("batch_size", int),
1091
+ "flush_interval": ("flush_interval", int),
1092
+ "endpoint": ("endpoint", str),
1093
+ }
1094
+
1095
+ if key not in key_map:
1096
+ valid_keys = ", ".join(key_map.keys())
1097
+ display_error(f"Unknown key: {key}", f"Valid keys: {valid_keys}")
1098
+ return
1099
+
1100
+ attr_name, value_type = key_map[key]
1101
+
1102
+ try:
1103
+ typed_value = value_type(value)
1104
+ except ValueError:
1105
+ display_error(f"Invalid value for {key}", f"Expected {value_type.__name__}, got: {value}")
1106
+ return
1107
+
1108
+ setattr(config.telemetry, attr_name, typed_value)
1109
+
1110
+ # Validate
1111
+ errors = config.validate()
1112
+ if errors:
1113
+ for error in errors:
1114
+ display_error("Validation error", error)
1115
+ return
1116
+
1117
+ config.save(config_path)
1118
+ display_success(f"Set telemetry.{key} = {value}")
1119
+
1120
+
1121
+ # =============================================================================
1122
+ # Endpoint Management Commands
1123
+ # =============================================================================
1124
+
1125
+
1126
+ @telemetry.group("endpoint")
1127
+ def endpoint() -> None:
1128
+ """Manage telemetry endpoint configuration.
1129
+
1130
+ Configure which backend endpoint the CLI uses for telemetry.
1131
+ Supports different environments (production, development, staging)
1132
+ and custom endpoint URLs.
1133
+ """
1134
+ pass
1135
+
1136
+
1137
+ @endpoint.command("show")
1138
+ @click.option(
1139
+ "--format",
1140
+ "output_format",
1141
+ type=click.Choice(["text", "json"]),
1142
+ default="text",
1143
+ help="Output format (default: text)",
1144
+ )
1145
+ @handle_cli_error
1146
+ def endpoint_show(output_format: str) -> None:
1147
+ """Show current endpoint configuration.
1148
+
1149
+ Displays the current telemetry endpoint URL and environment settings.
1150
+
1151
+ Examples:
1152
+ raxe telemetry endpoint show
1153
+ raxe telemetry endpoint show --format json
1154
+ """
1155
+ from raxe.infrastructure.config.endpoints import get_endpoint_info, Endpoint
1156
+
1157
+ info = get_endpoint_info()
1158
+
1159
+ if output_format == "json":
1160
+ console.print_json(data=info)
1161
+ return
1162
+
1163
+ # Text output
1164
+ from raxe.cli.branding import print_logo
1165
+
1166
+ print_logo(console, compact=True)
1167
+ console.print()
1168
+
1169
+ console.print("[bold cyan]Endpoint Configuration[/bold cyan]")
1170
+ console.print()
1171
+
1172
+ content = Text()
1173
+ content.append(f"Environment: {info['environment']}\n", style="green")
1174
+ content.append(f"Source: {info['environment_source']}\n", style="dim")
1175
+ content.append("\n")
1176
+
1177
+ content.append("[bold]Active Endpoints:[/bold]\n", style="cyan")
1178
+ for name, url in info["endpoints"].items():
1179
+ content.append(f" {name}: ", style="white")
1180
+ content.append(f"{url}\n", style="blue")
1181
+
1182
+ if info.get("overrides"):
1183
+ content.append("\n")
1184
+ content.append("[bold]Active Overrides:[/bold]\n", style="yellow")
1185
+ for name, url in info["overrides"].items():
1186
+ content.append(f" {name}: {url}\n", style="yellow")
1187
+
1188
+ if info.get("env_vars"):
1189
+ content.append("\n")
1190
+ content.append("[bold]Environment Variables:[/bold]\n", style="dim")
1191
+ for name, value in info["env_vars"].items():
1192
+ content.append(f" {name}: {value}\n", style="dim")
1193
+
1194
+ console.print(Panel(content, border_style="cyan", padding=(1, 2)))
1195
+ console.print()
1196
+
1197
+
1198
+ @endpoint.command("set")
1199
+ @click.argument("url")
1200
+ @handle_cli_error
1201
+ def endpoint_set(url: str) -> None:
1202
+ """Set a custom telemetry endpoint URL.
1203
+
1204
+ Overrides the default endpoint for telemetry transmission.
1205
+ Use this for enterprise deployments or custom backend installations.
1206
+
1207
+ Examples:
1208
+ raxe telemetry endpoint set https://custom.example.com/v1/telemetry
1209
+ """
1210
+ from raxe.infrastructure.config.endpoints import set_endpoint, Endpoint
1211
+
1212
+ try:
1213
+ set_endpoint(Endpoint.TELEMETRY, url)
1214
+ display_success(f"Telemetry endpoint set to: {url}")
1215
+ console.print()
1216
+ console.print("[dim]Note: This override is session-only.[/dim]")
1217
+ console.print("[dim]For persistent changes, set RAXE_TELEMETRY_ENDPOINT environment variable.[/dim]")
1218
+ console.print()
1219
+ except ValueError as e:
1220
+ display_error("Invalid URL", str(e))
1221
+
1222
+
1223
+ @endpoint.command("reset")
1224
+ @handle_cli_error
1225
+ def endpoint_reset() -> None:
1226
+ """Reset endpoint to default for current environment.
1227
+
1228
+ Clears any runtime overrides and returns to using the
1229
+ environment-default endpoint.
1230
+
1231
+ Examples:
1232
+ raxe telemetry endpoint reset
1233
+ """
1234
+ from raxe.infrastructure.config.endpoints import reset_endpoint, get_telemetry_endpoint, Endpoint
1235
+
1236
+ reset_endpoint(Endpoint.TELEMETRY)
1237
+ current = get_telemetry_endpoint()
1238
+ display_success(f"Endpoint reset to default: {current}")
1239
+
1240
+
1241
+ @endpoint.command("test")
1242
+ @click.option(
1243
+ "--timeout",
1244
+ type=float,
1245
+ default=10.0,
1246
+ help="Connection timeout in seconds (default: 10)",
1247
+ )
1248
+ @click.option(
1249
+ "--format",
1250
+ "output_format",
1251
+ type=click.Choice(["text", "json"]),
1252
+ default="text",
1253
+ help="Output format (default: text)",
1254
+ )
1255
+ @handle_cli_error
1256
+ def endpoint_test(timeout: float, output_format: str) -> None:
1257
+ """Test connectivity to all endpoints.
1258
+
1259
+ Performs DNS resolution and HTTP connectivity tests
1260
+ for each configured endpoint.
1261
+
1262
+ Examples:
1263
+ raxe telemetry endpoint test
1264
+ raxe telemetry endpoint test --timeout 5
1265
+ """
1266
+ from raxe.infrastructure.config.endpoints import test_all_endpoints, Endpoint
1267
+
1268
+ results = test_all_endpoints(timeout_seconds=timeout)
1269
+
1270
+ if output_format == "json":
1271
+ json_data = {
1272
+ ep.value: {
1273
+ "url": status.url,
1274
+ "dns_resolved": status.dns_resolved,
1275
+ "reachable": status.reachable,
1276
+ "response_time_ms": status.response_time_ms,
1277
+ "http_status": status.http_status,
1278
+ "error": status.error,
1279
+ }
1280
+ for ep, status in results.items()
1281
+ }
1282
+ console.print_json(data=json_data)
1283
+ return
1284
+
1285
+ # Text output
1286
+ from raxe.cli.branding import print_logo
1287
+
1288
+ print_logo(console, compact=True)
1289
+ console.print()
1290
+
1291
+ console.print("[bold cyan]Endpoint Connectivity Test[/bold cyan]")
1292
+ console.print()
1293
+
1294
+ table = Table(show_header=True, header_style="bold cyan", border_style="dim")
1295
+ table.add_column("Endpoint", style="cyan", no_wrap=True, width=15)
1296
+ table.add_column("DNS", style="white", width=5)
1297
+ table.add_column("HTTP", style="white", width=5)
1298
+ table.add_column("Time (ms)", style="white", width=10)
1299
+ table.add_column("Status", style="white")
1300
+
1301
+ for ep, status in results.items():
1302
+ dns_icon = "[green]✓[/green]" if status.dns_resolved else "[red]✗[/red]"
1303
+ http_icon = "[green]✓[/green]" if status.reachable else "[red]✗[/red]"
1304
+
1305
+ time_str = f"{status.response_time_ms:.0f}" if status.response_time_ms else "-"
1306
+
1307
+ if status.reachable and status.http_status:
1308
+ if status.http_status < 400:
1309
+ status_str = f"[green]{status.http_status} OK[/green]"
1310
+ elif status.http_status == 401:
1311
+ status_str = f"[yellow]{status.http_status} (needs auth)[/yellow]"
1312
+ else:
1313
+ status_str = f"[red]{status.http_status}[/red]"
1314
+ elif status.error:
1315
+ status_str = f"[red]{status.error[:30]}...[/red]" if len(status.error) > 30 else f"[red]{status.error}[/red]"
1316
+ else:
1317
+ status_str = "[dim]-[/dim]"
1318
+
1319
+ table.add_row(ep.value, dns_icon, http_icon, time_str, status_str)
1320
+
1321
+ console.print(table)
1322
+ console.print()
1323
+
1324
+ # Summary
1325
+ total = len(results)
1326
+ reachable = sum(1 for s in results.values() if s.reachable)
1327
+
1328
+ if reachable == total:
1329
+ console.print(f"[green]All {total} endpoints reachable[/green]")
1330
+ else:
1331
+ console.print(f"[yellow]{reachable}/{total} endpoints reachable[/yellow]")
1332
+ console.print()
1333
+
1334
+
1335
+ @endpoint.command("use")
1336
+ @click.argument(
1337
+ "environment",
1338
+ type=click.Choice(["production", "prod", "staging", "stage", "development", "dev", "local"]),
1339
+ )
1340
+ @handle_cli_error
1341
+ def endpoint_use(environment: str) -> None:
1342
+ """Switch to a predefined environment.
1343
+
1344
+ Changes all endpoints to use the specified environment's defaults.
1345
+
1346
+ Available environments:
1347
+ production (prod) - Production endpoints
1348
+ staging (stage) - Staging endpoints
1349
+ development (dev) - Development/test endpoints
1350
+ local - Local development server
1351
+
1352
+ Examples:
1353
+ raxe telemetry endpoint use dev
1354
+ raxe telemetry endpoint use production
1355
+ """
1356
+ from raxe.infrastructure.config.endpoints import (
1357
+ save_environment,
1358
+ Environment,
1359
+ get_endpoint_info,
1360
+ )
1361
+
1362
+ env_map = {
1363
+ "production": Environment.PRODUCTION,
1364
+ "prod": Environment.PRODUCTION,
1365
+ "staging": Environment.STAGING,
1366
+ "stage": Environment.STAGING,
1367
+ "development": Environment.DEVELOPMENT,
1368
+ "dev": Environment.DEVELOPMENT,
1369
+ "local": Environment.LOCAL,
1370
+ }
1371
+
1372
+ env = env_map[environment]
1373
+ save_environment(env) # Persist to config file
1374
+
1375
+ info = get_endpoint_info()
1376
+
1377
+ display_success(f"Switched to {env.value} environment")
1378
+ console.print()
1379
+ console.print("[bold]Active endpoints:[/bold]")
1380
+ for name, url in info["endpoints"].items():
1381
+ console.print(f" {name}: [blue]{url}[/blue]")
1382
+ console.print()
1383
+ console.print("[dim]Saved to ~/.raxe/config.yaml[/dim]")
1384
+ console.print()