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
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()
|