patchi 0.7.5__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.
- patchi/__init__.py +1 -0
- patchi/__main__.py +13 -0
- patchi/cli/__init__.py +0 -0
- patchi/cli/command_families.py +235 -0
- patchi/cli/commands/__init__.py +0 -0
- patchi/cli/commands/access_cmd.py +65 -0
- patchi/cli/commands/agent_stats_cmd.py +273 -0
- patchi/cli/commands/agents_cmd.py +187 -0
- patchi/cli/commands/ai_cmd.py +308 -0
- patchi/cli/commands/assure_cmd.py +538 -0
- patchi/cli/commands/audit_cmd.py +286 -0
- patchi/cli/commands/auto_cmd.py +110 -0
- patchi/cli/commands/blame_cmd.py +53 -0
- patchi/cli/commands/chains_cmd.py +327 -0
- patchi/cli/commands/charter_cmd.py +173 -0
- patchi/cli/commands/chat_cmd.py +597 -0
- patchi/cli/commands/check_cmd.py +232 -0
- patchi/cli/commands/cleanup_cmd.py +328 -0
- patchi/cli/commands/cockpit_cmd.py +208 -0
- patchi/cli/commands/commands_cmd.py +160 -0
- patchi/cli/commands/cross_repo_cmd.py +113 -0
- patchi/cli/commands/deps_cmd.py +110 -0
- patchi/cli/commands/dev_check_cmd.py +342 -0
- patchi/cli/commands/dev_cmd.py +445 -0
- patchi/cli/commands/doctor_cmd.py +457 -0
- patchi/cli/commands/findings_cmd.py +187 -0
- patchi/cli/commands/fix_cmd.py +648 -0
- patchi/cli/commands/fix_review_cmd.py +273 -0
- patchi/cli/commands/heatmap_cmd.py +62 -0
- patchi/cli/commands/help_cmd.py +159 -0
- patchi/cli/commands/hosted_cmd.py +980 -0
- patchi/cli/commands/init.py +456 -0
- patchi/cli/commands/key_cmd.py +571 -0
- patchi/cli/commands/learn_cmd.py +140 -0
- patchi/cli/commands/link_cmd.py +104 -0
- patchi/cli/commands/log_cmd.py +57 -0
- patchi/cli/commands/memory_cmd.py +215 -0
- patchi/cli/commands/model_cmd.py +207 -0
- patchi/cli/commands/notify_cmd.py +213 -0
- patchi/cli/commands/notify_prompts.py +29 -0
- patchi/cli/commands/patch_cmd.py +231 -0
- patchi/cli/commands/plan_cmd.py +128 -0
- patchi/cli/commands/plugins_cmd.py +307 -0
- patchi/cli/commands/queue_cmd.py +152 -0
- patchi/cli/commands/quick_cmd.py +159 -0
- patchi/cli/commands/ready_cmd.py +292 -0
- patchi/cli/commands/report_cmd.py +416 -0
- patchi/cli/commands/restrict_cmd.py +127 -0
- patchi/cli/commands/review_cmd.py +225 -0
- patchi/cli/commands/rules_cmd.py +233 -0
- patchi/cli/commands/scan_cmd.py +1908 -0
- patchi/cli/commands/settings_cmd.py +45 -0
- patchi/cli/commands/status_cmd.py +679 -0
- patchi/cli/commands/test_cmd.py +1052 -0
- patchi/cli/commands/trend_cmd.py +296 -0
- patchi/cli/commands/undo_cmd.py +235 -0
- patchi/cli/commands/update_cmd.py +345 -0
- patchi/cli/commands/verify_cmd.py +56 -0
- patchi/cli/commands/vr_cmd.py +440 -0
- patchi/cli/commands/watch_cmd.py +304 -0
- patchi/cli/commands/web_cmd.py +114 -0
- patchi/cli/console.py +101 -0
- patchi/cli/display/__init__.py +1 -0
- patchi/cli/display/live_audit.py +269 -0
- patchi/cli/display/live_progress.py +169 -0
- patchi/cli/framework.py +241 -0
- patchi/cli/logo.py +128 -0
- patchi/cli/main.py +222 -0
- patchi/cli/registry.py +1171 -0
- patchi/cli/style.py +68 -0
- patchi/cli/themes.py +187 -0
- patchi/cli/ux.py +444 -0
- patchi/core/__init__.py +0 -0
- patchi/core/agents/__init__.py +0 -0
- patchi/core/agents/api_fuzzer_agent.py +141 -0
- patchi/core/agents/attack_agent.py +242 -0
- patchi/core/agents/base.py +1012 -0
- patchi/core/agents/bug_predictor_agent.py +58 -0
- patchi/core/agents/build_tool_validator.py +150 -0
- patchi/core/agents/cache.py +192 -0
- patchi/core/agents/chaos_agent.py +133 -0
- patchi/core/agents/cicd_generator.py +240 -0
- patchi/core/agents/comment_scanner.py +280 -0
- patchi/core/agents/console_logging_agent.py +156 -0
- patchi/core/agents/coordinator.py +858 -0
- patchi/core/agents/core_scanner.py +269 -0
- patchi/core/agents/coverage_prioritizer.py +181 -0
- patchi/core/agents/dead_code_hygiene.py +296 -0
- patchi/core/agents/dead_code_scanner.py +692 -0
- patchi/core/agents/dependency_scanner.py +725 -0
- patchi/core/agents/doc_claim_agent.py +573 -0
- patchi/core/agents/duplicate_scanner.py +568 -0
- patchi/core/agents/env_scanner.py +410 -0
- patchi/core/agents/feature_flag_agent.py +89 -0
- patchi/core/agents/frontend_framework_agent.py +133 -0
- patchi/core/agents/goroutine_agent.py +82 -0
- patchi/core/agents/governor.py +1972 -0
- patchi/core/agents/i18n_agent.py +95 -0
- patchi/core/agents/license_compliance.py +247 -0
- patchi/core/agents/linking_agent.py +251 -0
- patchi/core/agents/memory_profiler_agent.py +102 -0
- patchi/core/agents/modernization_agent.py +84 -0
- patchi/core/agents/mutation_agent.py +174 -0
- patchi/core/agents/nl_query_agent.py +74 -0
- patchi/core/agents/promise_rejection_agent.py +100 -0
- patchi/core/agents/refactoring_agent.py +286 -0
- patchi/core/agents/resource_leak_agent.py +91 -0
- patchi/core/agents/route_graph_scanner.py +951 -0
- patchi/core/agents/run_agent.py +106 -0
- patchi/core/agents/rust_unwrap_agent.py +100 -0
- patchi/core/agents/sbom_generator.py +284 -0
- patchi/core/agents/scanners.py +67 -0
- patchi/core/agents/side/__init__.py +1 -0
- patchi/core/agents/side/build_agent.py +65 -0
- patchi/core/agents/side/format_agent.py +50 -0
- patchi/core/agents/side/install_agent.py +63 -0
- patchi/core/agents/side_file_scanner.py +549 -0
- patchi/core/agents/snapshot_drift_detector.py +146 -0
- patchi/core/agents/spa_route_inventory.py +203 -0
- patchi/core/agents/test_scanner.py +442 -0
- patchi/core/agents/tool_health.py +109 -0
- patchi/core/agents/tool_runner.py +162 -0
- patchi/core/agents/type_scanner.py +168 -0
- patchi/core/agents/ui_scanner.py +558 -0
- patchi/core/ai/__init__.py +20 -0
- patchi/core/ai/agent_audit.py +153 -0
- patchi/core/ai/agent_profiler.py +277 -0
- patchi/core/ai/client.py +499 -0
- patchi/core/ai/cost_tracker.py +107 -0
- patchi/core/ai/model_router.py +318 -0
- patchi/core/ai/orchestrator.py +625 -0
- patchi/core/ai/prompts.py +681 -0
- patchi/core/ai/smart.py +302 -0
- patchi/core/ai/tool_executor.py +24 -0
- patchi/core/ai/tools/__init__.py +10 -0
- patchi/core/ai/tools/executor.py +621 -0
- patchi/core/ai/tools/realize.py +1160 -0
- patchi/core/ai/tools/registry.py +1577 -0
- patchi/core/assurance/__init__.py +33 -0
- patchi/core/assurance/builder.py +211 -0
- patchi/core/assurance/graph.py +342 -0
- patchi/core/assurance/invariants.py +168 -0
- patchi/core/atomic.py +61 -0
- patchi/core/attackers/__init__.py +17 -0
- patchi/core/attackers/api_abuse.py +89 -0
- patchi/core/attackers/auth_bypass.py +73 -0
- patchi/core/attackers/base.py +91 -0
- patchi/core/attackers/business_logic.py +88 -0
- patchi/core/attackers/planner.py +108 -0
- patchi/core/attackers/priv_escalation.py +88 -0
- patchi/core/attackers/recon.py +87 -0
- patchi/core/brain/__init__.py +0 -0
- patchi/core/brain/ast_utils/__init__.py +81 -0
- patchi/core/brain/ast_utils/assignments.py +219 -0
- patchi/core/brain/ast_utils/calls.py +139 -0
- patchi/core/brain/ast_utils/config.py +132 -0
- patchi/core/brain/ast_utils/control_flow.py +74 -0
- patchi/core/brain/ast_utils/dead_symbols.py +205 -0
- patchi/core/brain/ast_utils/decorators.py +108 -0
- patchi/core/brain/ast_utils/helpers.py +144 -0
- patchi/core/brain/ast_utils/imports.py +169 -0
- patchi/core/brain/ast_utils/scan.py +143 -0
- patchi/core/brain/ast_utils/taint.py +310 -0
- patchi/core/brain/audit.py +379 -0
- patchi/core/brain/baseline.py +195 -0
- patchi/core/brain/blast_radius.py +312 -0
- patchi/core/brain/body_tags.py +144 -0
- patchi/core/brain/brain.py +1161 -0
- patchi/core/brain/brain_context.py +393 -0
- patchi/core/brain/brain_watcher.py +340 -0
- patchi/core/brain/charter.py +473 -0
- patchi/core/brain/classifier.py +214 -0
- patchi/core/brain/cockpit.py +350 -0
- patchi/core/brain/code_query.py +710 -0
- patchi/core/brain/contract.py +889 -0
- patchi/core/brain/contract_diff.py +155 -0
- patchi/core/brain/council.py +800 -0
- patchi/core/brain/cross_repo.py +104 -0
- patchi/core/brain/dead_code_tools.py +157 -0
- patchi/core/brain/doc_validator.py +197 -0
- patchi/core/brain/domain_activator.py +2183 -0
- patchi/core/brain/enriched_context.py +220 -0
- patchi/core/brain/file_corpus.py +220 -0
- patchi/core/brain/framework.py +953 -0
- patchi/core/brain/freshness.py +273 -0
- patchi/core/brain/git_aware.py +206 -0
- patchi/core/brain/heatmap.py +104 -0
- patchi/core/brain/ignore_parser.py +153 -0
- patchi/core/brain/import_graph.py +486 -0
- patchi/core/brain/languages.py +397 -0
- patchi/core/brain/layered_brain.py +419 -0
- patchi/core/brain/learning.py +229 -0
- patchi/core/brain/orphaned_endpoints.py +313 -0
- patchi/core/brain/personas/__init__.py +648 -0
- patchi/core/brain/personas/bad_user.py +165 -0
- patchi/core/brain/personas/base.py +349 -0
- patchi/core/brain/proactive.py +807 -0
- patchi/core/brain/project_context.py +121 -0
- patchi/core/brain/project_reader.py +521 -0
- patchi/core/brain/reasoning.py +269 -0
- patchi/core/brain/route_detector/__init__.py +15 -0
- patchi/core/brain/route_detector/base.py +31 -0
- patchi/core/brain/route_detector/csharp.py +209 -0
- patchi/core/brain/route_detector/file_based.py +81 -0
- patchi/core/brain/route_detector/go.py +152 -0
- patchi/core/brain/route_detector/java.py +195 -0
- patchi/core/brain/route_detector/javascript.py +197 -0
- patchi/core/brain/route_detector/php.py +158 -0
- patchi/core/brain/route_detector/python.py +193 -0
- patchi/core/brain/route_detector/registry.py +25 -0
- patchi/core/brain/route_detector/ruby.py +134 -0
- patchi/core/brain/route_detector/rust.py +216 -0
- patchi/core/brain/route_detector/swift.py +118 -0
- patchi/core/brain/route_mapper.py +265 -0
- patchi/core/brain/scanner.py +2136 -0
- patchi/core/brain/secrets.py +280 -0
- patchi/core/brain/symbol_graph.py +1955 -0
- patchi/core/brain/team.py +28 -0
- patchi/core/brain/trace_log.py +132 -0
- patchi/core/brain/type_checker/__init__.py +65 -0
- patchi/core/brain/type_checker/base.py +60 -0
- patchi/core/brain/type_checker/csharp.py +59 -0
- patchi/core/brain/type_checker/dart.py +59 -0
- patchi/core/brain/type_checker/go.py +81 -0
- patchi/core/brain/type_checker/java.py +75 -0
- patchi/core/brain/type_checker/kotlin.py +75 -0
- patchi/core/brain/type_checker/php.py +88 -0
- patchi/core/brain/type_checker/python.py +87 -0
- patchi/core/brain/type_checker/rust.py +81 -0
- patchi/core/brain/type_checker/swift.py +59 -0
- patchi/core/brain/type_checker/typescript.py +298 -0
- patchi/core/brain/understander.py +130 -0
- patchi/core/brain/verify.py +379 -0
- patchi/core/campaigns/__init__.py +14 -0
- patchi/core/campaigns/base.py +62 -0
- patchi/core/campaigns/data_flow_abuse.py +153 -0
- patchi/core/campaigns/orchestrator.py +140 -0
- patchi/core/campaigns/state_transitions.py +178 -0
- patchi/core/ci_bundle.py +104 -0
- patchi/core/config.py +311 -0
- patchi/core/constants.py +245 -0
- patchi/core/debug/__init__.py +25 -0
- patchi/core/debug/adapters/__init__.py +11 -0
- patchi/core/debug/adapters/codelldb.py +379 -0
- patchi/core/debug/adapters/node.py +222 -0
- patchi/core/debug/adapters/powershell.py +210 -0
- patchi/core/debug/adapters/python.py +244 -0
- patchi/core/debug/capture.py +210 -0
- patchi/core/debug/dap_client.py +268 -0
- patchi/core/detector/__init__.py +0 -0
- patchi/core/detector/audit.py +194 -0
- patchi/core/detector/bus.py +226 -0
- patchi/core/detector/dispatcher.py +275 -0
- patchi/core/detector/event.py +254 -0
- patchi/core/detector/sigma_engine.py +378 -0
- patchi/core/detector/triage.py +289 -0
- patchi/core/export/sarif.py +113 -0
- patchi/core/fix/__init__.py +17 -0
- patchi/core/fix/applier.py +574 -0
- patchi/core/fix/base.py +510 -0
- patchi/core/fix/code_fixer.py +169 -0
- patchi/core/fix/dead_code_remover.py +131 -0
- patchi/core/fix/fix_agents.py +775 -0
- patchi/core/fix/patch.py +444 -0
- patchi/core/fix/risk_gate.py +383 -0
- patchi/core/fix/security_fixer.py +93 -0
- patchi/core/fix/verify_loop.py +204 -0
- patchi/core/fuzz/__init__.py +8 -0
- patchi/core/fuzz/corpus.py +137 -0
- patchi/core/fuzz/input_fuzzer.py +256 -0
- patchi/core/fuzz/sequence_fuzzer.py +162 -0
- patchi/core/fuzz/state_fuzzer.py +236 -0
- patchi/core/health.py +407 -0
- patchi/core/hosted/__init__.py +1 -0
- patchi/core/hosted/anomaly.py +298 -0
- patchi/core/hosted/audit_log.py +102 -0
- patchi/core/hosted/compliance_report.py +148 -0
- patchi/core/hosted/ip_reputation.py +169 -0
- patchi/core/hosted/log_parsers.py +251 -0
- patchi/core/hosted/tokens.py +127 -0
- patchi/core/hosted/watchlist.py +164 -0
- patchi/core/hosted/webhooks.py +156 -0
- patchi/core/memory.py +437 -0
- patchi/core/notifications/__init__.py +18 -0
- patchi/core/notifications/channels.py +192 -0
- patchi/core/notifications/digest.py +141 -0
- patchi/core/notifications/escalation.py +158 -0
- patchi/core/notifications/notifier.py +182 -0
- patchi/core/notifications/quiet_hours.py +69 -0
- patchi/core/plugins/__init__.py +48 -0
- patchi/core/plugins/analyzer.py +326 -0
- patchi/core/plugins/analyzers/__init__.py +6 -0
- patchi/core/plugins/analyzers/dead_code_analyzer.py +120 -0
- patchi/core/plugins/analyzers/secret_scanner.py +158 -0
- patchi/core/plugins/analyzers/todo_scanner.py +115 -0
- patchi/core/plugins/registry.py +335 -0
- patchi/core/queue.py +375 -0
- patchi/core/reliability/__init__.py +13 -0
- patchi/core/reliability/chaos.py +163 -0
- patchi/core/reliability/fault_injector.py +172 -0
- patchi/core/reliability/idempotency.py +128 -0
- patchi/core/reliability/recovery.py +147 -0
- patchi/core/runtime/__init__.py +5 -0
- patchi/core/runtime/tracer.py +230 -0
- patchi/core/scan_bus.py +156 -0
- patchi/core/security/__init__.py +1 -0
- patchi/core/security/ai_validator.py +462 -0
- patchi/core/security/app_mapper.py +216 -0
- patchi/core/security/app_profile.py +464 -0
- patchi/core/security/attack_feedback.py +141 -0
- patchi/core/security/attack_scenarios/__init__.py +1 -0
- patchi/core/security/attack_tree.py +176 -0
- patchi/core/security/auth_audit_agent.py +344 -0
- patchi/core/security/authz_agent.py +602 -0
- patchi/core/security/auto_fix_proactive.py +620 -0
- patchi/core/security/auto_fixer.py +660 -0
- patchi/core/security/auto_ticket.py +53 -0
- patchi/core/security/bandit_agent.py +152 -0
- patchi/core/security/blast_radius.py +226 -0
- patchi/core/security/browser_tester.py +333 -0
- patchi/core/security/business_logic_agent.py +263 -0
- patchi/core/security/catch_block_auditor.py +395 -0
- patchi/core/security/cdn_cache_agent.py +357 -0
- patchi/core/security/chain_analyzer.py +321 -0
- patchi/core/security/chain_to_assurance.py +168 -0
- patchi/core/security/charter.py +510 -0
- patchi/core/security/cloud_waf_detector.py +295 -0
- patchi/core/security/codeql_agent.py +233 -0
- patchi/core/security/compliance_agent.py +348 -0
- patchi/core/security/confidence_gate.py +423 -0
- patchi/core/security/container_scanner.py +282 -0
- patchi/core/security/crypto_agent.py +378 -0
- patchi/core/security/cve_monitor.py +262 -0
- patchi/core/security/dast_agent.py +865 -0
- patchi/core/security/defectdojo.py +238 -0
- patchi/core/security/defenders/__init__.py +75 -0
- patchi/core/security/defenders/auth_middleware.py +115 -0
- patchi/core/security/defenders/base.py +97 -0
- patchi/core/security/defenders/block_ip.py +71 -0
- patchi/core/security/defenders/block_ws_origin.py +36 -0
- patchi/core/security/defenders/crypto_fix.py +118 -0
- patchi/core/security/defenders/enforce_rate_limit.py +108 -0
- patchi/core/security/defenders/fix_code.py +93 -0
- patchi/core/security/defenders/invalidate_session.py +40 -0
- patchi/core/security/defenders/patch_config.py +70 -0
- patchi/core/security/defenders/rotate_secret.py +54 -0
- patchi/core/security/defenders/suspend_account.py +41 -0
- patchi/core/security/defenders/update_dependency.py +160 -0
- patchi/core/security/defense_layer.py +217 -0
- patchi/core/security/dependency_vulnerability_agent.py +616 -0
- patchi/core/security/detection_pipeline.py +289 -0
- patchi/core/security/dns_security_agent.py +497 -0
- patchi/core/security/domain_activator_v2.py +913 -0
- patchi/core/security/domain_loader.py +1004 -0
- patchi/core/security/domains/__init__.py +0 -0
- patchi/core/security/email_authentication_agent.py +417 -0
- patchi/core/security/env_var_validator.py +304 -0
- patchi/core/security/evidence.py +100 -0
- patchi/core/security/falco_runtime_agent.py +387 -0
- patchi/core/security/fix-playbooks/__init__.py +0 -0
- patchi/core/security/gated_finding.py +85 -0
- patchi/core/security/git_diff_activator.py +367 -0
- patchi/core/security/governance.py +208 -0
- patchi/core/security/gradual_gate.py +34 -0
- patchi/core/security/history.py +315 -0
- patchi/core/security/iac_scanner.py +250 -0
- patchi/core/security/ignore_expiry.py +111 -0
- patchi/core/security/ignore_learner.py +416 -0
- patchi/core/security/injection_agent.py +319 -0
- patchi/core/security/insecure_randomness_agent.py +288 -0
- patchi/core/security/intent_analyzer.py +326 -0
- patchi/core/security/jwt_agent.py +453 -0
- patchi/core/security/kubernetes_agent.py +638 -0
- patchi/core/security/layer2_orchestrator.py +341 -0
- patchi/core/security/llm_security_agent.py +252 -0
- patchi/core/security/misconfig_agent.py +600 -0
- patchi/core/security/mobile_security_agent.py +474 -0
- patchi/core/security/network_agent.py +376 -0
- patchi/core/security/noise_filter.py +274 -0
- patchi/core/security/orchestrator.py +313 -0
- patchi/core/security/pattern_context.py +125 -0
- patchi/core/security/pattern_loader.py +101 -0
- patchi/core/security/pentest/__init__.py +4 -0
- patchi/core/security/pentest/base.py +58 -0
- patchi/core/security/pentest/dalfox_adapter.py +40 -0
- patchi/core/security/pentest/ffuf_adapter.py +48 -0
- patchi/core/security/pentest/nuclei_adapter.py +50 -0
- patchi/core/security/pentest/registry.py +80 -0
- patchi/core/security/pentest/shannon_adapter.py +107 -0
- patchi/core/security/pentest/sqlmap_adapter.py +56 -0
- patchi/core/security/pentest/zap_adapter.py +52 -0
- patchi/core/security/plan_auditor.py +276 -0
- patchi/core/security/policy_engine.py +272 -0
- patchi/core/security/prechecks.py +172 -0
- patchi/core/security/precommit_hook.py +247 -0
- patchi/core/security/privacy_agent.py +361 -0
- patchi/core/security/push_notification_agent.py +417 -0
- patchi/core/security/pysa_agent.py +143 -0
- patchi/core/security/reasoning.py +442 -0
- patchi/core/security/red_team_agent.py +184 -0
- patchi/core/security/red_team_engine.py +1142 -0
- patchi/core/security/remediation.py +360 -0
- patchi/core/security/request_interceptor.py +294 -0
- patchi/core/security/runtime_validator.py +245 -0
- patchi/core/security/saml_sso_agent.py +401 -0
- patchi/core/security/sast_agent.py +213 -0
- patchi/core/security/sast_gate.py +95 -0
- patchi/core/security/scheduler.py +287 -0
- patchi/core/security/secrets_runtime_agent.py +476 -0
- patchi/core/security/security_agents.py +175 -0
- patchi/core/security/security_config.py +551 -0
- patchi/core/security/security_probe.py +762 -0
- patchi/core/security/security_taint.py +1005 -0
- patchi/core/security/sensitive_data_agent.py +271 -0
- patchi/core/security/service_mesh_agent.py +412 -0
- patchi/core/security/session_management_agent.py +245 -0
- patchi/core/security/ssrf_agent.py +422 -0
- patchi/core/security/supply_chain.py +449 -0
- patchi/core/security/threat_model_generator.py +378 -0
- patchi/core/security/threat_model_updater.py +196 -0
- patchi/core/security/tool_adapters.py +135 -0
- patchi/core/security/tool_verify.py +173 -0
- patchi/core/security/websocket_security_agent.py +188 -0
- patchi/core/snapshot.py +242 -0
- patchi/core/tenant.py +386 -0
- patchi/core/testing/__init__.py +17 -0
- patchi/core/testing/_browser.py +147 -0
- patchi/core/testing/ads_agent.py +141 -0
- patchi/core/testing/api_contract_agent.py +450 -0
- patchi/core/testing/app_discovery_agent.py +641 -0
- patchi/core/testing/app_launcher.py +184 -0
- patchi/core/testing/browser_test_agent.py +516 -0
- patchi/core/testing/e2e_flow_agent.py +354 -0
- patchi/core/testing/flake_detector_agent.py +292 -0
- patchi/core/testing/gate.py +31 -0
- patchi/core/testing/live_test_runner.py +304 -0
- patchi/core/testing/live_v2/__init__.py +1 -0
- patchi/core/testing/live_v2/browser_pool.py +349 -0
- patchi/core/testing/live_v2/browser_test_runner.py +313 -0
- patchi/core/testing/live_v2/runner.py +626 -0
- patchi/core/testing/live_v2/screenshot_manager.py +474 -0
- patchi/core/testing/live_v2/stress_orchestrator.py +523 -0
- patchi/core/testing/live_v2/video_recorder.py +303 -0
- patchi/core/testing/regression_agent.py +302 -0
- patchi/core/testing/security_test_agent.py +253 -0
- patchi/core/testing/stress_test_agent.py +203 -0
- patchi/core/testing/test_agents.py +50 -0
- patchi/core/testing/test_config.py +194 -0
- patchi/core/testing/ui_accessibility_agent.py +324 -0
- patchi/core/testing/ui_button_agent.py +294 -0
- patchi/core/testing/ui_layout_agent.py +330 -0
- patchi/core/testing/unit_test_agent.py +1149 -0
- patchi/core/testing/visual_regression_agent.py +260 -0
- patchi/web/__init__.py +1 -0
- patchi/web/api/__init__.py +1 -0
- patchi/web/api/brain_map.py +114 -0
- patchi/web/api/charts.py +47 -0
- patchi/web/api/chat.py +224 -0
- patchi/web/api/cicd.py +576 -0
- patchi/web/api/dast_evidence.py +49 -0
- patchi/web/api/dev_check.py +320 -0
- patchi/web/api/fix.py +134 -0
- patchi/web/api/guard.py +65 -0
- patchi/web/api/hosted.py +128 -0
- patchi/web/api/hosted_v2.py +234 -0
- patchi/web/api/live_testing.py +699 -0
- patchi/web/api/scan.py +582 -0
- patchi/web/api/smart.py +111 -0
- patchi/web/api/tenant.py +117 -0
- patchi/web/api/tools.py +85 -0
- patchi/web/api_legacy.py +2401 -0
- patchi/web/app.py +310 -0
- patchi/web/events.py +483 -0
- patchi/web/routes/__init__.py +1 -0
- patchi/web/routes/assurance.py +541 -0
- patchi/web/routes/brain.py +36 -0
- patchi/web/routes/charter.py +227 -0
- patchi/web/routes/chat.py +23 -0
- patchi/web/routes/dashboard.py +558 -0
- patchi/web/routes/dashboard_v2.py +402 -0
- patchi/web/routes/findings.py +330 -0
- patchi/web/routes/guard.py +35 -0
- patchi/web/routes/history.py +126 -0
- patchi/web/routes/landing.py +52 -0
- patchi/web/routes/live_testing.py +24 -0
- patchi/web/routes/review.py +57 -0
- patchi/web/routes/self_improvement.py +110 -0
- patchi/web/routes/settings.py +49 -0
- patchi/web/routes/tokens.py +29 -0
- patchi/web/spawn.py +139 -0
- patchi/web/ws.py +141 -0
- patchi-0.7.5.dist-info/METADATA +315 -0
- patchi-0.7.5.dist-info/RECORD +496 -0
- patchi-0.7.5.dist-info/WHEEL +5 -0
- patchi-0.7.5.dist-info/licenses/LICENSE +60 -0
- patchi-0.7.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Self-Improvement dashboard — agent profiles, learning, threat model."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from fastapi import APIRouter, Request
|
|
10
|
+
from fastapi.responses import HTMLResponse, JSONResponse
|
|
11
|
+
from fastapi.templating import Jinja2Templates
|
|
12
|
+
|
|
13
|
+
_log = logging.getLogger("patchi.web.routes.self_improvement")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
router = APIRouter()
|
|
17
|
+
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _load_json(root: Path, rel: str) -> dict:
|
|
21
|
+
p = root / rel
|
|
22
|
+
if p.is_file():
|
|
23
|
+
try:
|
|
24
|
+
return json.loads(p.read_text(encoding="utf-8"))
|
|
25
|
+
except Exception as _exc:
|
|
26
|
+
_log.warning('_load_json failed: %s', _exc)
|
|
27
|
+
return {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@router.get("/self-improvement", response_class=HTMLResponse)
|
|
31
|
+
async def self_improvement(request: Request):
|
|
32
|
+
root = request.app.state.root
|
|
33
|
+
|
|
34
|
+
# Agent profiles
|
|
35
|
+
try:
|
|
36
|
+
from patchi.core.ai.agent_profiler import get_all_profiles, get_profile_summary
|
|
37
|
+
|
|
38
|
+
profile_summary = get_profile_summary(root)
|
|
39
|
+
all_profiles = get_all_profiles(root)
|
|
40
|
+
profiles_data = {k: v.to_dict() for k, v in all_profiles.items()}
|
|
41
|
+
except Exception:
|
|
42
|
+
profile_summary = {"agents": 0, "total_runs": 0, "total_cost": 0}
|
|
43
|
+
profiles_data = {}
|
|
44
|
+
|
|
45
|
+
# Learning summary
|
|
46
|
+
try:
|
|
47
|
+
from patchi.core.security.attack_feedback import get_learning_summary
|
|
48
|
+
|
|
49
|
+
learning = get_learning_summary(root)
|
|
50
|
+
except Exception:
|
|
51
|
+
learning = {"acceptances": {}, "rejections": {}, "agent_trust": {}, "skip_types": []}
|
|
52
|
+
|
|
53
|
+
# Threat model
|
|
54
|
+
threat_model = _load_json(root, ".patchi/threat_model.json")
|
|
55
|
+
|
|
56
|
+
# Fuzz corpus stats
|
|
57
|
+
try:
|
|
58
|
+
from patchi.core.fuzz.corpus import FuzzCorpus
|
|
59
|
+
|
|
60
|
+
corpus = FuzzCorpus(root)
|
|
61
|
+
corpus_stats = corpus.stats()
|
|
62
|
+
except Exception:
|
|
63
|
+
corpus_stats = {"total": 0, "avg_score": 0, "strategies": {}}
|
|
64
|
+
|
|
65
|
+
# Ignore learner stats
|
|
66
|
+
try:
|
|
67
|
+
from patchi.core.security.ignore_learner import IgnoreLearner
|
|
68
|
+
|
|
69
|
+
learner = IgnoreLearner(root)
|
|
70
|
+
learner.build()
|
|
71
|
+
ignore_count = len(learner.entries)
|
|
72
|
+
except Exception:
|
|
73
|
+
ignore_count = 0
|
|
74
|
+
|
|
75
|
+
return templates.TemplateResponse(
|
|
76
|
+
request,
|
|
77
|
+
"self_improvement.html",
|
|
78
|
+
{
|
|
79
|
+
"request": request,
|
|
80
|
+
"profile_summary": profile_summary,
|
|
81
|
+
"profiles": profiles_data,
|
|
82
|
+
"learning": learning,
|
|
83
|
+
"threat_model": threat_model,
|
|
84
|
+
"corpus_stats": corpus_stats,
|
|
85
|
+
"ignore_count": ignore_count,
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@router.get("/api/self-improvement")
|
|
91
|
+
async def api_self_improvement(request: Request):
|
|
92
|
+
root = request.app.state.root
|
|
93
|
+
try:
|
|
94
|
+
from patchi.core.ai.agent_profiler import get_all_profiles, get_profile_summary
|
|
95
|
+
from patchi.core.security.attack_feedback import get_learning_summary
|
|
96
|
+
|
|
97
|
+
profile_summary = get_profile_summary(root)
|
|
98
|
+
all_profiles = get_all_profiles(root)
|
|
99
|
+
learning = get_learning_summary(root)
|
|
100
|
+
threat_model = _load_json(root, ".patchi/threat_model.json")
|
|
101
|
+
return JSONResponse(
|
|
102
|
+
{
|
|
103
|
+
"profiles": profile_summary,
|
|
104
|
+
"agent_details": {k: v.to_dict() for k, v in all_profiles.items()},
|
|
105
|
+
"learning": learning,
|
|
106
|
+
"threat_model": threat_model,
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
return JSONResponse({"error": str(e)}, status_code=500)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Settings route — web UI configuration panel."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Request
|
|
8
|
+
from fastapi.responses import HTMLResponse
|
|
9
|
+
from fastapi.templating import Jinja2Templates
|
|
10
|
+
|
|
11
|
+
router = APIRouter()
|
|
12
|
+
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/settings", response_class=HTMLResponse)
|
|
16
|
+
async def settings_page(request: Request):
|
|
17
|
+
root = request.app.state.root
|
|
18
|
+
from patchi.core import config as cfg
|
|
19
|
+
from patchi.core import memory as mem
|
|
20
|
+
|
|
21
|
+
brain = mem.get_brain(root)
|
|
22
|
+
config = cfg.load(root)
|
|
23
|
+
|
|
24
|
+
safe_keys = {
|
|
25
|
+
"mode",
|
|
26
|
+
"theme",
|
|
27
|
+
"device_tier",
|
|
28
|
+
"scan_depth",
|
|
29
|
+
"risk_threshold",
|
|
30
|
+
"queue_mode",
|
|
31
|
+
"quiet_hours_start",
|
|
32
|
+
"quiet_hours_end",
|
|
33
|
+
"max_parallel_agents",
|
|
34
|
+
"max_queue_depth",
|
|
35
|
+
"websocket_timeout",
|
|
36
|
+
}
|
|
37
|
+
safe_config = {k: v for k, v in config.items() if k in safe_keys}
|
|
38
|
+
|
|
39
|
+
return templates.TemplateResponse(
|
|
40
|
+
request,
|
|
41
|
+
"settings.html",
|
|
42
|
+
{
|
|
43
|
+
"request": request,
|
|
44
|
+
"config": safe_config,
|
|
45
|
+
"framework": brain.get("framework", "Unknown"),
|
|
46
|
+
"health_score": brain.get("health_score", {}).get("total", 0),
|
|
47
|
+
"ai_keys": len(config.get("ai", {}).get("keys", [])),
|
|
48
|
+
},
|
|
49
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Tokens route — admin token management page."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Request
|
|
8
|
+
from fastapi.responses import HTMLResponse
|
|
9
|
+
from fastapi.templating import Jinja2Templates
|
|
10
|
+
|
|
11
|
+
router = APIRouter()
|
|
12
|
+
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/tokens", response_class=HTMLResponse)
|
|
16
|
+
async def tokens_page(request: Request):
|
|
17
|
+
root = request.app.state.root
|
|
18
|
+
from patchi.core.hosted import tokens as tokens_mod
|
|
19
|
+
|
|
20
|
+
token_list = tokens_mod.list_tokens(root)
|
|
21
|
+
|
|
22
|
+
return templates.TemplateResponse(
|
|
23
|
+
request,
|
|
24
|
+
"tokens.html",
|
|
25
|
+
{
|
|
26
|
+
"request": request,
|
|
27
|
+
"tokens": token_list,
|
|
28
|
+
},
|
|
29
|
+
)
|
patchi/web/spawn.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tap-to-spawn handler for the Patchi Brain Map.
|
|
3
|
+
|
|
4
|
+
Enforces the full spec from patchi_builder_sidenote.md:
|
|
5
|
+
- Max 5 user-spawned ants simultaneously
|
|
6
|
+
- 10-second tap cooldown per node
|
|
7
|
+
- 60-second ant expiry
|
|
8
|
+
- No spawn if Patchi is idle (returns None — caller shows node inspector)
|
|
9
|
+
- No spawn on restricted nodes
|
|
10
|
+
- No spawn if 3+ ants already working on the node
|
|
11
|
+
- Second tap on same node within 10s is ignored
|
|
12
|
+
|
|
13
|
+
SpawnManager is instantiated once on server start and shared via FastAPI state.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import time
|
|
19
|
+
import uuid
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
import patchi.core.config as config
|
|
24
|
+
|
|
25
|
+
# Spec constants
|
|
26
|
+
MAX_USER_ANTS = 5
|
|
27
|
+
NODE_COOLDOWN_S = 10
|
|
28
|
+
ANT_EXPIRY_S = 60
|
|
29
|
+
MAX_ANTS_ON_NODE = 3
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class SpawnedAnt:
|
|
34
|
+
ant_id: str
|
|
35
|
+
node_id: str
|
|
36
|
+
spawned_at: float = field(default_factory=time.time)
|
|
37
|
+
done: bool = False
|
|
38
|
+
|
|
39
|
+
def is_expired(self) -> bool:
|
|
40
|
+
return (time.time() - self.spawned_at) > ANT_EXPIRY_S
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class SpawnManager:
|
|
44
|
+
"""
|
|
45
|
+
Tracks all user-spawned ants. Called by the WebSocket handler when
|
|
46
|
+
the client sends an `action.spawn_ant` event.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, root: Path) -> None:
|
|
50
|
+
self._root = root
|
|
51
|
+
self._ants: dict[str, SpawnedAnt] = {} # ant_id → SpawnedAnt
|
|
52
|
+
self._taps: dict[str, float] = {} # node_id → last tap timestamp
|
|
53
|
+
self._is_active: bool = False # set by server when a scan runs
|
|
54
|
+
|
|
55
|
+
# ── Lifecycle ─────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
def set_active(self, active: bool) -> None:
|
|
58
|
+
"""Called by the scan runner to indicate whether Patchi is running."""
|
|
59
|
+
self._is_active = active
|
|
60
|
+
|
|
61
|
+
def mark_done(self, ant_id: str) -> None:
|
|
62
|
+
if ant_id in self._ants:
|
|
63
|
+
self._ants[ant_id].done = True
|
|
64
|
+
|
|
65
|
+
# ── Spawn gate ────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
def try_spawn(self, node_id: str) -> tuple[str | None, str]:
|
|
68
|
+
"""
|
|
69
|
+
Attempt to spawn an ant on node_id.
|
|
70
|
+
|
|
71
|
+
Returns (ant_id, reason):
|
|
72
|
+
- (ant_id, "ok") → spawned successfully
|
|
73
|
+
- (None, "idle") → Patchi not running — show inspector instead
|
|
74
|
+
- (None, "restricted") → node is restricted
|
|
75
|
+
- (None, "cooldown") → same node tapped within 10s
|
|
76
|
+
- (None, "capacity") → 5 user-spawned ants already active
|
|
77
|
+
- (None, "node_busy") → 3+ ants already on this node
|
|
78
|
+
"""
|
|
79
|
+
self._expire_ants()
|
|
80
|
+
|
|
81
|
+
if not self._is_active:
|
|
82
|
+
return None, "idle"
|
|
83
|
+
|
|
84
|
+
if self._is_restricted(node_id):
|
|
85
|
+
return None, "restricted"
|
|
86
|
+
|
|
87
|
+
if self._on_cooldown(node_id):
|
|
88
|
+
return None, "cooldown"
|
|
89
|
+
|
|
90
|
+
live = self._live_ants()
|
|
91
|
+
if len(live) >= MAX_USER_ANTS:
|
|
92
|
+
return None, "capacity"
|
|
93
|
+
|
|
94
|
+
ants_on_node = sum(1 for a in live.values() if a.node_id == node_id)
|
|
95
|
+
if ants_on_node >= MAX_ANTS_ON_NODE:
|
|
96
|
+
return None, "node_busy"
|
|
97
|
+
|
|
98
|
+
ant_id = str(uuid.uuid4())[:8]
|
|
99
|
+
self._ants[ant_id] = SpawnedAnt(ant_id=ant_id, node_id=node_id)
|
|
100
|
+
self._taps[node_id] = time.time()
|
|
101
|
+
return ant_id, "ok"
|
|
102
|
+
|
|
103
|
+
def active_count(self) -> int:
|
|
104
|
+
self._expire_ants()
|
|
105
|
+
return len(self._live_ants())
|
|
106
|
+
|
|
107
|
+
def all_active(self) -> list[dict]:
|
|
108
|
+
self._expire_ants()
|
|
109
|
+
return [
|
|
110
|
+
{"ant_id": a.ant_id, "node_id": a.node_id, "age_s": int(time.time() - a.spawned_at)}
|
|
111
|
+
for a in self._live_ants().values()
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
# ── Internal helpers ──────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
def _live_ants(self) -> dict[str, SpawnedAnt]:
|
|
117
|
+
return {aid: a for aid, a in self._ants.items() if not a.done and not a.is_expired()}
|
|
118
|
+
|
|
119
|
+
def _expire_ants(self) -> None:
|
|
120
|
+
expired = [aid for aid, ant in self._ants.items() if ant.is_expired()]
|
|
121
|
+
for aid in expired:
|
|
122
|
+
del self._ants[aid]
|
|
123
|
+
|
|
124
|
+
def _on_cooldown(self, node_id: str) -> bool:
|
|
125
|
+
last = self._taps.get(node_id, 0.0)
|
|
126
|
+
return (time.time() - last) < NODE_COOLDOWN_S
|
|
127
|
+
|
|
128
|
+
def _is_restricted(self, node_id: str) -> bool:
|
|
129
|
+
"""Check config restrictions. node_id is the file path used as Brain Map node key."""
|
|
130
|
+
try:
|
|
131
|
+
restrictions = config.get_restrictions(self._root)
|
|
132
|
+
return any(r["path"] == node_id and r.get("enabled", True) for r in restrictions)
|
|
133
|
+
except Exception as e:
|
|
134
|
+
import logging
|
|
135
|
+
|
|
136
|
+
logging.getLogger("patchi.web.spawn").warning(
|
|
137
|
+
"Restriction check failed for %s: %s", node_id, e
|
|
138
|
+
)
|
|
139
|
+
return False
|
patchi/web/ws.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WebSocket manager for live events.
|
|
3
|
+
|
|
4
|
+
Broadcasts scan progress, finding alerts, guard events to all connected clients.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import json
|
|
11
|
+
import time
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class WSManager:
|
|
18
|
+
"""Manages WebSocket connections and broadcasts events."""
|
|
19
|
+
|
|
20
|
+
_connections: list = field(default_factory=list)
|
|
21
|
+
_lock: asyncio.Lock = field(default_factory=asyncio.Lock)
|
|
22
|
+
|
|
23
|
+
async def connect(self, ws: Any) -> None:
|
|
24
|
+
async with self._lock:
|
|
25
|
+
self._connections.append(ws)
|
|
26
|
+
|
|
27
|
+
async def disconnect(self, ws: Any) -> None:
|
|
28
|
+
async with self._lock:
|
|
29
|
+
if ws in self._connections:
|
|
30
|
+
self._connections.remove(ws)
|
|
31
|
+
|
|
32
|
+
async def broadcast(self, event: str, data: dict | None = None) -> None:
|
|
33
|
+
"""Send event to all connected clients."""
|
|
34
|
+
message = json.dumps(
|
|
35
|
+
{
|
|
36
|
+
"event": event,
|
|
37
|
+
"data": data or {},
|
|
38
|
+
"ts": time.time(),
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
async with self._lock:
|
|
42
|
+
targets = list(self._connections)
|
|
43
|
+
dead = []
|
|
44
|
+
for ws in targets:
|
|
45
|
+
try:
|
|
46
|
+
await ws.send_text(message)
|
|
47
|
+
except Exception:
|
|
48
|
+
dead.append(ws)
|
|
49
|
+
for ws in dead:
|
|
50
|
+
if ws in self._connections:
|
|
51
|
+
self._connections.remove(ws)
|
|
52
|
+
if dead:
|
|
53
|
+
import logging
|
|
54
|
+
|
|
55
|
+
logging.getLogger("patchi.web.ws").warning(
|
|
56
|
+
"Removed %d dead WS connection(s)", len(dead)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def connection_count(self) -> int:
|
|
61
|
+
return len(self._connections)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Global instance
|
|
65
|
+
manager = WSManager()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ── Event helpers ─────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def evt_scan_started(agent_count: int = 0) -> None:
|
|
72
|
+
await manager.broadcast("scan.started", {"agent_count": agent_count})
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def evt_scan_progress(agent: str, phase: str, current: int, total: int) -> None:
|
|
76
|
+
await manager.broadcast(
|
|
77
|
+
"scan.progress",
|
|
78
|
+
{
|
|
79
|
+
"agent": agent,
|
|
80
|
+
"phase": phase,
|
|
81
|
+
"current": current,
|
|
82
|
+
"total": total,
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def evt_scan_finding(finding: dict) -> None:
|
|
88
|
+
await manager.broadcast("scan.finding", finding)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def evt_scan_complete(total_findings: int, duration_ms: int) -> None:
|
|
92
|
+
await manager.broadcast(
|
|
93
|
+
"scan.complete",
|
|
94
|
+
{
|
|
95
|
+
"total_findings": total_findings,
|
|
96
|
+
"duration_ms": duration_ms,
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
async def evt_scan_cancelled(total_findings: int) -> None:
|
|
102
|
+
await manager.broadcast(
|
|
103
|
+
"scan.cancelled",
|
|
104
|
+
{"total_findings": total_findings},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async def evt_agent_started(agent_name: str) -> None:
|
|
109
|
+
await manager.broadcast("agent.started", {"agent": agent_name})
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
async def evt_agent_done(agent_name: str, finding_count: int) -> None:
|
|
113
|
+
await manager.broadcast(
|
|
114
|
+
"agent.done",
|
|
115
|
+
{
|
|
116
|
+
"agent": agent_name,
|
|
117
|
+
"finding_count": finding_count,
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def evt_guard_alert(alert: dict) -> None:
|
|
123
|
+
await manager.broadcast("guard.alert", alert)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
async def evt_guard_status(status: dict) -> None:
|
|
127
|
+
await manager.broadcast("guard.status", status)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
async def evt_fix_applied(patch_id: str, file_path: str) -> None:
|
|
131
|
+
await manager.broadcast(
|
|
132
|
+
"fix.applied",
|
|
133
|
+
{
|
|
134
|
+
"patch_id": patch_id,
|
|
135
|
+
"file_path": file_path,
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
async def evt_status_update(status: dict) -> None:
|
|
141
|
+
await manager.broadcast("status.update", status)
|