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.
- raxe/__init__.py +101 -0
- raxe/application/__init__.py +48 -0
- raxe/application/ab_testing.py +170 -0
- raxe/application/analytics/__init__.py +30 -0
- raxe/application/analytics/achievement_service.py +444 -0
- raxe/application/analytics/repositories.py +172 -0
- raxe/application/analytics/retention_service.py +267 -0
- raxe/application/analytics/statistics_service.py +419 -0
- raxe/application/analytics/streak_service.py +283 -0
- raxe/application/apply_policy.py +291 -0
- raxe/application/eager_l2.py +503 -0
- raxe/application/preloader.py +353 -0
- raxe/application/scan_merger.py +321 -0
- raxe/application/scan_pipeline.py +1059 -0
- raxe/application/scan_pipeline_async.py +403 -0
- raxe/application/session_tracker.py +458 -0
- raxe/application/telemetry_manager.py +357 -0
- raxe/application/telemetry_orchestrator.py +1210 -0
- raxe/async_sdk/__init__.py +34 -0
- raxe/async_sdk/cache.py +286 -0
- raxe/async_sdk/client.py +556 -0
- raxe/async_sdk/wrappers/__init__.py +23 -0
- raxe/async_sdk/wrappers/openai.py +238 -0
- raxe/cli/__init__.py +21 -0
- raxe/cli/auth.py +1047 -0
- raxe/cli/branding.py +235 -0
- raxe/cli/config.py +334 -0
- raxe/cli/custom_rules.py +458 -0
- raxe/cli/doctor.py +686 -0
- raxe/cli/error_handler.py +665 -0
- raxe/cli/event.py +648 -0
- raxe/cli/exit_codes.py +57 -0
- raxe/cli/expiry_warning.py +302 -0
- raxe/cli/export.py +183 -0
- raxe/cli/history.py +247 -0
- raxe/cli/l2_formatter.py +872 -0
- raxe/cli/main.py +1137 -0
- raxe/cli/models.py +590 -0
- raxe/cli/output.py +403 -0
- raxe/cli/privacy.py +84 -0
- raxe/cli/profiler.py +262 -0
- raxe/cli/progress.py +379 -0
- raxe/cli/progress_context.py +101 -0
- raxe/cli/repl.py +394 -0
- raxe/cli/rules.py +542 -0
- raxe/cli/setup_wizard.py +721 -0
- raxe/cli/stats.py +292 -0
- raxe/cli/suppress.py +501 -0
- raxe/cli/telemetry.py +1384 -0
- raxe/cli/test.py +130 -0
- raxe/cli/tune.py +315 -0
- raxe/cli/validate.py +218 -0
- raxe/domain/__init__.py +30 -0
- raxe/domain/analytics/__init__.py +97 -0
- raxe/domain/analytics/achievements.py +306 -0
- raxe/domain/analytics/models.py +120 -0
- raxe/domain/analytics/retention.py +168 -0
- raxe/domain/analytics/statistics.py +207 -0
- raxe/domain/analytics/streaks.py +173 -0
- raxe/domain/engine/__init__.py +15 -0
- raxe/domain/engine/executor.py +396 -0
- raxe/domain/engine/matcher.py +212 -0
- raxe/domain/inline_suppression.py +176 -0
- raxe/domain/ml/__init__.py +133 -0
- raxe/domain/ml/embedding_cache.py +309 -0
- raxe/domain/ml/gemma_detector.py +921 -0
- raxe/domain/ml/gemma_models.py +346 -0
- raxe/domain/ml/l2_config.py +428 -0
- raxe/domain/ml/l2_output_schema.py +443 -0
- raxe/domain/ml/manifest_loader.py +309 -0
- raxe/domain/ml/manifest_schema.py +345 -0
- raxe/domain/ml/model_metadata.py +263 -0
- raxe/domain/ml/model_registry.py +786 -0
- raxe/domain/ml/protocol.py +282 -0
- raxe/domain/ml/scoring_models.py +419 -0
- raxe/domain/ml/stub_detector.py +397 -0
- raxe/domain/ml/threat_scorer.py +757 -0
- raxe/domain/ml/tokenizer_registry.py +372 -0
- raxe/domain/ml/voting/__init__.py +89 -0
- raxe/domain/ml/voting/config.py +595 -0
- raxe/domain/ml/voting/engine.py +465 -0
- raxe/domain/ml/voting/head_voters.py +378 -0
- raxe/domain/ml/voting/models.py +222 -0
- raxe/domain/models.py +82 -0
- raxe/domain/packs/__init__.py +17 -0
- raxe/domain/packs/models.py +304 -0
- raxe/domain/policies/__init__.py +20 -0
- raxe/domain/policies/evaluator.py +212 -0
- raxe/domain/policies/models.py +223 -0
- raxe/domain/rules/__init__.py +32 -0
- raxe/domain/rules/custom.py +286 -0
- raxe/domain/rules/models.py +273 -0
- raxe/domain/rules/schema.py +166 -0
- raxe/domain/rules/validator.py +556 -0
- raxe/domain/suppression.py +801 -0
- raxe/domain/suppression_factory.py +174 -0
- raxe/domain/telemetry/__init__.py +116 -0
- raxe/domain/telemetry/backpressure.py +424 -0
- raxe/domain/telemetry/event_creator.py +362 -0
- raxe/domain/telemetry/events.py +1282 -0
- raxe/domain/telemetry/priority.py +263 -0
- raxe/domain/telemetry/scan_telemetry_builder.py +670 -0
- raxe/infrastructure/__init__.py +25 -0
- raxe/infrastructure/analytics/__init__.py +18 -0
- raxe/infrastructure/analytics/aggregator.py +484 -0
- raxe/infrastructure/analytics/aggregator_optimized.py +184 -0
- raxe/infrastructure/analytics/engine.py +748 -0
- raxe/infrastructure/analytics/repository.py +409 -0
- raxe/infrastructure/analytics/streaks.py +467 -0
- raxe/infrastructure/analytics/views.py +178 -0
- raxe/infrastructure/cloud/__init__.py +9 -0
- raxe/infrastructure/config/__init__.py +56 -0
- raxe/infrastructure/config/endpoints.py +641 -0
- raxe/infrastructure/config/scan_config.py +352 -0
- raxe/infrastructure/config/yaml_config.py +459 -0
- raxe/infrastructure/database/__init__.py +10 -0
- raxe/infrastructure/database/connection.py +200 -0
- raxe/infrastructure/database/models.py +325 -0
- raxe/infrastructure/database/scan_history.py +764 -0
- raxe/infrastructure/ml/__init__.py +0 -0
- raxe/infrastructure/ml/download_progress.py +438 -0
- raxe/infrastructure/ml/model_downloader.py +457 -0
- raxe/infrastructure/models/__init__.py +16 -0
- raxe/infrastructure/models/discovery.py +461 -0
- raxe/infrastructure/packs/__init__.py +13 -0
- raxe/infrastructure/packs/loader.py +407 -0
- raxe/infrastructure/packs/registry.py +381 -0
- raxe/infrastructure/policies/__init__.py +16 -0
- raxe/infrastructure/policies/api_client.py +256 -0
- raxe/infrastructure/policies/validator.py +227 -0
- raxe/infrastructure/policies/yaml_loader.py +250 -0
- raxe/infrastructure/rules/__init__.py +18 -0
- raxe/infrastructure/rules/custom_loader.py +224 -0
- raxe/infrastructure/rules/versioning.py +222 -0
- raxe/infrastructure/rules/yaml_loader.py +286 -0
- raxe/infrastructure/security/__init__.py +31 -0
- raxe/infrastructure/security/auth.py +145 -0
- raxe/infrastructure/security/policy_validator.py +124 -0
- raxe/infrastructure/security/signatures.py +171 -0
- raxe/infrastructure/suppression/__init__.py +36 -0
- raxe/infrastructure/suppression/composite_repository.py +154 -0
- raxe/infrastructure/suppression/sqlite_repository.py +231 -0
- raxe/infrastructure/suppression/yaml_composite_repository.py +156 -0
- raxe/infrastructure/suppression/yaml_repository.py +510 -0
- raxe/infrastructure/telemetry/__init__.py +79 -0
- raxe/infrastructure/telemetry/acquisition.py +179 -0
- raxe/infrastructure/telemetry/config.py +254 -0
- raxe/infrastructure/telemetry/credential_store.py +947 -0
- raxe/infrastructure/telemetry/dual_queue.py +1123 -0
- raxe/infrastructure/telemetry/flush_helper.py +343 -0
- raxe/infrastructure/telemetry/flush_scheduler.py +776 -0
- raxe/infrastructure/telemetry/health_client.py +394 -0
- raxe/infrastructure/telemetry/hook.py +347 -0
- raxe/infrastructure/telemetry/queue.py +520 -0
- raxe/infrastructure/telemetry/sender.py +476 -0
- raxe/infrastructure/tracking/__init__.py +13 -0
- raxe/infrastructure/tracking/usage.py +389 -0
- raxe/integrations/__init__.py +55 -0
- raxe/integrations/availability.py +143 -0
- raxe/integrations/registry.py +122 -0
- raxe/integrations/utils.py +135 -0
- raxe/mcp/__init__.py +62 -0
- raxe/mcp/cli.py +97 -0
- raxe/mcp/server.py +409 -0
- raxe/monitoring/__init__.py +51 -0
- raxe/monitoring/metrics.py +372 -0
- raxe/monitoring/profiler.py +388 -0
- raxe/monitoring/server.py +136 -0
- raxe/packs/core/v1.0.0/pack.yaml +1394 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-001@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-006@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-014@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-017@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-022@1.0.0.yaml +67 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-023@1.0.0.yaml +91 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-024@1.0.0.yaml +80 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-025@1.0.0.yaml +81 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-026@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-027@1.0.0.yaml +77 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-028@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-029@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-030@1.0.0.yaml +55 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-033@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-034@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-035@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-046@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-047@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-048@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-049@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-050@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-068@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-078@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-2001@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-2004@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-201@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-202@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-203@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3007@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3016@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3026@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3027@1.0.0.yaml +64 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3028@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3029@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3030@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3031@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3032@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3033@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3034@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-79@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-80@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-81@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-82@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-83@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-84@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-85@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-86@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-87@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-88@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-89@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-90@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-91@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-92@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-93@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-94@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-95@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-96@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-97@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-98@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-007@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-015@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-016@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-017@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-021@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-022@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-023@1.0.0.yaml +78 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-024@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-025@1.0.0.yaml +93 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-026@1.0.0.yaml +81 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-027@1.0.0.yaml +82 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-028@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-033@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-036@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-037@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-052@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-054@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-056@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-065@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-075@1.0.0.yaml +45 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-079@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1080@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1090@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1104@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1105@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1112@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-201@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-202@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-203@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-204@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-205@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-206@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-207@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-208@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-209@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-210@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-211@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-212@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-213@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-214@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-215@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-216@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-217@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-218@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-219@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-220@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-221@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-222@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-223@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-224@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-225@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-226@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-227@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-228@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-229@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-230@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-231@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-232@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-233@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-234@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-235@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-236@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-237@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-238@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-013@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-019@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-020@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-024@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-029@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-038@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-044@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-067@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-069@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-100@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-101@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-102@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-103@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-104@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-105@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-106@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-107@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-108@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-109@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-110@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-111@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-112@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-113@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-114@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-115@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-116@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-117@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-118@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-119@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-120@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-201@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-203@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3004@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3006@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3011@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-5016@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-6001@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-6002@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-70@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-71@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-72@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-73@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-74@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-75@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-76@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-77@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-78@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-79@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-80@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-81@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-82@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-83@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-84@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-85@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-86@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-87@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-88@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-89@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-90@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-91@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-92@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-93@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-94@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-95@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-96@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-97@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-98@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-99@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-001@1.0.0.yaml +73 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-002@1.0.0.yaml +71 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-003@1.0.0.yaml +65 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-004@1.0.0.yaml +73 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-101@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-102@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-103@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-104@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-105@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-106@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-107@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-108@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-109@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-110@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-111@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-112@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-113@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-114@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-115@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-116@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-117@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-118@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-119@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-120@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-121@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-122@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-123@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-124@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-125@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-126@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-127@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-128@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-129@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-130@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-131@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-132@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-133@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-134@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-135@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-136@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-137@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-138@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-139@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-140@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-141@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-142@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-143@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-144@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-145@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-146@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-147@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-148@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-149@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-150@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-151@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-152@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-153@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-154@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-155@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-156@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-157@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-158@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-159@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-160@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-161@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-001@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-009@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-020@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-021@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-022@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-028@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-033@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-034@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-036@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-039@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-056@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-066@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-076@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-098@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-103@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-104@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-105@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-110@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-111@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-112@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-113@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-114@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-115@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-116@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-117@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-118@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-119@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-120@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-121@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-122@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-123@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-124@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-125@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-126@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-127@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-128@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-129@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-130@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-131@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-132@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-133@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-134@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-135@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-136@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-137@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-138@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-139@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-140@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-141@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-142@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-143@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-144@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-145@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-146@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-147@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-148@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-149@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-150@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-151@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-152@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-153@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-154@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-155@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-156@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-157@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-158@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-159@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-160@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-161@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-162@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-201@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-203@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-204@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-205@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-206@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-207@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-009@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-012@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-017@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-022@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-025@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-027@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-028@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-034@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-037@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-040@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-041@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-044@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-050@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-051@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-052@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-053@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-054@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-055@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-056@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-058@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2015@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2025@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2026@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2035@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2037@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2042@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3001@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3002@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3003@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3004@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3005@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3006@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3007@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3008@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3009@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3010@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3011@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3012@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3013@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3014@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3015@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3016@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3017@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3018@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3019@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3020@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3021@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3022@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3023@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3024@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3025@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3026@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3027@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3028@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3029@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3030@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3031@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3032@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3033@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3034@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3035@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3036@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3037@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3038@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3039@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3040@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3041@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3042@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3043@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3044@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3045@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3046@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3047@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3048@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3049@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3050@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3051@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3052@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3053@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3054@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3055@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3056@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3057@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3058@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3059@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3060@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3061@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3062@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3063@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3064@1.0.0.yaml +78 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3065@1.0.0.yaml +84 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3066@1.0.0.yaml +84 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3067@1.0.0.yaml +88 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3068@1.0.0.yaml +94 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3069@1.0.0.yaml +90 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3070@1.0.0.yaml +99 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3071@1.0.0.yaml +91 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3072@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3073@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3074@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3075@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3076@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3077@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3078@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3079@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3080@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3081@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3082@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3083@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3084@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3085@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-016@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-028@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-042@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-044@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-045@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-050@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-201@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3001@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3006@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3009@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3012@1.0.0.yaml +41 -0
- raxe/plugins/__init__.py +98 -0
- raxe/plugins/custom_rules.py +380 -0
- raxe/plugins/loader.py +389 -0
- raxe/plugins/manager.py +538 -0
- raxe/plugins/protocol.py +428 -0
- raxe/py.typed +0 -0
- raxe/sdk/__init__.py +77 -0
- raxe/sdk/agent_scanner.py +1918 -0
- raxe/sdk/client.py +1603 -0
- raxe/sdk/decorator.py +175 -0
- raxe/sdk/exceptions.py +859 -0
- raxe/sdk/integrations/__init__.py +277 -0
- raxe/sdk/integrations/agent_scanner.py +71 -0
- raxe/sdk/integrations/autogen.py +872 -0
- raxe/sdk/integrations/crewai.py +1368 -0
- raxe/sdk/integrations/dspy.py +845 -0
- raxe/sdk/integrations/extractors.py +363 -0
- raxe/sdk/integrations/huggingface.py +395 -0
- raxe/sdk/integrations/langchain.py +948 -0
- raxe/sdk/integrations/litellm.py +484 -0
- raxe/sdk/integrations/llamaindex.py +1049 -0
- raxe/sdk/integrations/portkey.py +831 -0
- raxe/sdk/suppression_context.py +215 -0
- raxe/sdk/wrappers/__init__.py +163 -0
- raxe/sdk/wrappers/anthropic.py +310 -0
- raxe/sdk/wrappers/openai.py +221 -0
- raxe/sdk/wrappers/vertexai.py +484 -0
- raxe/utils/__init__.py +12 -0
- raxe/utils/error_sanitizer.py +135 -0
- raxe/utils/logging.py +241 -0
- raxe/utils/performance.py +414 -0
- raxe/utils/profiler.py +339 -0
- raxe/utils/validators.py +170 -0
- raxe-0.4.6.dist-info/METADATA +471 -0
- raxe-0.4.6.dist-info/RECORD +668 -0
- raxe-0.4.6.dist-info/WHEEL +5 -0
- raxe-0.4.6.dist-info/entry_points.txt +2 -0
- raxe-0.4.6.dist-info/licenses/LICENSE +56 -0
- raxe-0.4.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQLite-based dual-priority queue for telemetry events.
|
|
3
|
+
|
|
4
|
+
This module implements a persistent, dual-priority event queue using SQLite.
|
|
5
|
+
Events are routed to critical or standard priority queues based on their type.
|
|
6
|
+
The queue provides state persistence, dead letter queue support, and graceful
|
|
7
|
+
degradation when the database is unavailable.
|
|
8
|
+
|
|
9
|
+
Key Features:
|
|
10
|
+
- Dual priority queues (critical, standard)
|
|
11
|
+
- SQLite with WAL mode for concurrency
|
|
12
|
+
- State persistence across restarts
|
|
13
|
+
- Dead letter queue for failed events
|
|
14
|
+
- Thread-safe operations
|
|
15
|
+
- Graceful degradation on database errors
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import sqlite3
|
|
23
|
+
import threading
|
|
24
|
+
from contextlib import contextmanager
|
|
25
|
+
from datetime import datetime, timedelta, timezone
|
|
26
|
+
from enum import Enum
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import TYPE_CHECKING, Any
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from collections.abc import Generator
|
|
32
|
+
|
|
33
|
+
from raxe.domain.telemetry.events import TelemetryEvent, event_to_dict
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class StateKey(str, Enum):
|
|
39
|
+
"""Enumeration of state keys for persistent telemetry state tracking.
|
|
40
|
+
|
|
41
|
+
These keys are used to track installation state, activation milestones,
|
|
42
|
+
and session information across restarts.
|
|
43
|
+
|
|
44
|
+
Activation keys are aligned with backend canonical values.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
INSTALLATION_FIRED = "installation_fired"
|
|
48
|
+
INSTALLATION_ID = "installation_id"
|
|
49
|
+
INSTALL_TIMESTAMP = "install_timestamp"
|
|
50
|
+
SESSION_COUNT = "session_count"
|
|
51
|
+
# Activation milestones (aligned with backend canonical values)
|
|
52
|
+
ACTIVATED_FIRST_SCAN = "activated_first_scan"
|
|
53
|
+
ACTIVATED_FIRST_THREAT = "activated_first_threat"
|
|
54
|
+
ACTIVATED_FIRST_BLOCK = "activated_first_block"
|
|
55
|
+
ACTIVATED_FIRST_CLI = "activated_first_cli"
|
|
56
|
+
ACTIVATED_FIRST_SDK = "activated_first_sdk"
|
|
57
|
+
ACTIVATED_FIRST_DECORATOR = "activated_first_decorator"
|
|
58
|
+
ACTIVATED_FIRST_WRAPPER = "activated_first_wrapper"
|
|
59
|
+
ACTIVATED_FIRST_LANGCHAIN = "activated_first_langchain"
|
|
60
|
+
ACTIVATED_FIRST_L2_DETECTION = "activated_first_l2_detection"
|
|
61
|
+
ACTIVATED_FIRST_CUSTOM_RULE = "activated_first_custom_rule"
|
|
62
|
+
# API key ID tracking for consistency across auth and telemetry flows
|
|
63
|
+
CURRENT_API_KEY_ID = "current_api_key_id"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class DualQueue:
|
|
67
|
+
"""
|
|
68
|
+
SQLite-based dual-priority queue for telemetry events.
|
|
69
|
+
|
|
70
|
+
Provides separate queues for critical and standard priority events with
|
|
71
|
+
persistent state storage, dead letter queue support, and graceful error handling.
|
|
72
|
+
|
|
73
|
+
Features:
|
|
74
|
+
- Dual priority queues (critical sent first, standard batched)
|
|
75
|
+
- WAL mode for concurrent access
|
|
76
|
+
- State persistence across restarts
|
|
77
|
+
- Dead letter queue for failed events after max retries
|
|
78
|
+
- Thread-safe operations with connection pooling
|
|
79
|
+
- Graceful degradation on database errors
|
|
80
|
+
|
|
81
|
+
Example:
|
|
82
|
+
>>> queue = DualQueue()
|
|
83
|
+
>>> event = create_scan_event(...)
|
|
84
|
+
>>> event_id = queue.enqueue(event)
|
|
85
|
+
>>> batch = queue.dequeue_critical(batch_size=50)
|
|
86
|
+
>>> queue.mark_batch_sent([e["event_id"] for e in batch])
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# Schema version for migrations
|
|
90
|
+
_SCHEMA_VERSION = 1
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
db_path: Path | None = None,
|
|
95
|
+
critical_max_size: int = 10_000,
|
|
96
|
+
standard_max_size: int = 50_000,
|
|
97
|
+
*,
|
|
98
|
+
max_retry_count: int = 3,
|
|
99
|
+
enable_wal: bool = True,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Initialize the dual-priority queue.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
db_path: Path to SQLite database (default: ~/.raxe/telemetry.db).
|
|
106
|
+
critical_max_size: Maximum events in critical queue before overflow.
|
|
107
|
+
standard_max_size: Maximum events in standard queue before overflow.
|
|
108
|
+
max_retry_count: Maximum retries before moving to dead letter queue.
|
|
109
|
+
enable_wal: Enable Write-Ahead Logging for better concurrency.
|
|
110
|
+
"""
|
|
111
|
+
self.db_path = db_path or Path.home() / ".raxe" / "telemetry.db"
|
|
112
|
+
self.critical_max_size = critical_max_size
|
|
113
|
+
self.standard_max_size = standard_max_size
|
|
114
|
+
self.max_retry_count = max_retry_count
|
|
115
|
+
self._enable_wal = enable_wal
|
|
116
|
+
self._lock = threading.Lock()
|
|
117
|
+
self._closed = False
|
|
118
|
+
|
|
119
|
+
# Ensure directory exists
|
|
120
|
+
try:
|
|
121
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
except OSError as e:
|
|
123
|
+
logger.error(f"Failed to create telemetry directory: {e}")
|
|
124
|
+
# Continue - we'll handle database errors gracefully
|
|
125
|
+
|
|
126
|
+
# Initialize database
|
|
127
|
+
self._init_database()
|
|
128
|
+
|
|
129
|
+
logger.debug(f"DualQueue initialized at {self.db_path}")
|
|
130
|
+
|
|
131
|
+
def _init_database(self) -> None:
|
|
132
|
+
"""Initialize database schema with all required tables and indexes."""
|
|
133
|
+
try:
|
|
134
|
+
with self._get_connection() as conn:
|
|
135
|
+
# Enable WAL mode for better concurrency
|
|
136
|
+
if self._enable_wal:
|
|
137
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
138
|
+
conn.execute("PRAGMA synchronous=NORMAL")
|
|
139
|
+
|
|
140
|
+
# Set busy timeout for concurrent access
|
|
141
|
+
conn.execute("PRAGMA busy_timeout=5000")
|
|
142
|
+
|
|
143
|
+
# Create events table with priority routing
|
|
144
|
+
conn.execute("""
|
|
145
|
+
CREATE TABLE IF NOT EXISTS telemetry_events (
|
|
146
|
+
event_id TEXT PRIMARY KEY,
|
|
147
|
+
event_type TEXT NOT NULL,
|
|
148
|
+
priority TEXT NOT NULL CHECK (priority IN ('critical', 'standard')),
|
|
149
|
+
payload TEXT NOT NULL,
|
|
150
|
+
created_at TEXT NOT NULL,
|
|
151
|
+
retry_count INTEGER DEFAULT 0,
|
|
152
|
+
retry_after TEXT,
|
|
153
|
+
batch_id TEXT
|
|
154
|
+
)
|
|
155
|
+
""")
|
|
156
|
+
|
|
157
|
+
# Create state flags table
|
|
158
|
+
conn.execute("""
|
|
159
|
+
CREATE TABLE IF NOT EXISTS telemetry_state (
|
|
160
|
+
key TEXT PRIMARY KEY,
|
|
161
|
+
value TEXT NOT NULL,
|
|
162
|
+
updated_at TEXT NOT NULL
|
|
163
|
+
)
|
|
164
|
+
""")
|
|
165
|
+
|
|
166
|
+
# Create dead letter queue
|
|
167
|
+
conn.execute("""
|
|
168
|
+
CREATE TABLE IF NOT EXISTS telemetry_dlq (
|
|
169
|
+
event_id TEXT PRIMARY KEY,
|
|
170
|
+
event_type TEXT NOT NULL,
|
|
171
|
+
priority TEXT NOT NULL,
|
|
172
|
+
payload TEXT NOT NULL,
|
|
173
|
+
created_at TEXT NOT NULL,
|
|
174
|
+
failed_at TEXT NOT NULL,
|
|
175
|
+
failure_reason TEXT,
|
|
176
|
+
retry_count INTEGER,
|
|
177
|
+
server_error_code TEXT,
|
|
178
|
+
server_error_message TEXT
|
|
179
|
+
)
|
|
180
|
+
""")
|
|
181
|
+
|
|
182
|
+
# Create stats table for tracking sent events
|
|
183
|
+
conn.execute("""
|
|
184
|
+
CREATE TABLE IF NOT EXISTS telemetry_stats (
|
|
185
|
+
stat_key TEXT PRIMARY KEY,
|
|
186
|
+
stat_value INTEGER DEFAULT 0,
|
|
187
|
+
updated_at TEXT NOT NULL
|
|
188
|
+
)
|
|
189
|
+
""")
|
|
190
|
+
|
|
191
|
+
# Initialize stats if not present
|
|
192
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
193
|
+
conn.execute("""
|
|
194
|
+
INSERT OR IGNORE INTO telemetry_stats (stat_key, stat_value, updated_at)
|
|
195
|
+
VALUES ('events_sent_total', 0, ?), ('batches_sent_total', 0, ?), ('scans_sent_total', 0, ?)
|
|
196
|
+
""", (now, now, now))
|
|
197
|
+
|
|
198
|
+
# Create indexes for efficient queries
|
|
199
|
+
conn.execute("""
|
|
200
|
+
CREATE INDEX IF NOT EXISTS idx_events_priority
|
|
201
|
+
ON telemetry_events(priority, created_at)
|
|
202
|
+
""")
|
|
203
|
+
|
|
204
|
+
conn.execute("""
|
|
205
|
+
CREATE INDEX IF NOT EXISTS idx_events_retry
|
|
206
|
+
ON telemetry_events(retry_after)
|
|
207
|
+
""")
|
|
208
|
+
|
|
209
|
+
conn.execute("""
|
|
210
|
+
CREATE INDEX IF NOT EXISTS idx_dlq_failed
|
|
211
|
+
ON telemetry_dlq(failed_at)
|
|
212
|
+
""")
|
|
213
|
+
|
|
214
|
+
conn.commit()
|
|
215
|
+
|
|
216
|
+
except sqlite3.Error as e:
|
|
217
|
+
logger.error(f"Failed to initialize database: {e}")
|
|
218
|
+
# Continue - graceful degradation
|
|
219
|
+
|
|
220
|
+
@contextmanager
|
|
221
|
+
def _get_connection(self) -> Generator[sqlite3.Connection, None, None]:
|
|
222
|
+
"""Get a database connection context manager.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
SQLite connection with proper cleanup.
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
sqlite3.Error: If connection cannot be established.
|
|
229
|
+
"""
|
|
230
|
+
conn = sqlite3.connect(str(self.db_path), timeout=10.0)
|
|
231
|
+
try:
|
|
232
|
+
yield conn
|
|
233
|
+
finally:
|
|
234
|
+
conn.close()
|
|
235
|
+
|
|
236
|
+
def enqueue(self, event: TelemetryEvent) -> str:
|
|
237
|
+
"""
|
|
238
|
+
Add an event to the appropriate priority queue.
|
|
239
|
+
|
|
240
|
+
Events are routed to critical or standard queue based on their priority
|
|
241
|
+
attribute. Queue overflow is handled by dropping oldest events from the
|
|
242
|
+
same priority.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
event: TelemetryEvent to enqueue.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Event ID of the enqueued event.
|
|
249
|
+
|
|
250
|
+
Example:
|
|
251
|
+
>>> event = create_scan_event(...)
|
|
252
|
+
>>> event_id = queue.enqueue(event)
|
|
253
|
+
"""
|
|
254
|
+
if self._closed:
|
|
255
|
+
logger.warning("Attempted to enqueue to closed queue")
|
|
256
|
+
return event.event_id
|
|
257
|
+
|
|
258
|
+
event_dict = event_to_dict(event)
|
|
259
|
+
priority = event.priority
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
with self._lock:
|
|
263
|
+
with self._get_connection() as conn:
|
|
264
|
+
# Check queue size for this priority
|
|
265
|
+
count = conn.execute(
|
|
266
|
+
"SELECT COUNT(*) FROM telemetry_events WHERE priority = ?",
|
|
267
|
+
(priority,),
|
|
268
|
+
).fetchone()[0]
|
|
269
|
+
|
|
270
|
+
max_size = (
|
|
271
|
+
self.critical_max_size if priority == "critical" else self.standard_max_size
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if count >= max_size:
|
|
275
|
+
# Handle overflow - drop oldest event from same priority
|
|
276
|
+
self._handle_overflow(conn, priority)
|
|
277
|
+
|
|
278
|
+
# Insert new event
|
|
279
|
+
conn.execute(
|
|
280
|
+
"""
|
|
281
|
+
INSERT INTO telemetry_events (
|
|
282
|
+
event_id, event_type, priority, payload, created_at
|
|
283
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
284
|
+
""",
|
|
285
|
+
(
|
|
286
|
+
event.event_id,
|
|
287
|
+
event.event_type,
|
|
288
|
+
priority,
|
|
289
|
+
json.dumps(event_dict["payload"]),
|
|
290
|
+
event.timestamp,
|
|
291
|
+
),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
conn.commit()
|
|
295
|
+
|
|
296
|
+
logger.debug(f"Enqueued {priority} event {event.event_id} ({event.event_type})")
|
|
297
|
+
|
|
298
|
+
except sqlite3.Error as e:
|
|
299
|
+
logger.error(f"Failed to enqueue event: {e}")
|
|
300
|
+
# Graceful degradation - event is lost but application continues
|
|
301
|
+
|
|
302
|
+
return event.event_id
|
|
303
|
+
|
|
304
|
+
def _handle_overflow(self, conn: sqlite3.Connection, priority: str) -> None:
|
|
305
|
+
"""Handle queue overflow by dropping oldest event from same priority.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
conn: Active database connection.
|
|
309
|
+
priority: Priority queue experiencing overflow.
|
|
310
|
+
"""
|
|
311
|
+
result = conn.execute(
|
|
312
|
+
"""
|
|
313
|
+
SELECT event_id FROM telemetry_events
|
|
314
|
+
WHERE priority = ?
|
|
315
|
+
ORDER BY created_at ASC
|
|
316
|
+
LIMIT 1
|
|
317
|
+
""",
|
|
318
|
+
(priority,),
|
|
319
|
+
).fetchone()
|
|
320
|
+
|
|
321
|
+
if result:
|
|
322
|
+
event_id = result[0]
|
|
323
|
+
conn.execute("DELETE FROM telemetry_events WHERE event_id = ?", (event_id,))
|
|
324
|
+
logger.warning(f"Dropped oldest {priority} event {event_id} due to queue overflow")
|
|
325
|
+
|
|
326
|
+
def dequeue_critical(self, batch_size: int = 100) -> list[dict[str, Any]]:
|
|
327
|
+
"""
|
|
328
|
+
Dequeue a batch of critical priority events.
|
|
329
|
+
|
|
330
|
+
Events are returned in FIFO order, skipping events with retry_after
|
|
331
|
+
in the future.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
batch_size: Maximum number of events to dequeue.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
List of event dictionaries ready for sending.
|
|
338
|
+
|
|
339
|
+
Example:
|
|
340
|
+
>>> events = queue.dequeue_critical(batch_size=50)
|
|
341
|
+
>>> for event in events:
|
|
342
|
+
... print(event["event_id"])
|
|
343
|
+
"""
|
|
344
|
+
return self._dequeue_by_priority("critical", batch_size)
|
|
345
|
+
|
|
346
|
+
def dequeue_standard(self, batch_size: int = 100) -> list[dict[str, Any]]:
|
|
347
|
+
"""
|
|
348
|
+
Dequeue a batch of standard priority events.
|
|
349
|
+
|
|
350
|
+
Events are returned in FIFO order, skipping events with retry_after
|
|
351
|
+
in the future.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
batch_size: Maximum number of events to dequeue.
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
List of event dictionaries ready for sending.
|
|
358
|
+
|
|
359
|
+
Example:
|
|
360
|
+
>>> events = queue.dequeue_standard(batch_size=100)
|
|
361
|
+
"""
|
|
362
|
+
return self._dequeue_by_priority("standard", batch_size)
|
|
363
|
+
|
|
364
|
+
def _dequeue_by_priority(self, priority: str, batch_size: int) -> list[dict[str, Any]]:
|
|
365
|
+
"""Dequeue events of a specific priority.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
priority: Priority level to dequeue ("critical" or "standard").
|
|
369
|
+
batch_size: Maximum number of events to return.
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
List of event dictionaries.
|
|
373
|
+
"""
|
|
374
|
+
if self._closed:
|
|
375
|
+
logger.warning("Attempted to dequeue from closed queue")
|
|
376
|
+
return []
|
|
377
|
+
|
|
378
|
+
events: list[dict[str, Any]] = []
|
|
379
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
380
|
+
|
|
381
|
+
try:
|
|
382
|
+
with self._lock:
|
|
383
|
+
with self._get_connection() as conn:
|
|
384
|
+
# Select events ready for processing (not in retry backoff)
|
|
385
|
+
cursor = conn.execute(
|
|
386
|
+
"""
|
|
387
|
+
SELECT event_id, event_type, priority, payload, created_at,
|
|
388
|
+
retry_count, retry_after, batch_id
|
|
389
|
+
FROM telemetry_events
|
|
390
|
+
WHERE priority = ?
|
|
391
|
+
AND batch_id IS NULL
|
|
392
|
+
AND (retry_after IS NULL OR retry_after <= ?)
|
|
393
|
+
ORDER BY created_at ASC
|
|
394
|
+
LIMIT ?
|
|
395
|
+
""",
|
|
396
|
+
(priority, now, batch_size),
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
for row in cursor:
|
|
400
|
+
event_dict = {
|
|
401
|
+
"event_id": row[0],
|
|
402
|
+
"event_type": row[1],
|
|
403
|
+
"priority": row[2],
|
|
404
|
+
"payload": json.loads(row[3]),
|
|
405
|
+
"timestamp": row[4],
|
|
406
|
+
"retry_count": row[5],
|
|
407
|
+
}
|
|
408
|
+
events.append(event_dict)
|
|
409
|
+
|
|
410
|
+
logger.debug(f"Dequeued {len(events)} {priority} events")
|
|
411
|
+
|
|
412
|
+
except sqlite3.Error as e:
|
|
413
|
+
logger.error(f"Failed to dequeue events: {e}")
|
|
414
|
+
|
|
415
|
+
return events
|
|
416
|
+
|
|
417
|
+
def mark_batch_sent(self, event_ids: list[str]) -> None:
|
|
418
|
+
"""
|
|
419
|
+
Mark events as successfully sent and remove from queue.
|
|
420
|
+
|
|
421
|
+
Also updates lifetime stats (events_sent_total, batches_sent_total).
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
event_ids: List of event IDs to mark as sent.
|
|
425
|
+
|
|
426
|
+
Example:
|
|
427
|
+
>>> queue.mark_batch_sent(["evt_abc123", "evt_def456"])
|
|
428
|
+
"""
|
|
429
|
+
if not event_ids:
|
|
430
|
+
return
|
|
431
|
+
|
|
432
|
+
if self._closed:
|
|
433
|
+
logger.warning("Attempted to mark batch on closed queue")
|
|
434
|
+
return
|
|
435
|
+
|
|
436
|
+
try:
|
|
437
|
+
with self._lock:
|
|
438
|
+
with self._get_connection() as conn:
|
|
439
|
+
# Security: Safe - placeholders constructed from fixed '?' list
|
|
440
|
+
placeholders = ",".join(["?"] * len(event_ids))
|
|
441
|
+
|
|
442
|
+
# Count scan events BEFORE deleting (for scans_sent_total stat)
|
|
443
|
+
count_sql = f"SELECT COUNT(*) FROM telemetry_events WHERE event_id IN ({placeholders}) AND event_type = 'scan'" # noqa: S608
|
|
444
|
+
scan_count = conn.execute(count_sql, event_ids).fetchone()[0]
|
|
445
|
+
|
|
446
|
+
# Delete events
|
|
447
|
+
sql = f"DELETE FROM telemetry_events WHERE event_id IN ({placeholders})" # noqa: S608
|
|
448
|
+
conn.execute(sql, event_ids)
|
|
449
|
+
|
|
450
|
+
# Update lifetime stats
|
|
451
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
452
|
+
conn.execute("""
|
|
453
|
+
UPDATE telemetry_stats
|
|
454
|
+
SET stat_value = stat_value + ?, updated_at = ?
|
|
455
|
+
WHERE stat_key = 'events_sent_total'
|
|
456
|
+
""", (len(event_ids), now))
|
|
457
|
+
conn.execute("""
|
|
458
|
+
UPDATE telemetry_stats
|
|
459
|
+
SET stat_value = stat_value + ?, updated_at = ?
|
|
460
|
+
WHERE stat_key = 'scans_sent_total'
|
|
461
|
+
""", (scan_count, now))
|
|
462
|
+
conn.execute("""
|
|
463
|
+
UPDATE telemetry_stats
|
|
464
|
+
SET stat_value = stat_value + 1, updated_at = ?
|
|
465
|
+
WHERE stat_key = 'batches_sent_total'
|
|
466
|
+
""", (now,))
|
|
467
|
+
|
|
468
|
+
conn.commit()
|
|
469
|
+
|
|
470
|
+
logger.debug(f"Marked {len(event_ids)} events ({scan_count} scans) as sent")
|
|
471
|
+
|
|
472
|
+
except sqlite3.Error as e:
|
|
473
|
+
logger.error(f"Failed to mark batch sent: {e}")
|
|
474
|
+
|
|
475
|
+
def mark_batch_failed(self, event_ids: list[str], error: str, retry_delay_seconds: int) -> None:
|
|
476
|
+
"""
|
|
477
|
+
Mark events as failed and schedule for retry.
|
|
478
|
+
|
|
479
|
+
Events exceeding max_retry_count are moved to dead letter queue.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
event_ids: List of event IDs that failed.
|
|
483
|
+
error: Error message describing failure.
|
|
484
|
+
retry_delay_seconds: Seconds to wait before retry.
|
|
485
|
+
|
|
486
|
+
Example:
|
|
487
|
+
>>> queue.mark_batch_failed(
|
|
488
|
+
... ["evt_abc123"],
|
|
489
|
+
... "Connection timeout",
|
|
490
|
+
... retry_delay_seconds=60,
|
|
491
|
+
... )
|
|
492
|
+
"""
|
|
493
|
+
if not event_ids:
|
|
494
|
+
return
|
|
495
|
+
|
|
496
|
+
if self._closed:
|
|
497
|
+
logger.warning("Attempted to mark batch failed on closed queue")
|
|
498
|
+
return
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
with self._lock:
|
|
502
|
+
with self._get_connection() as conn:
|
|
503
|
+
retry_after = datetime.now(timezone.utc) + timedelta(
|
|
504
|
+
seconds=retry_delay_seconds
|
|
505
|
+
)
|
|
506
|
+
retry_after_str = retry_after.isoformat()
|
|
507
|
+
now_str = datetime.now(timezone.utc).isoformat()
|
|
508
|
+
|
|
509
|
+
# Get current retry counts for all events
|
|
510
|
+
# Security: Safe - placeholders constructed from fixed '?' list
|
|
511
|
+
placeholders = ",".join(["?"] * len(event_ids))
|
|
512
|
+
sql = f"""
|
|
513
|
+
SELECT event_id, event_type, priority, payload, created_at, retry_count
|
|
514
|
+
FROM telemetry_events
|
|
515
|
+
WHERE event_id IN ({placeholders})
|
|
516
|
+
""" # noqa: S608
|
|
517
|
+
cursor = conn.execute(sql, event_ids)
|
|
518
|
+
|
|
519
|
+
dlq_events = []
|
|
520
|
+
retry_events = []
|
|
521
|
+
|
|
522
|
+
for row in cursor:
|
|
523
|
+
event_id = row[0]
|
|
524
|
+
retry_count = row[5]
|
|
525
|
+
|
|
526
|
+
if retry_count >= self.max_retry_count:
|
|
527
|
+
dlq_events.append(
|
|
528
|
+
{
|
|
529
|
+
"event_id": event_id,
|
|
530
|
+
"event_type": row[1],
|
|
531
|
+
"priority": row[2],
|
|
532
|
+
"payload": row[3],
|
|
533
|
+
"created_at": row[4],
|
|
534
|
+
"retry_count": retry_count,
|
|
535
|
+
}
|
|
536
|
+
)
|
|
537
|
+
else:
|
|
538
|
+
retry_events.append(event_id)
|
|
539
|
+
|
|
540
|
+
# Move max-retry events to dead letter queue
|
|
541
|
+
for dlq_event in dlq_events:
|
|
542
|
+
conn.execute(
|
|
543
|
+
"""
|
|
544
|
+
INSERT INTO telemetry_dlq (
|
|
545
|
+
event_id, event_type, priority, payload, created_at,
|
|
546
|
+
failed_at, failure_reason, retry_count
|
|
547
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
548
|
+
""",
|
|
549
|
+
(
|
|
550
|
+
dlq_event["event_id"],
|
|
551
|
+
dlq_event["event_type"],
|
|
552
|
+
dlq_event["priority"],
|
|
553
|
+
dlq_event["payload"],
|
|
554
|
+
dlq_event["created_at"],
|
|
555
|
+
now_str,
|
|
556
|
+
error,
|
|
557
|
+
dlq_event["retry_count"],
|
|
558
|
+
),
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
if dlq_events:
|
|
562
|
+
dlq_ids = [e["event_id"] for e in dlq_events]
|
|
563
|
+
placeholders = ",".join(["?"] * len(dlq_ids))
|
|
564
|
+
sql = f"DELETE FROM telemetry_events WHERE event_id IN ({placeholders})" # noqa: S608
|
|
565
|
+
conn.execute(sql, dlq_ids)
|
|
566
|
+
logger.warning(f"Moved {len(dlq_events)} events to dead letter queue")
|
|
567
|
+
|
|
568
|
+
# Update retry events
|
|
569
|
+
if retry_events:
|
|
570
|
+
placeholders = ",".join(["?"] * len(retry_events))
|
|
571
|
+
sql = f"""
|
|
572
|
+
UPDATE telemetry_events
|
|
573
|
+
SET batch_id = NULL,
|
|
574
|
+
retry_count = retry_count + 1,
|
|
575
|
+
retry_after = ?
|
|
576
|
+
WHERE event_id IN ({placeholders})
|
|
577
|
+
""" # noqa: S608
|
|
578
|
+
conn.execute(sql, [retry_after_str, *retry_events])
|
|
579
|
+
|
|
580
|
+
conn.commit()
|
|
581
|
+
|
|
582
|
+
logger.debug(
|
|
583
|
+
f"Marked {len(event_ids)} events as failed, "
|
|
584
|
+
f"{len(retry_events)} will retry, {len(dlq_events)} moved to DLQ"
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
except sqlite3.Error as e:
|
|
588
|
+
logger.error(f"Failed to mark batch failed: {e}")
|
|
589
|
+
|
|
590
|
+
def get_state(self, key: str | StateKey) -> str | None:
|
|
591
|
+
"""
|
|
592
|
+
Get a state value by key.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
key: State key (StateKey enum or string).
|
|
596
|
+
|
|
597
|
+
Returns:
|
|
598
|
+
State value if exists, None otherwise.
|
|
599
|
+
|
|
600
|
+
Example:
|
|
601
|
+
>>> if queue.get_state(StateKey.INSTALLATION_FIRED):
|
|
602
|
+
... print("Installation already fired")
|
|
603
|
+
"""
|
|
604
|
+
if self._closed:
|
|
605
|
+
return None
|
|
606
|
+
|
|
607
|
+
key_str = key.value if isinstance(key, StateKey) else key
|
|
608
|
+
|
|
609
|
+
try:
|
|
610
|
+
with self._get_connection() as conn:
|
|
611
|
+
result = conn.execute(
|
|
612
|
+
"SELECT value FROM telemetry_state WHERE key = ?", (key_str,)
|
|
613
|
+
).fetchone()
|
|
614
|
+
return result[0] if result else None
|
|
615
|
+
|
|
616
|
+
except sqlite3.Error as e:
|
|
617
|
+
logger.error(f"Failed to get state: {e}")
|
|
618
|
+
return None
|
|
619
|
+
|
|
620
|
+
def set_state(self, key: str | StateKey, value: str) -> None:
|
|
621
|
+
"""
|
|
622
|
+
Set a state value.
|
|
623
|
+
|
|
624
|
+
Args:
|
|
625
|
+
key: State key (StateKey enum or string).
|
|
626
|
+
value: Value to store.
|
|
627
|
+
|
|
628
|
+
Example:
|
|
629
|
+
>>> queue.set_state(StateKey.INSTALLATION_FIRED, "true")
|
|
630
|
+
"""
|
|
631
|
+
if self._closed:
|
|
632
|
+
logger.warning("Attempted to set state on closed queue")
|
|
633
|
+
return
|
|
634
|
+
|
|
635
|
+
key_str = key.value if isinstance(key, StateKey) else key
|
|
636
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
637
|
+
|
|
638
|
+
try:
|
|
639
|
+
with self._lock:
|
|
640
|
+
with self._get_connection() as conn:
|
|
641
|
+
conn.execute(
|
|
642
|
+
"""
|
|
643
|
+
INSERT OR REPLACE INTO telemetry_state (key, value, updated_at)
|
|
644
|
+
VALUES (?, ?, ?)
|
|
645
|
+
""",
|
|
646
|
+
(key_str, value, now),
|
|
647
|
+
)
|
|
648
|
+
conn.commit()
|
|
649
|
+
|
|
650
|
+
except sqlite3.Error as e:
|
|
651
|
+
logger.error(f"Failed to set state: {e}")
|
|
652
|
+
|
|
653
|
+
def has_state(self, key: str | StateKey) -> bool:
|
|
654
|
+
"""
|
|
655
|
+
Check if a state key exists.
|
|
656
|
+
|
|
657
|
+
Args:
|
|
658
|
+
key: State key to check.
|
|
659
|
+
|
|
660
|
+
Returns:
|
|
661
|
+
True if key exists, False otherwise.
|
|
662
|
+
|
|
663
|
+
Example:
|
|
664
|
+
>>> if not queue.has_state(StateKey.ACTIVATED_FIRST_SCAN):
|
|
665
|
+
... # Fire first scan activation event
|
|
666
|
+
... pass
|
|
667
|
+
"""
|
|
668
|
+
return self.get_state(key) is not None
|
|
669
|
+
|
|
670
|
+
def increment_state(self, key: str | StateKey, default: int = 0) -> int:
|
|
671
|
+
"""
|
|
672
|
+
Atomically increment a numeric state value.
|
|
673
|
+
|
|
674
|
+
Args:
|
|
675
|
+
key: State key to increment.
|
|
676
|
+
default: Default value if key doesn't exist.
|
|
677
|
+
|
|
678
|
+
Returns:
|
|
679
|
+
New value after increment.
|
|
680
|
+
|
|
681
|
+
Example:
|
|
682
|
+
>>> session_num = queue.increment_state(StateKey.SESSION_COUNT)
|
|
683
|
+
>>> print(f"Session #{session_num}")
|
|
684
|
+
"""
|
|
685
|
+
if self._closed:
|
|
686
|
+
logger.warning("Attempted to increment state on closed queue")
|
|
687
|
+
return default
|
|
688
|
+
|
|
689
|
+
key_str = key.value if isinstance(key, StateKey) else key
|
|
690
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
691
|
+
|
|
692
|
+
try:
|
|
693
|
+
with self._lock:
|
|
694
|
+
with self._get_connection() as conn:
|
|
695
|
+
# Get current value
|
|
696
|
+
result = conn.execute(
|
|
697
|
+
"SELECT value FROM telemetry_state WHERE key = ?", (key_str,)
|
|
698
|
+
).fetchone()
|
|
699
|
+
|
|
700
|
+
current = int(result[0]) if result else default
|
|
701
|
+
new_value = current + 1
|
|
702
|
+
|
|
703
|
+
conn.execute(
|
|
704
|
+
"""
|
|
705
|
+
INSERT OR REPLACE INTO telemetry_state (key, value, updated_at)
|
|
706
|
+
VALUES (?, ?, ?)
|
|
707
|
+
""",
|
|
708
|
+
(key_str, str(new_value), now),
|
|
709
|
+
)
|
|
710
|
+
conn.commit()
|
|
711
|
+
|
|
712
|
+
return new_value
|
|
713
|
+
|
|
714
|
+
except (sqlite3.Error, ValueError) as e:
|
|
715
|
+
logger.error(f"Failed to increment state: {e}")
|
|
716
|
+
return default
|
|
717
|
+
|
|
718
|
+
def move_to_dlq(
|
|
719
|
+
self,
|
|
720
|
+
event_id: str,
|
|
721
|
+
reason: str,
|
|
722
|
+
server_code: str | None = None,
|
|
723
|
+
server_message: str | None = None,
|
|
724
|
+
) -> None:
|
|
725
|
+
"""
|
|
726
|
+
Manually move an event to the dead letter queue.
|
|
727
|
+
|
|
728
|
+
Args:
|
|
729
|
+
event_id: Event ID to move.
|
|
730
|
+
reason: Reason for moving to DLQ.
|
|
731
|
+
server_code: HTTP error code from server.
|
|
732
|
+
server_message: Error message from server.
|
|
733
|
+
|
|
734
|
+
Example:
|
|
735
|
+
>>> queue.move_to_dlq(
|
|
736
|
+
... "evt_abc123",
|
|
737
|
+
... "Permanent failure",
|
|
738
|
+
... server_code="400",
|
|
739
|
+
... server_message="Invalid payload",
|
|
740
|
+
... )
|
|
741
|
+
"""
|
|
742
|
+
if self._closed:
|
|
743
|
+
logger.warning("Attempted to move to DLQ on closed queue")
|
|
744
|
+
return
|
|
745
|
+
|
|
746
|
+
now_str = datetime.now(timezone.utc).isoformat()
|
|
747
|
+
|
|
748
|
+
try:
|
|
749
|
+
with self._lock:
|
|
750
|
+
with self._get_connection() as conn:
|
|
751
|
+
# Get event from queue
|
|
752
|
+
result = conn.execute(
|
|
753
|
+
"""
|
|
754
|
+
SELECT event_id, event_type, priority, payload, created_at, retry_count
|
|
755
|
+
FROM telemetry_events
|
|
756
|
+
WHERE event_id = ?
|
|
757
|
+
""",
|
|
758
|
+
(event_id,),
|
|
759
|
+
).fetchone()
|
|
760
|
+
|
|
761
|
+
if not result:
|
|
762
|
+
logger.warning(f"Event {event_id} not found for DLQ move")
|
|
763
|
+
return
|
|
764
|
+
|
|
765
|
+
# Insert into DLQ
|
|
766
|
+
conn.execute(
|
|
767
|
+
"""
|
|
768
|
+
INSERT INTO telemetry_dlq (
|
|
769
|
+
event_id, event_type, priority, payload, created_at,
|
|
770
|
+
failed_at, failure_reason, retry_count,
|
|
771
|
+
server_error_code, server_error_message
|
|
772
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
773
|
+
""",
|
|
774
|
+
(
|
|
775
|
+
result[0], # event_id
|
|
776
|
+
result[1], # event_type
|
|
777
|
+
result[2], # priority
|
|
778
|
+
result[3], # payload
|
|
779
|
+
result[4], # created_at
|
|
780
|
+
now_str,
|
|
781
|
+
reason,
|
|
782
|
+
result[5], # retry_count
|
|
783
|
+
server_code,
|
|
784
|
+
server_message,
|
|
785
|
+
),
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
# Remove from main queue
|
|
789
|
+
conn.execute("DELETE FROM telemetry_events WHERE event_id = ?", (event_id,))
|
|
790
|
+
|
|
791
|
+
conn.commit()
|
|
792
|
+
|
|
793
|
+
logger.info(f"Moved event {event_id} to DLQ: {reason}")
|
|
794
|
+
|
|
795
|
+
except sqlite3.Error as e:
|
|
796
|
+
logger.error(f"Failed to move event to DLQ: {e}")
|
|
797
|
+
|
|
798
|
+
def get_dlq_events(self, limit: int = 100) -> list[dict[str, Any]]:
|
|
799
|
+
"""
|
|
800
|
+
Get events from the dead letter queue.
|
|
801
|
+
|
|
802
|
+
Args:
|
|
803
|
+
limit: Maximum number of events to return.
|
|
804
|
+
|
|
805
|
+
Returns:
|
|
806
|
+
List of DLQ event dictionaries.
|
|
807
|
+
|
|
808
|
+
Example:
|
|
809
|
+
>>> dlq_events = queue.get_dlq_events(limit=50)
|
|
810
|
+
>>> for event in dlq_events:
|
|
811
|
+
... print(f"{event['event_id']}: {event['failure_reason']}")
|
|
812
|
+
"""
|
|
813
|
+
if self._closed:
|
|
814
|
+
return []
|
|
815
|
+
|
|
816
|
+
events: list[dict[str, Any]] = []
|
|
817
|
+
|
|
818
|
+
try:
|
|
819
|
+
with self._get_connection() as conn:
|
|
820
|
+
cursor = conn.execute(
|
|
821
|
+
"""
|
|
822
|
+
SELECT event_id, event_type, priority, payload, created_at,
|
|
823
|
+
failed_at, failure_reason, retry_count,
|
|
824
|
+
server_error_code, server_error_message
|
|
825
|
+
FROM telemetry_dlq
|
|
826
|
+
ORDER BY failed_at DESC
|
|
827
|
+
LIMIT ?
|
|
828
|
+
""",
|
|
829
|
+
(limit,),
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
for row in cursor:
|
|
833
|
+
events.append(
|
|
834
|
+
{
|
|
835
|
+
"event_id": row[0],
|
|
836
|
+
"event_type": row[1],
|
|
837
|
+
"priority": row[2],
|
|
838
|
+
"payload": json.loads(row[3]),
|
|
839
|
+
"created_at": row[4],
|
|
840
|
+
"failed_at": row[5],
|
|
841
|
+
"failure_reason": row[6],
|
|
842
|
+
"retry_count": row[7],
|
|
843
|
+
"server_error_code": row[8],
|
|
844
|
+
"server_error_message": row[9],
|
|
845
|
+
}
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
except sqlite3.Error as e:
|
|
849
|
+
logger.error(f"Failed to get DLQ events: {e}")
|
|
850
|
+
|
|
851
|
+
return events
|
|
852
|
+
|
|
853
|
+
def retry_dlq_events(self, event_ids: list[str] | None = None) -> int:
|
|
854
|
+
"""
|
|
855
|
+
Move events from DLQ back to main queue for retry.
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
event_ids: Specific event IDs to retry, or None for all.
|
|
859
|
+
|
|
860
|
+
Returns:
|
|
861
|
+
Number of events moved back to queue.
|
|
862
|
+
|
|
863
|
+
Example:
|
|
864
|
+
>>> # Retry specific events
|
|
865
|
+
>>> count = queue.retry_dlq_events(["evt_abc123"])
|
|
866
|
+
>>> # Retry all DLQ events
|
|
867
|
+
>>> count = queue.retry_dlq_events()
|
|
868
|
+
"""
|
|
869
|
+
if self._closed:
|
|
870
|
+
logger.warning("Attempted to retry DLQ on closed queue")
|
|
871
|
+
return 0
|
|
872
|
+
|
|
873
|
+
moved_count = 0
|
|
874
|
+
|
|
875
|
+
try:
|
|
876
|
+
with self._lock:
|
|
877
|
+
with self._get_connection() as conn:
|
|
878
|
+
if event_ids:
|
|
879
|
+
# Retry specific events
|
|
880
|
+
# Security: Safe - placeholders constructed from fixed '?' list
|
|
881
|
+
placeholders = ",".join(["?"] * len(event_ids))
|
|
882
|
+
sql = f"""
|
|
883
|
+
SELECT event_id, event_type, priority, payload, created_at
|
|
884
|
+
FROM telemetry_dlq
|
|
885
|
+
WHERE event_id IN ({placeholders})
|
|
886
|
+
""" # noqa: S608
|
|
887
|
+
cursor = conn.execute(sql, event_ids)
|
|
888
|
+
else:
|
|
889
|
+
# Retry all events
|
|
890
|
+
cursor = conn.execute(
|
|
891
|
+
"""
|
|
892
|
+
SELECT event_id, event_type, priority, payload, created_at
|
|
893
|
+
FROM telemetry_dlq
|
|
894
|
+
"""
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
events_to_move = list(cursor)
|
|
898
|
+
|
|
899
|
+
for row in events_to_move:
|
|
900
|
+
# Insert back into main queue with reset retry count
|
|
901
|
+
conn.execute(
|
|
902
|
+
"""
|
|
903
|
+
INSERT INTO telemetry_events (
|
|
904
|
+
event_id, event_type, priority, payload, created_at, retry_count
|
|
905
|
+
) VALUES (?, ?, ?, ?, ?, 0)
|
|
906
|
+
""",
|
|
907
|
+
(row[0], row[1], row[2], row[3], row[4]),
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
# Remove from DLQ
|
|
911
|
+
conn.execute("DELETE FROM telemetry_dlq WHERE event_id = ?", (row[0],))
|
|
912
|
+
|
|
913
|
+
moved_count += 1
|
|
914
|
+
|
|
915
|
+
conn.commit()
|
|
916
|
+
|
|
917
|
+
if moved_count:
|
|
918
|
+
logger.info(f"Moved {moved_count} events from DLQ back to queue")
|
|
919
|
+
|
|
920
|
+
except sqlite3.Error as e:
|
|
921
|
+
logger.error(f"Failed to retry DLQ events: {e}")
|
|
922
|
+
|
|
923
|
+
return moved_count
|
|
924
|
+
|
|
925
|
+
def clear_dlq(self, older_than_days: int | None = None) -> int:
|
|
926
|
+
"""
|
|
927
|
+
Clear events from the dead letter queue.
|
|
928
|
+
|
|
929
|
+
Args:
|
|
930
|
+
older_than_days: Only clear events older than this many days,
|
|
931
|
+
or None to clear all.
|
|
932
|
+
|
|
933
|
+
Returns:
|
|
934
|
+
Number of events cleared.
|
|
935
|
+
|
|
936
|
+
Example:
|
|
937
|
+
>>> # Clear events older than 30 days
|
|
938
|
+
>>> cleared = queue.clear_dlq(older_than_days=30)
|
|
939
|
+
>>> # Clear all DLQ events
|
|
940
|
+
>>> cleared = queue.clear_dlq()
|
|
941
|
+
"""
|
|
942
|
+
if self._closed:
|
|
943
|
+
logger.warning("Attempted to clear DLQ on closed queue")
|
|
944
|
+
return 0
|
|
945
|
+
|
|
946
|
+
cleared_count = 0
|
|
947
|
+
|
|
948
|
+
try:
|
|
949
|
+
with self._lock:
|
|
950
|
+
with self._get_connection() as conn:
|
|
951
|
+
if older_than_days is not None:
|
|
952
|
+
cutoff = datetime.now(timezone.utc) - timedelta(days=older_than_days)
|
|
953
|
+
cutoff_str = cutoff.isoformat()
|
|
954
|
+
|
|
955
|
+
result = conn.execute(
|
|
956
|
+
"SELECT COUNT(*) FROM telemetry_dlq WHERE failed_at < ?",
|
|
957
|
+
(cutoff_str,),
|
|
958
|
+
).fetchone()
|
|
959
|
+
cleared_count = result[0] if result else 0
|
|
960
|
+
|
|
961
|
+
conn.execute(
|
|
962
|
+
"DELETE FROM telemetry_dlq WHERE failed_at < ?",
|
|
963
|
+
(cutoff_str,),
|
|
964
|
+
)
|
|
965
|
+
else:
|
|
966
|
+
result = conn.execute("SELECT COUNT(*) FROM telemetry_dlq").fetchone()
|
|
967
|
+
cleared_count = result[0] if result else 0
|
|
968
|
+
|
|
969
|
+
conn.execute("DELETE FROM telemetry_dlq")
|
|
970
|
+
|
|
971
|
+
conn.commit()
|
|
972
|
+
|
|
973
|
+
if cleared_count:
|
|
974
|
+
logger.info(f"Cleared {cleared_count} events from DLQ")
|
|
975
|
+
|
|
976
|
+
except sqlite3.Error as e:
|
|
977
|
+
logger.error(f"Failed to clear DLQ: {e}")
|
|
978
|
+
|
|
979
|
+
return cleared_count
|
|
980
|
+
|
|
981
|
+
def get_stats(self) -> dict[str, Any]:
|
|
982
|
+
"""
|
|
983
|
+
Get queue statistics.
|
|
984
|
+
|
|
985
|
+
Returns:
|
|
986
|
+
Dictionary with queue metrics including:
|
|
987
|
+
- critical_count: Events in critical queue
|
|
988
|
+
- standard_count: Events in standard queue
|
|
989
|
+
- dlq_count: Events in dead letter queue
|
|
990
|
+
- total_queued: Total events across all queues
|
|
991
|
+
- oldest_critical: Timestamp of oldest critical event
|
|
992
|
+
- oldest_standard: Timestamp of oldest standard event
|
|
993
|
+
- retry_pending: Events waiting for retry
|
|
994
|
+
|
|
995
|
+
Example:
|
|
996
|
+
>>> stats = queue.get_stats()
|
|
997
|
+
>>> print(f"Critical: {stats['critical_count']}")
|
|
998
|
+
>>> print(f"DLQ: {stats['dlq_count']}")
|
|
999
|
+
"""
|
|
1000
|
+
if self._closed:
|
|
1001
|
+
return {
|
|
1002
|
+
"critical_count": 0,
|
|
1003
|
+
"standard_count": 0,
|
|
1004
|
+
"dlq_count": 0,
|
|
1005
|
+
"total_queued": 0,
|
|
1006
|
+
"oldest_critical": None,
|
|
1007
|
+
"oldest_standard": None,
|
|
1008
|
+
"retry_pending": 0,
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
stats: dict[str, Any] = {}
|
|
1012
|
+
|
|
1013
|
+
try:
|
|
1014
|
+
with self._get_connection() as conn:
|
|
1015
|
+
# Count by priority
|
|
1016
|
+
critical_count = conn.execute(
|
|
1017
|
+
"SELECT COUNT(*) FROM telemetry_events WHERE priority = 'critical'"
|
|
1018
|
+
).fetchone()[0]
|
|
1019
|
+
|
|
1020
|
+
standard_count = conn.execute(
|
|
1021
|
+
"SELECT COUNT(*) FROM telemetry_events WHERE priority = 'standard'"
|
|
1022
|
+
).fetchone()[0]
|
|
1023
|
+
|
|
1024
|
+
dlq_count = conn.execute("SELECT COUNT(*) FROM telemetry_dlq").fetchone()[0]
|
|
1025
|
+
|
|
1026
|
+
# Get oldest events
|
|
1027
|
+
oldest_critical = conn.execute(
|
|
1028
|
+
"""
|
|
1029
|
+
SELECT MIN(created_at) FROM telemetry_events WHERE priority = 'critical'
|
|
1030
|
+
"""
|
|
1031
|
+
).fetchone()[0]
|
|
1032
|
+
|
|
1033
|
+
oldest_standard = conn.execute(
|
|
1034
|
+
"""
|
|
1035
|
+
SELECT MIN(created_at) FROM telemetry_events WHERE priority = 'standard'
|
|
1036
|
+
"""
|
|
1037
|
+
).fetchone()[0]
|
|
1038
|
+
|
|
1039
|
+
# Events in retry backoff
|
|
1040
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
1041
|
+
retry_pending = conn.execute(
|
|
1042
|
+
"""
|
|
1043
|
+
SELECT COUNT(*) FROM telemetry_events
|
|
1044
|
+
WHERE retry_after IS NOT NULL AND retry_after > ?
|
|
1045
|
+
""",
|
|
1046
|
+
(now,),
|
|
1047
|
+
).fetchone()[0]
|
|
1048
|
+
|
|
1049
|
+
# Get lifetime stats (events_sent_total, batches_sent_total, scans_sent_total)
|
|
1050
|
+
events_sent_total = 0
|
|
1051
|
+
batches_sent_total = 0
|
|
1052
|
+
scans_sent_total = 0
|
|
1053
|
+
try:
|
|
1054
|
+
result = conn.execute(
|
|
1055
|
+
"SELECT stat_key, stat_value FROM telemetry_stats"
|
|
1056
|
+
).fetchall()
|
|
1057
|
+
for row in result:
|
|
1058
|
+
if row[0] == "events_sent_total":
|
|
1059
|
+
events_sent_total = row[1]
|
|
1060
|
+
elif row[0] == "batches_sent_total":
|
|
1061
|
+
batches_sent_total = row[1]
|
|
1062
|
+
elif row[0] == "scans_sent_total":
|
|
1063
|
+
scans_sent_total = row[1]
|
|
1064
|
+
except sqlite3.Error:
|
|
1065
|
+
pass # Stats table might not exist in older DBs
|
|
1066
|
+
|
|
1067
|
+
stats = {
|
|
1068
|
+
"critical_count": critical_count,
|
|
1069
|
+
"standard_count": standard_count,
|
|
1070
|
+
"dlq_count": dlq_count,
|
|
1071
|
+
"total_queued": critical_count + standard_count,
|
|
1072
|
+
"oldest_critical": oldest_critical,
|
|
1073
|
+
"oldest_standard": oldest_standard,
|
|
1074
|
+
"retry_pending": retry_pending,
|
|
1075
|
+
"scans_sent_total": scans_sent_total,
|
|
1076
|
+
"events_sent_total": events_sent_total,
|
|
1077
|
+
"batches_sent_total": batches_sent_total,
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
except sqlite3.Error as e:
|
|
1081
|
+
logger.error(f"Failed to get stats: {e}")
|
|
1082
|
+
stats = {
|
|
1083
|
+
"critical_count": 0,
|
|
1084
|
+
"standard_count": 0,
|
|
1085
|
+
"dlq_count": 0,
|
|
1086
|
+
"total_queued": 0,
|
|
1087
|
+
"oldest_critical": None,
|
|
1088
|
+
"oldest_standard": None,
|
|
1089
|
+
"retry_pending": 0,
|
|
1090
|
+
"scans_sent_total": 0,
|
|
1091
|
+
"events_sent_total": 0,
|
|
1092
|
+
"batches_sent_total": 0,
|
|
1093
|
+
"error": str(e),
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
return stats
|
|
1097
|
+
|
|
1098
|
+
def close(self) -> None:
|
|
1099
|
+
"""
|
|
1100
|
+
Close the queue and release resources.
|
|
1101
|
+
|
|
1102
|
+
After closing, all operations will be no-ops and return empty/default values.
|
|
1103
|
+
|
|
1104
|
+
Example:
|
|
1105
|
+
>>> queue = DualQueue()
|
|
1106
|
+
>>> # ... use queue ...
|
|
1107
|
+
>>> queue.close()
|
|
1108
|
+
"""
|
|
1109
|
+
self._closed = True
|
|
1110
|
+
logger.debug("DualQueue closed")
|
|
1111
|
+
|
|
1112
|
+
def __enter__(self) -> DualQueue:
|
|
1113
|
+
"""Context manager entry."""
|
|
1114
|
+
return self
|
|
1115
|
+
|
|
1116
|
+
def __exit__(
|
|
1117
|
+
self,
|
|
1118
|
+
exc_type: type[BaseException] | None,
|
|
1119
|
+
exc_val: BaseException | None,
|
|
1120
|
+
exc_tb: Any,
|
|
1121
|
+
) -> None:
|
|
1122
|
+
"""Context manager exit."""
|
|
1123
|
+
self.close()
|