tapps-agents 3.5.40__py3-none-any.whl → 3.6.0__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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/__init__.py +22 -22
- tapps_agents/agents/analyst/__init__.py +5 -5
- tapps_agents/agents/architect/__init__.py +5 -5
- tapps_agents/agents/architect/agent.py +1033 -1033
- tapps_agents/agents/architect/pattern_detector.py +75 -75
- tapps_agents/agents/cleanup/__init__.py +7 -7
- tapps_agents/agents/cleanup/agent.py +445 -445
- tapps_agents/agents/debugger/__init__.py +7 -7
- tapps_agents/agents/debugger/agent.py +310 -310
- tapps_agents/agents/debugger/error_analyzer.py +437 -437
- tapps_agents/agents/designer/__init__.py +5 -5
- tapps_agents/agents/designer/agent.py +786 -786
- tapps_agents/agents/designer/visual_designer.py +638 -638
- tapps_agents/agents/documenter/__init__.py +7 -7
- tapps_agents/agents/documenter/agent.py +531 -531
- tapps_agents/agents/documenter/doc_generator.py +472 -472
- tapps_agents/agents/documenter/doc_validator.py +393 -393
- tapps_agents/agents/documenter/framework_doc_updater.py +493 -493
- tapps_agents/agents/enhancer/__init__.py +7 -7
- tapps_agents/agents/evaluator/__init__.py +7 -7
- tapps_agents/agents/evaluator/agent.py +443 -443
- tapps_agents/agents/evaluator/priority_evaluator.py +641 -641
- tapps_agents/agents/evaluator/quality_analyzer.py +147 -147
- tapps_agents/agents/evaluator/report_generator.py +344 -344
- tapps_agents/agents/evaluator/usage_analyzer.py +192 -192
- tapps_agents/agents/evaluator/workflow_analyzer.py +189 -189
- tapps_agents/agents/implementer/__init__.py +7 -7
- tapps_agents/agents/implementer/agent.py +798 -798
- tapps_agents/agents/implementer/auto_fix.py +1119 -1119
- tapps_agents/agents/implementer/code_generator.py +73 -73
- tapps_agents/agents/improver/__init__.py +1 -1
- tapps_agents/agents/improver/agent.py +753 -753
- tapps_agents/agents/ops/__init__.py +1 -1
- tapps_agents/agents/ops/agent.py +619 -619
- tapps_agents/agents/ops/dependency_analyzer.py +600 -600
- tapps_agents/agents/orchestrator/__init__.py +5 -5
- tapps_agents/agents/orchestrator/agent.py +522 -522
- tapps_agents/agents/planner/__init__.py +7 -7
- tapps_agents/agents/planner/agent.py +1127 -1127
- tapps_agents/agents/reviewer/__init__.py +24 -24
- tapps_agents/agents/reviewer/agent.py +3513 -3513
- tapps_agents/agents/reviewer/aggregator.py +213 -213
- tapps_agents/agents/reviewer/batch_review.py +448 -448
- tapps_agents/agents/reviewer/cache.py +443 -443
- tapps_agents/agents/reviewer/context7_enhancer.py +630 -630
- tapps_agents/agents/reviewer/context_detector.py +203 -203
- tapps_agents/agents/reviewer/docker_compose_validator.py +158 -158
- tapps_agents/agents/reviewer/dockerfile_validator.py +176 -176
- tapps_agents/agents/reviewer/error_handling.py +126 -126
- tapps_agents/agents/reviewer/feedback_generator.py +490 -490
- tapps_agents/agents/reviewer/influxdb_validator.py +316 -316
- tapps_agents/agents/reviewer/issue_tracking.py +169 -169
- tapps_agents/agents/reviewer/library_detector.py +295 -295
- tapps_agents/agents/reviewer/library_patterns.py +268 -268
- tapps_agents/agents/reviewer/maintainability_scorer.py +593 -593
- tapps_agents/agents/reviewer/metric_strategies.py +276 -276
- tapps_agents/agents/reviewer/mqtt_validator.py +160 -160
- tapps_agents/agents/reviewer/output_enhancer.py +105 -105
- tapps_agents/agents/reviewer/pattern_detector.py +241 -241
- tapps_agents/agents/reviewer/performance_scorer.py +357 -357
- tapps_agents/agents/reviewer/phased_review.py +516 -516
- tapps_agents/agents/reviewer/progressive_review.py +435 -435
- tapps_agents/agents/reviewer/react_scorer.py +331 -331
- tapps_agents/agents/reviewer/score_constants.py +228 -228
- tapps_agents/agents/reviewer/score_validator.py +507 -507
- tapps_agents/agents/reviewer/scorer_registry.py +373 -373
- tapps_agents/agents/reviewer/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- tapps_agents/agents/reviewer/tools/parallel_executor.py +581 -581
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -250
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -284
- tapps_agents/agents/reviewer/typescript_scorer.py +1142 -1142
- tapps_agents/agents/reviewer/validation.py +208 -208
- tapps_agents/agents/reviewer/websocket_validator.py +132 -132
- tapps_agents/agents/tester/__init__.py +7 -7
- tapps_agents/agents/tester/accessibility_auditor.py +309 -309
- tapps_agents/agents/tester/agent.py +1080 -1080
- tapps_agents/agents/tester/batch_generator.py +54 -54
- tapps_agents/agents/tester/context_learner.py +51 -51
- tapps_agents/agents/tester/coverage_analyzer.py +386 -386
- tapps_agents/agents/tester/coverage_test_generator.py +290 -290
- tapps_agents/agents/tester/debug_enhancer.py +238 -238
- tapps_agents/agents/tester/device_emulator.py +241 -241
- tapps_agents/agents/tester/integration_generator.py +62 -62
- tapps_agents/agents/tester/network_recorder.py +300 -300
- tapps_agents/agents/tester/performance_monitor.py +320 -320
- tapps_agents/agents/tester/test_fixer.py +316 -316
- tapps_agents/agents/tester/test_generator.py +632 -632
- tapps_agents/agents/tester/trace_manager.py +234 -234
- tapps_agents/agents/tester/visual_regression.py +291 -291
- tapps_agents/analysis/pattern_detector.py +36 -36
- tapps_agents/beads/hydration.py +213 -213
- tapps_agents/beads/parse.py +32 -32
- tapps_agents/beads/specs.py +206 -206
- tapps_agents/cli/__init__.py +9 -9
- tapps_agents/cli/__main__.py +8 -8
- tapps_agents/cli/base.py +478 -478
- tapps_agents/cli/command_classifier.py +72 -72
- tapps_agents/cli/commands/__init__.py +2 -2
- tapps_agents/cli/commands/analyst.py +173 -173
- tapps_agents/cli/commands/architect.py +109 -109
- tapps_agents/cli/commands/cleanup_agent.py +92 -92
- tapps_agents/cli/commands/common.py +126 -126
- tapps_agents/cli/commands/debugger.py +90 -90
- tapps_agents/cli/commands/designer.py +112 -112
- tapps_agents/cli/commands/documenter.py +136 -136
- tapps_agents/cli/commands/enhancer.py +110 -110
- tapps_agents/cli/commands/evaluator.py +255 -255
- tapps_agents/cli/commands/health.py +665 -665
- tapps_agents/cli/commands/implementer.py +301 -301
- tapps_agents/cli/commands/improver.py +91 -91
- tapps_agents/cli/commands/knowledge.py +111 -111
- tapps_agents/cli/commands/learning.py +172 -172
- tapps_agents/cli/commands/observability.py +283 -283
- tapps_agents/cli/commands/ops.py +135 -135
- tapps_agents/cli/commands/orchestrator.py +116 -116
- tapps_agents/cli/commands/planner.py +237 -237
- tapps_agents/cli/commands/reviewer.py +1872 -1872
- tapps_agents/cli/commands/status.py +285 -285
- tapps_agents/cli/commands/task.py +227 -219
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/commands/top_level.py +3586 -3586
- tapps_agents/cli/feedback.py +936 -936
- tapps_agents/cli/formatters.py +608 -608
- tapps_agents/cli/help/__init__.py +7 -7
- tapps_agents/cli/help/static_help.py +425 -425
- tapps_agents/cli/network_detection.py +110 -110
- tapps_agents/cli/output_compactor.py +274 -274
- tapps_agents/cli/parsers/__init__.py +2 -2
- tapps_agents/cli/parsers/analyst.py +186 -186
- tapps_agents/cli/parsers/architect.py +167 -167
- tapps_agents/cli/parsers/cleanup_agent.py +228 -228
- tapps_agents/cli/parsers/debugger.py +116 -116
- tapps_agents/cli/parsers/designer.py +182 -182
- tapps_agents/cli/parsers/documenter.py +134 -134
- tapps_agents/cli/parsers/enhancer.py +113 -113
- tapps_agents/cli/parsers/evaluator.py +213 -213
- tapps_agents/cli/parsers/implementer.py +168 -168
- tapps_agents/cli/parsers/improver.py +132 -132
- tapps_agents/cli/parsers/ops.py +159 -159
- tapps_agents/cli/parsers/orchestrator.py +98 -98
- tapps_agents/cli/parsers/planner.py +145 -145
- tapps_agents/cli/parsers/reviewer.py +462 -462
- tapps_agents/cli/parsers/tester.py +124 -124
- tapps_agents/cli/progress_heartbeat.py +254 -254
- tapps_agents/cli/streaming_progress.py +336 -336
- tapps_agents/cli/utils/__init__.py +6 -6
- tapps_agents/cli/utils/agent_lifecycle.py +48 -48
- tapps_agents/cli/utils/error_formatter.py +82 -82
- tapps_agents/cli/utils/error_recovery.py +188 -188
- tapps_agents/cli/utils/output_handler.py +59 -59
- tapps_agents/cli/utils/prompt_enhancer.py +319 -319
- tapps_agents/cli/validators/__init__.py +9 -9
- tapps_agents/cli/validators/command_validator.py +81 -81
- tapps_agents/context7/__init__.py +112 -112
- tapps_agents/context7/agent_integration.py +869 -869
- tapps_agents/context7/analytics.py +382 -382
- tapps_agents/context7/analytics_dashboard.py +299 -299
- tapps_agents/context7/async_cache.py +681 -681
- tapps_agents/context7/backup_client.py +958 -958
- tapps_agents/context7/cache_locking.py +194 -194
- tapps_agents/context7/cache_metadata.py +214 -214
- tapps_agents/context7/cache_prewarm.py +488 -488
- tapps_agents/context7/cache_structure.py +168 -168
- tapps_agents/context7/cache_warming.py +604 -604
- tapps_agents/context7/circuit_breaker.py +376 -376
- tapps_agents/context7/cleanup.py +461 -461
- tapps_agents/context7/commands.py +858 -858
- tapps_agents/context7/credential_validation.py +276 -276
- tapps_agents/context7/cross_reference_resolver.py +168 -168
- tapps_agents/context7/cross_references.py +424 -424
- tapps_agents/context7/doc_manager.py +225 -225
- tapps_agents/context7/fuzzy_matcher.py +369 -369
- tapps_agents/context7/kb_cache.py +404 -404
- tapps_agents/context7/language_detector.py +219 -219
- tapps_agents/context7/library_detector.py +725 -725
- tapps_agents/context7/lookup.py +738 -738
- tapps_agents/context7/metadata.py +258 -258
- tapps_agents/context7/refresh_queue.py +300 -300
- tapps_agents/context7/security.py +373 -373
- tapps_agents/context7/staleness_policies.py +278 -278
- tapps_agents/context7/tiles_integration.py +47 -47
- tapps_agents/continuous_bug_fix/__init__.py +20 -20
- tapps_agents/continuous_bug_fix/bug_finder.py +306 -306
- tapps_agents/continuous_bug_fix/bug_fix_coordinator.py +177 -177
- tapps_agents/continuous_bug_fix/commit_manager.py +178 -178
- tapps_agents/continuous_bug_fix/continuous_bug_fixer.py +322 -322
- tapps_agents/continuous_bug_fix/proactive_bug_finder.py +285 -285
- tapps_agents/core/__init__.py +298 -298
- tapps_agents/core/adaptive_cache_config.py +432 -432
- tapps_agents/core/agent_base.py +647 -647
- tapps_agents/core/agent_cache.py +466 -466
- tapps_agents/core/agent_learning.py +1865 -1865
- tapps_agents/core/analytics_dashboard.py +563 -563
- tapps_agents/core/analytics_enhancements.py +597 -597
- tapps_agents/core/anonymization.py +274 -274
- tapps_agents/core/artifact_context_builder.py +293 -0
- tapps_agents/core/ast_parser.py +228 -228
- tapps_agents/core/async_file_ops.py +402 -402
- tapps_agents/core/best_practice_consultant.py +299 -299
- tapps_agents/core/brownfield_analyzer.py +299 -299
- tapps_agents/core/brownfield_review.py +541 -541
- tapps_agents/core/browser_controller.py +513 -513
- tapps_agents/core/capability_registry.py +418 -418
- tapps_agents/core/change_impact_analyzer.py +190 -190
- tapps_agents/core/checkpoint_manager.py +377 -377
- tapps_agents/core/code_generator.py +329 -329
- tapps_agents/core/code_validator.py +276 -276
- tapps_agents/core/command_registry.py +327 -327
- tapps_agents/core/config.py +33 -0
- tapps_agents/core/context_gathering/__init__.py +2 -2
- tapps_agents/core/context_gathering/repository_explorer.py +28 -28
- tapps_agents/core/context_intelligence/__init__.py +2 -2
- tapps_agents/core/context_intelligence/relevance_scorer.py +24 -24
- tapps_agents/core/context_intelligence/token_budget_manager.py +27 -27
- tapps_agents/core/context_manager.py +240 -240
- tapps_agents/core/cursor_feedback_monitor.py +146 -146
- tapps_agents/core/cursor_verification.py +290 -290
- tapps_agents/core/customization_loader.py +280 -280
- tapps_agents/core/customization_schema.py +260 -260
- tapps_agents/core/customization_template.py +238 -238
- tapps_agents/core/debug_logger.py +124 -124
- tapps_agents/core/design_validator.py +298 -298
- tapps_agents/core/diagram_generator.py +226 -226
- tapps_agents/core/docker_utils.py +232 -232
- tapps_agents/core/document_generator.py +617 -617
- tapps_agents/core/domain_detector.py +30 -30
- tapps_agents/core/error_envelope.py +454 -454
- tapps_agents/core/error_handler.py +270 -270
- tapps_agents/core/estimation_tracker.py +189 -189
- tapps_agents/core/eval_prompt_engine.py +116 -116
- tapps_agents/core/evaluation_base.py +119 -119
- tapps_agents/core/evaluation_models.py +320 -320
- tapps_agents/core/evaluation_orchestrator.py +225 -225
- tapps_agents/core/evaluators/__init__.py +7 -7
- tapps_agents/core/evaluators/architectural_evaluator.py +205 -205
- tapps_agents/core/evaluators/behavioral_evaluator.py +160 -160
- tapps_agents/core/evaluators/performance_profile_evaluator.py +160 -160
- tapps_agents/core/evaluators/security_posture_evaluator.py +148 -148
- tapps_agents/core/evaluators/spec_compliance_evaluator.py +181 -181
- tapps_agents/core/exceptions.py +107 -107
- tapps_agents/core/expert_config_generator.py +293 -293
- tapps_agents/core/export_schema.py +202 -202
- tapps_agents/core/external_feedback_models.py +102 -102
- tapps_agents/core/external_feedback_storage.py +213 -213
- tapps_agents/core/fallback_strategy.py +314 -314
- tapps_agents/core/feedback_analyzer.py +162 -162
- tapps_agents/core/feedback_collector.py +178 -178
- tapps_agents/core/git_operations.py +445 -445
- tapps_agents/core/hardware_profiler.py +151 -151
- tapps_agents/core/instructions.py +324 -324
- tapps_agents/core/io_guardrails.py +69 -69
- tapps_agents/core/issue_manifest.py +249 -249
- tapps_agents/core/issue_schema.py +139 -139
- tapps_agents/core/json_utils.py +128 -128
- tapps_agents/core/knowledge_graph.py +446 -446
- tapps_agents/core/language_detector.py +296 -296
- tapps_agents/core/learning_confidence.py +242 -242
- tapps_agents/core/learning_dashboard.py +246 -246
- tapps_agents/core/learning_decision.py +384 -384
- tapps_agents/core/learning_explainability.py +578 -578
- tapps_agents/core/learning_export.py +287 -287
- tapps_agents/core/learning_integration.py +228 -228
- tapps_agents/core/llm_behavior.py +232 -232
- tapps_agents/core/long_duration_support.py +786 -786
- tapps_agents/core/mcp_setup.py +106 -106
- tapps_agents/core/memory_integration.py +396 -396
- tapps_agents/core/meta_learning.py +666 -666
- tapps_agents/core/module_path_sanitizer.py +199 -199
- tapps_agents/core/multi_agent_orchestrator.py +382 -382
- tapps_agents/core/network_errors.py +125 -125
- tapps_agents/core/nfr_validator.py +336 -336
- tapps_agents/core/offline_mode.py +158 -158
- tapps_agents/core/output_contracts.py +300 -300
- tapps_agents/core/output_formatter.py +300 -300
- tapps_agents/core/path_normalizer.py +174 -174
- tapps_agents/core/path_validator.py +322 -322
- tapps_agents/core/pattern_library.py +250 -250
- tapps_agents/core/performance_benchmark.py +301 -301
- tapps_agents/core/performance_monitor.py +184 -184
- tapps_agents/core/playwright_mcp_controller.py +771 -771
- tapps_agents/core/policy_loader.py +135 -135
- tapps_agents/core/progress.py +166 -166
- tapps_agents/core/project_profile.py +354 -354
- tapps_agents/core/project_type_detector.py +454 -454
- tapps_agents/core/prompt_base.py +223 -223
- tapps_agents/core/prompt_learning/__init__.py +2 -2
- tapps_agents/core/prompt_learning/learning_loop.py +24 -24
- tapps_agents/core/prompt_learning/project_prompt_store.py +25 -25
- tapps_agents/core/prompt_learning/skills_prompt_analyzer.py +35 -35
- tapps_agents/core/prompt_optimization/__init__.py +6 -6
- tapps_agents/core/prompt_optimization/ab_tester.py +114 -114
- tapps_agents/core/prompt_optimization/correlation_analyzer.py +160 -160
- tapps_agents/core/prompt_optimization/progressive_refiner.py +129 -129
- tapps_agents/core/prompt_optimization/prompt_library.py +37 -37
- tapps_agents/core/requirements_evaluator.py +431 -431
- tapps_agents/core/resource_aware_executor.py +449 -449
- tapps_agents/core/resource_monitor.py +343 -343
- tapps_agents/core/resume_handler.py +298 -298
- tapps_agents/core/retry_handler.py +197 -197
- tapps_agents/core/review_checklists.py +479 -479
- tapps_agents/core/role_loader.py +201 -201
- tapps_agents/core/role_template_loader.py +201 -201
- tapps_agents/core/runtime_mode.py +60 -60
- tapps_agents/core/security_scanner.py +342 -342
- tapps_agents/core/skill_agent_registry.py +194 -194
- tapps_agents/core/skill_integration.py +208 -208
- tapps_agents/core/skill_loader.py +492 -492
- tapps_agents/core/skill_template.py +341 -341
- tapps_agents/core/skill_validator.py +478 -478
- tapps_agents/core/stack_analyzer.py +35 -35
- tapps_agents/core/startup.py +174 -174
- tapps_agents/core/storage_manager.py +397 -397
- tapps_agents/core/storage_models.py +166 -166
- tapps_agents/core/story_evaluator.py +410 -410
- tapps_agents/core/subprocess_utils.py +170 -170
- tapps_agents/core/task_duration.py +296 -296
- tapps_agents/core/task_memory.py +582 -582
- tapps_agents/core/task_state.py +226 -226
- tapps_agents/core/tech_stack_priorities.py +208 -208
- tapps_agents/core/temp_directory.py +194 -194
- tapps_agents/core/template_merger.py +600 -600
- tapps_agents/core/template_selector.py +280 -280
- tapps_agents/core/test_generator.py +286 -286
- tapps_agents/core/tiered_context.py +253 -253
- tapps_agents/core/token_monitor.py +345 -345
- tapps_agents/core/traceability.py +254 -254
- tapps_agents/core/trajectory_tracker.py +50 -50
- tapps_agents/core/unicode_safe.py +143 -143
- tapps_agents/core/unified_cache_config.py +170 -170
- tapps_agents/core/unified_state.py +324 -324
- tapps_agents/core/validate_cursor_setup.py +237 -237
- tapps_agents/core/validation_registry.py +136 -136
- tapps_agents/core/validators/__init__.py +4 -4
- tapps_agents/core/validators/python_validator.py +87 -87
- tapps_agents/core/verification_agent.py +90 -90
- tapps_agents/core/visual_feedback.py +644 -644
- tapps_agents/core/workflow_validator.py +197 -197
- tapps_agents/core/worktree.py +367 -367
- tapps_agents/docker/__init__.py +10 -10
- tapps_agents/docker/analyzer.py +186 -186
- tapps_agents/docker/debugger.py +229 -229
- tapps_agents/docker/error_patterns.py +216 -216
- tapps_agents/epic/__init__.py +22 -22
- tapps_agents/epic/beads_sync.py +115 -115
- tapps_agents/epic/markdown_sync.py +105 -105
- tapps_agents/epic/models.py +96 -96
- tapps_agents/experts/__init__.py +163 -163
- tapps_agents/experts/agent_integration.py +243 -243
- tapps_agents/experts/auto_generator.py +331 -331
- tapps_agents/experts/base_expert.py +536 -536
- tapps_agents/experts/builtin_registry.py +261 -261
- tapps_agents/experts/business_metrics.py +565 -565
- tapps_agents/experts/cache.py +266 -266
- tapps_agents/experts/confidence_breakdown.py +306 -306
- tapps_agents/experts/confidence_calculator.py +336 -336
- tapps_agents/experts/confidence_metrics.py +236 -236
- tapps_agents/experts/domain_config.py +311 -311
- tapps_agents/experts/domain_detector.py +550 -550
- tapps_agents/experts/domain_utils.py +84 -84
- tapps_agents/experts/expert_config.py +113 -113
- tapps_agents/experts/expert_engine.py +465 -465
- tapps_agents/experts/expert_registry.py +744 -744
- tapps_agents/experts/expert_synthesizer.py +70 -70
- tapps_agents/experts/governance.py +197 -197
- tapps_agents/experts/history_logger.py +312 -312
- tapps_agents/experts/knowledge/README.md +180 -180
- tapps_agents/experts/knowledge/accessibility/accessible-forms.md +331 -331
- tapps_agents/experts/knowledge/accessibility/aria-patterns.md +344 -344
- tapps_agents/experts/knowledge/accessibility/color-contrast.md +285 -285
- tapps_agents/experts/knowledge/accessibility/keyboard-navigation.md +332 -332
- tapps_agents/experts/knowledge/accessibility/screen-readers.md +282 -282
- tapps_agents/experts/knowledge/accessibility/semantic-html.md +355 -355
- tapps_agents/experts/knowledge/accessibility/testing-accessibility.md +369 -369
- tapps_agents/experts/knowledge/accessibility/wcag-2.1.md +296 -296
- tapps_agents/experts/knowledge/accessibility/wcag-2.2.md +211 -211
- tapps_agents/experts/knowledge/agent-learning/best-practices.md +715 -715
- tapps_agents/experts/knowledge/agent-learning/pattern-extraction.md +282 -282
- tapps_agents/experts/knowledge/agent-learning/prompt-optimization.md +320 -320
- tapps_agents/experts/knowledge/ai-frameworks/model-optimization.md +90 -90
- tapps_agents/experts/knowledge/ai-frameworks/openvino-patterns.md +260 -260
- tapps_agents/experts/knowledge/api-design-integration/api-gateway-patterns.md +309 -309
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +521 -521
- tapps_agents/experts/knowledge/api-design-integration/api-versioning.md +421 -421
- tapps_agents/experts/knowledge/api-design-integration/async-protocol-patterns.md +61 -61
- tapps_agents/experts/knowledge/api-design-integration/contract-testing.md +221 -221
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +489 -489
- tapps_agents/experts/knowledge/api-design-integration/fastapi-patterns.md +360 -360
- tapps_agents/experts/knowledge/api-design-integration/fastapi-testing.md +262 -262
- tapps_agents/experts/knowledge/api-design-integration/graphql-patterns.md +582 -582
- tapps_agents/experts/knowledge/api-design-integration/grpc-best-practices.md +499 -499
- tapps_agents/experts/knowledge/api-design-integration/mqtt-patterns.md +455 -455
- tapps_agents/experts/knowledge/api-design-integration/rate-limiting.md +507 -507
- tapps_agents/experts/knowledge/api-design-integration/restful-api-design.md +618 -618
- tapps_agents/experts/knowledge/api-design-integration/websocket-patterns.md +480 -480
- tapps_agents/experts/knowledge/cloud-infrastructure/cloud-native-patterns.md +175 -175
- tapps_agents/experts/knowledge/cloud-infrastructure/container-health-checks.md +261 -261
- tapps_agents/experts/knowledge/cloud-infrastructure/containerization.md +222 -222
- tapps_agents/experts/knowledge/cloud-infrastructure/cost-optimization.md +122 -122
- tapps_agents/experts/knowledge/cloud-infrastructure/disaster-recovery.md +153 -153
- tapps_agents/experts/knowledge/cloud-infrastructure/dockerfile-patterns.md +285 -285
- tapps_agents/experts/knowledge/cloud-infrastructure/infrastructure-as-code.md +187 -187
- tapps_agents/experts/knowledge/cloud-infrastructure/kubernetes-patterns.md +253 -253
- tapps_agents/experts/knowledge/cloud-infrastructure/multi-cloud-strategies.md +155 -155
- tapps_agents/experts/knowledge/cloud-infrastructure/serverless-architecture.md +200 -200
- tapps_agents/experts/knowledge/code-quality-analysis/README.md +16 -16
- tapps_agents/experts/knowledge/code-quality-analysis/code-metrics.md +137 -137
- tapps_agents/experts/knowledge/code-quality-analysis/complexity-analysis.md +181 -181
- tapps_agents/experts/knowledge/code-quality-analysis/technical-debt-patterns.md +191 -191
- tapps_agents/experts/knowledge/data-privacy-compliance/anonymization.md +313 -313
- tapps_agents/experts/knowledge/data-privacy-compliance/ccpa.md +255 -255
- tapps_agents/experts/knowledge/data-privacy-compliance/consent-management.md +282 -282
- tapps_agents/experts/knowledge/data-privacy-compliance/data-minimization.md +275 -275
- tapps_agents/experts/knowledge/data-privacy-compliance/data-retention.md +297 -297
- tapps_agents/experts/knowledge/data-privacy-compliance/data-subject-rights.md +383 -383
- tapps_agents/experts/knowledge/data-privacy-compliance/encryption-privacy.md +285 -285
- tapps_agents/experts/knowledge/data-privacy-compliance/gdpr.md +344 -344
- tapps_agents/experts/knowledge/data-privacy-compliance/hipaa.md +385 -385
- tapps_agents/experts/knowledge/data-privacy-compliance/privacy-by-design.md +280 -280
- tapps_agents/experts/knowledge/database-data-management/acid-vs-cap.md +164 -164
- tapps_agents/experts/knowledge/database-data-management/backup-and-recovery.md +182 -182
- tapps_agents/experts/knowledge/database-data-management/data-modeling.md +172 -172
- tapps_agents/experts/knowledge/database-data-management/database-design.md +187 -187
- tapps_agents/experts/knowledge/database-data-management/flux-query-optimization.md +342 -342
- tapps_agents/experts/knowledge/database-data-management/influxdb-connection-patterns.md +432 -432
- tapps_agents/experts/knowledge/database-data-management/influxdb-patterns.md +442 -442
- tapps_agents/experts/knowledge/database-data-management/migration-strategies.md +216 -216
- tapps_agents/experts/knowledge/database-data-management/nosql-patterns.md +259 -259
- tapps_agents/experts/knowledge/database-data-management/scalability-patterns.md +184 -184
- tapps_agents/experts/knowledge/database-data-management/sql-optimization.md +175 -175
- tapps_agents/experts/knowledge/database-data-management/time-series-modeling.md +444 -444
- tapps_agents/experts/knowledge/development-workflow/README.md +16 -16
- tapps_agents/experts/knowledge/development-workflow/automation-best-practices.md +216 -216
- tapps_agents/experts/knowledge/development-workflow/build-strategies.md +198 -198
- tapps_agents/experts/knowledge/development-workflow/deployment-patterns.md +205 -205
- tapps_agents/experts/knowledge/development-workflow/git-workflows.md +205 -205
- tapps_agents/experts/knowledge/documentation-knowledge-management/README.md +16 -16
- tapps_agents/experts/knowledge/documentation-knowledge-management/api-documentation-patterns.md +231 -231
- tapps_agents/experts/knowledge/documentation-knowledge-management/documentation-standards.md +191 -191
- tapps_agents/experts/knowledge/documentation-knowledge-management/knowledge-management.md +171 -171
- tapps_agents/experts/knowledge/documentation-knowledge-management/technical-writing-guide.md +192 -192
- tapps_agents/experts/knowledge/observability-monitoring/alerting-patterns.md +461 -461
- tapps_agents/experts/knowledge/observability-monitoring/apm-tools.md +459 -459
- tapps_agents/experts/knowledge/observability-monitoring/distributed-tracing.md +367 -367
- tapps_agents/experts/knowledge/observability-monitoring/logging-strategies.md +478 -478
- tapps_agents/experts/knowledge/observability-monitoring/metrics-and-monitoring.md +510 -510
- tapps_agents/experts/knowledge/observability-monitoring/observability-best-practices.md +492 -492
- tapps_agents/experts/knowledge/observability-monitoring/open-telemetry.md +573 -573
- tapps_agents/experts/knowledge/observability-monitoring/slo-sli-sla.md +419 -419
- tapps_agents/experts/knowledge/performance/anti-patterns.md +284 -284
- tapps_agents/experts/knowledge/performance/api-performance.md +256 -256
- tapps_agents/experts/knowledge/performance/caching.md +327 -327
- tapps_agents/experts/knowledge/performance/database-performance.md +252 -252
- tapps_agents/experts/knowledge/performance/optimization-patterns.md +327 -327
- tapps_agents/experts/knowledge/performance/profiling.md +297 -297
- tapps_agents/experts/knowledge/performance/resource-management.md +293 -293
- tapps_agents/experts/knowledge/performance/scalability.md +306 -306
- tapps_agents/experts/knowledge/security/owasp-top10.md +209 -209
- tapps_agents/experts/knowledge/security/secure-coding-practices.md +207 -207
- tapps_agents/experts/knowledge/security/threat-modeling.md +220 -220
- tapps_agents/experts/knowledge/security/vulnerability-patterns.md +342 -342
- tapps_agents/experts/knowledge/software-architecture/docker-compose-patterns.md +314 -314
- tapps_agents/experts/knowledge/software-architecture/microservices-patterns.md +379 -379
- tapps_agents/experts/knowledge/software-architecture/service-communication.md +316 -316
- tapps_agents/experts/knowledge/testing/best-practices.md +310 -310
- tapps_agents/experts/knowledge/testing/coverage-analysis.md +293 -293
- tapps_agents/experts/knowledge/testing/mocking.md +256 -256
- tapps_agents/experts/knowledge/testing/test-automation.md +276 -276
- tapps_agents/experts/knowledge/testing/test-data.md +271 -271
- tapps_agents/experts/knowledge/testing/test-design-patterns.md +280 -280
- tapps_agents/experts/knowledge/testing/test-maintenance.md +236 -236
- tapps_agents/experts/knowledge/testing/test-strategies.md +311 -311
- tapps_agents/experts/knowledge/user-experience/information-architecture.md +325 -325
- tapps_agents/experts/knowledge/user-experience/interaction-design.md +363 -363
- tapps_agents/experts/knowledge/user-experience/prototyping.md +293 -293
- tapps_agents/experts/knowledge/user-experience/usability-heuristics.md +337 -337
- tapps_agents/experts/knowledge/user-experience/usability-testing.md +311 -311
- tapps_agents/experts/knowledge/user-experience/user-journeys.md +296 -296
- tapps_agents/experts/knowledge/user-experience/user-research.md +373 -373
- tapps_agents/experts/knowledge/user-experience/ux-principles.md +340 -340
- tapps_agents/experts/knowledge_freshness.py +321 -321
- tapps_agents/experts/knowledge_ingestion.py +438 -438
- tapps_agents/experts/knowledge_need_detector.py +93 -93
- tapps_agents/experts/knowledge_validator.py +382 -382
- tapps_agents/experts/observability.py +440 -440
- tapps_agents/experts/passive_notifier.py +238 -238
- tapps_agents/experts/proactive_orchestrator.py +32 -32
- tapps_agents/experts/rag_chunker.py +205 -205
- tapps_agents/experts/rag_embedder.py +152 -152
- tapps_agents/experts/rag_evaluation.py +299 -299
- tapps_agents/experts/rag_index.py +303 -303
- tapps_agents/experts/rag_metrics.py +293 -293
- tapps_agents/experts/rag_safety.py +263 -263
- tapps_agents/experts/report_generator.py +296 -296
- tapps_agents/experts/setup_wizard.py +441 -441
- tapps_agents/experts/simple_rag.py +431 -431
- tapps_agents/experts/vector_rag.py +354 -354
- tapps_agents/experts/weight_distributor.py +304 -304
- tapps_agents/health/__init__.py +24 -24
- tapps_agents/health/base.py +75 -75
- tapps_agents/health/checks/__init__.py +22 -22
- tapps_agents/health/checks/automation.py +127 -127
- tapps_agents/health/checks/context7_cache.py +210 -210
- tapps_agents/health/checks/environment.py +116 -116
- tapps_agents/health/checks/execution.py +170 -170
- tapps_agents/health/checks/knowledge_base.py +187 -187
- tapps_agents/health/checks/outcomes.py +324 -324
- tapps_agents/health/collector.py +280 -280
- tapps_agents/health/dashboard.py +137 -137
- tapps_agents/health/metrics.py +151 -151
- tapps_agents/health/orchestrator.py +271 -271
- tapps_agents/health/registry.py +166 -166
- tapps_agents/hooks/__init__.py +33 -33
- tapps_agents/hooks/config.py +140 -140
- tapps_agents/hooks/events.py +135 -135
- tapps_agents/hooks/executor.py +128 -128
- tapps_agents/hooks/manager.py +143 -143
- tapps_agents/integration/__init__.py +8 -8
- tapps_agents/integration/service_integrator.py +121 -121
- tapps_agents/integrations/__init__.py +10 -10
- tapps_agents/integrations/clawdbot.py +525 -525
- tapps_agents/integrations/memory_bridge.py +356 -356
- tapps_agents/mcp/__init__.py +18 -18
- tapps_agents/mcp/gateway.py +112 -112
- tapps_agents/mcp/servers/__init__.py +13 -13
- tapps_agents/mcp/servers/analysis.py +204 -204
- tapps_agents/mcp/servers/context7.py +198 -198
- tapps_agents/mcp/servers/filesystem.py +218 -218
- tapps_agents/mcp/servers/git.py +201 -201
- tapps_agents/mcp/tool_registry.py +115 -115
- tapps_agents/quality/__init__.py +54 -54
- tapps_agents/quality/coverage_analyzer.py +379 -379
- tapps_agents/quality/enforcement.py +82 -82
- tapps_agents/quality/gates/__init__.py +37 -37
- tapps_agents/quality/gates/approval_gate.py +255 -255
- tapps_agents/quality/gates/base.py +84 -84
- tapps_agents/quality/gates/exceptions.py +43 -43
- tapps_agents/quality/gates/policy_gate.py +195 -195
- tapps_agents/quality/gates/registry.py +239 -239
- tapps_agents/quality/gates/security_gate.py +156 -156
- tapps_agents/quality/quality_gates.py +369 -369
- tapps_agents/quality/secret_scanner.py +335 -335
- tapps_agents/session/__init__.py +19 -19
- tapps_agents/session/manager.py +256 -256
- tapps_agents/simple_mode/__init__.py +66 -66
- tapps_agents/simple_mode/agent_contracts.py +357 -357
- tapps_agents/simple_mode/beads_hooks.py +151 -151
- tapps_agents/simple_mode/code_snippet_handler.py +382 -382
- tapps_agents/simple_mode/documentation_manager.py +395 -395
- tapps_agents/simple_mode/documentation_reader.py +187 -187
- tapps_agents/simple_mode/file_inference.py +292 -292
- tapps_agents/simple_mode/framework_change_detector.py +268 -268
- tapps_agents/simple_mode/intent_parser.py +510 -510
- tapps_agents/simple_mode/learning_progression.py +358 -358
- tapps_agents/simple_mode/nl_handler.py +700 -700
- tapps_agents/simple_mode/onboarding.py +253 -253
- tapps_agents/simple_mode/orchestrators/__init__.py +38 -38
- tapps_agents/simple_mode/orchestrators/base.py +185 -185
- tapps_agents/simple_mode/orchestrators/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2700 -2667
- tapps_agents/simple_mode/orchestrators/deliverable_checklist.py +349 -349
- tapps_agents/simple_mode/orchestrators/enhance_orchestrator.py +53 -53
- tapps_agents/simple_mode/orchestrators/epic_orchestrator.py +122 -122
- tapps_agents/simple_mode/orchestrators/explore_orchestrator.py +184 -184
- tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
- tapps_agents/simple_mode/orchestrators/plan_analysis_orchestrator.py +206 -206
- tapps_agents/simple_mode/orchestrators/pr_orchestrator.py +237 -237
- tapps_agents/simple_mode/orchestrators/refactor_orchestrator.py +222 -222
- tapps_agents/simple_mode/orchestrators/requirements_tracer.py +262 -262
- tapps_agents/simple_mode/orchestrators/resume_orchestrator.py +210 -210
- tapps_agents/simple_mode/orchestrators/review_orchestrator.py +161 -161
- tapps_agents/simple_mode/orchestrators/test_orchestrator.py +82 -82
- tapps_agents/simple_mode/output_aggregator.py +340 -340
- tapps_agents/simple_mode/result_formatters.py +598 -598
- tapps_agents/simple_mode/step_dependencies.py +382 -382
- tapps_agents/simple_mode/step_results.py +276 -276
- tapps_agents/simple_mode/streaming.py +388 -388
- tapps_agents/simple_mode/variations.py +129 -129
- tapps_agents/simple_mode/visual_feedback.py +238 -238
- tapps_agents/simple_mode/zero_config.py +274 -274
- tapps_agents/suggestions/__init__.py +8 -8
- tapps_agents/suggestions/inline_suggester.py +52 -52
- tapps_agents/templates/__init__.py +8 -8
- tapps_agents/templates/microservice_generator.py +274 -274
- tapps_agents/utils/env_validator.py +291 -291
- tapps_agents/workflow/__init__.py +171 -171
- tapps_agents/workflow/acceptance_verifier.py +132 -132
- tapps_agents/workflow/agent_handlers/__init__.py +41 -41
- tapps_agents/workflow/agent_handlers/analyst_handler.py +75 -75
- tapps_agents/workflow/agent_handlers/architect_handler.py +107 -107
- tapps_agents/workflow/agent_handlers/base.py +84 -84
- tapps_agents/workflow/agent_handlers/debugger_handler.py +100 -100
- tapps_agents/workflow/agent_handlers/designer_handler.py +110 -110
- tapps_agents/workflow/agent_handlers/documenter_handler.py +94 -94
- tapps_agents/workflow/agent_handlers/implementer_handler.py +235 -235
- tapps_agents/workflow/agent_handlers/ops_handler.py +62 -62
- tapps_agents/workflow/agent_handlers/orchestrator_handler.py +43 -43
- tapps_agents/workflow/agent_handlers/planner_handler.py +98 -98
- tapps_agents/workflow/agent_handlers/registry.py +119 -119
- tapps_agents/workflow/agent_handlers/reviewer_handler.py +119 -119
- tapps_agents/workflow/agent_handlers/tester_handler.py +69 -69
- tapps_agents/workflow/analytics_accessor.py +337 -337
- tapps_agents/workflow/analytics_alerts.py +416 -416
- tapps_agents/workflow/analytics_dashboard_cursor.py +281 -281
- tapps_agents/workflow/analytics_dual_write.py +103 -103
- tapps_agents/workflow/analytics_integration.py +119 -119
- tapps_agents/workflow/analytics_query_parser.py +278 -278
- tapps_agents/workflow/analytics_visualizer.py +259 -259
- tapps_agents/workflow/artifact_helper.py +204 -204
- tapps_agents/workflow/audit_logger.py +263 -263
- tapps_agents/workflow/auto_execution_config.py +340 -340
- tapps_agents/workflow/auto_progression.py +586 -586
- tapps_agents/workflow/branch_cleanup.py +349 -349
- tapps_agents/workflow/checkpoint.py +256 -256
- tapps_agents/workflow/checkpoint_manager.py +178 -178
- tapps_agents/workflow/code_artifact.py +179 -179
- tapps_agents/workflow/common_enums.py +96 -96
- tapps_agents/workflow/confirmation_handler.py +130 -130
- tapps_agents/workflow/context_analyzer.py +222 -222
- tapps_agents/workflow/context_artifact.py +230 -230
- tapps_agents/workflow/cursor_chat.py +94 -94
- tapps_agents/workflow/cursor_executor.py +2337 -2196
- tapps_agents/workflow/cursor_skill_helper.py +516 -516
- tapps_agents/workflow/dependency_resolver.py +244 -244
- tapps_agents/workflow/design_artifact.py +156 -156
- tapps_agents/workflow/detector.py +751 -751
- tapps_agents/workflow/direct_execution_fallback.py +301 -301
- tapps_agents/workflow/docs_artifact.py +168 -168
- tapps_agents/workflow/enforcer.py +389 -389
- tapps_agents/workflow/enhancement_artifact.py +142 -142
- tapps_agents/workflow/error_recovery.py +806 -806
- tapps_agents/workflow/event_bus.py +183 -183
- tapps_agents/workflow/event_log.py +612 -612
- tapps_agents/workflow/events.py +63 -63
- tapps_agents/workflow/exceptions.py +43 -43
- tapps_agents/workflow/execution_graph.py +498 -498
- tapps_agents/workflow/execution_plan.py +126 -126
- tapps_agents/workflow/file_utils.py +186 -186
- tapps_agents/workflow/gate_evaluator.py +182 -182
- tapps_agents/workflow/gate_integration.py +200 -200
- tapps_agents/workflow/graph_visualizer.py +130 -130
- tapps_agents/workflow/health_checker.py +206 -206
- tapps_agents/workflow/logging_helper.py +243 -243
- tapps_agents/workflow/manifest.py +582 -582
- tapps_agents/workflow/marker_writer.py +250 -250
- tapps_agents/workflow/message_formatter.py +188 -188
- tapps_agents/workflow/messaging.py +325 -325
- tapps_agents/workflow/metadata_models.py +91 -91
- tapps_agents/workflow/metrics_integration.py +226 -226
- tapps_agents/workflow/migration_utils.py +116 -116
- tapps_agents/workflow/models.py +148 -111
- tapps_agents/workflow/nlp_config.py +198 -198
- tapps_agents/workflow/nlp_error_handler.py +207 -207
- tapps_agents/workflow/nlp_executor.py +163 -163
- tapps_agents/workflow/nlp_parser.py +528 -528
- tapps_agents/workflow/observability_dashboard.py +451 -451
- tapps_agents/workflow/observer.py +170 -170
- tapps_agents/workflow/ops_artifact.py +257 -257
- tapps_agents/workflow/output_passing.py +214 -214
- tapps_agents/workflow/parallel_executor.py +463 -463
- tapps_agents/workflow/planning_artifact.py +179 -179
- tapps_agents/workflow/preset_loader.py +285 -285
- tapps_agents/workflow/preset_recommender.py +270 -270
- tapps_agents/workflow/progress_logger.py +145 -145
- tapps_agents/workflow/progress_manager.py +303 -303
- tapps_agents/workflow/progress_monitor.py +186 -186
- tapps_agents/workflow/progress_updates.py +423 -423
- tapps_agents/workflow/quality_artifact.py +158 -158
- tapps_agents/workflow/quality_loopback.py +101 -101
- tapps_agents/workflow/recommender.py +387 -387
- tapps_agents/workflow/remediation_loop.py +166 -166
- tapps_agents/workflow/result_aggregator.py +300 -300
- tapps_agents/workflow/review_artifact.py +185 -185
- tapps_agents/workflow/schema_validator.py +522 -522
- tapps_agents/workflow/session_handoff.py +178 -178
- tapps_agents/workflow/skill_invoker.py +648 -648
- tapps_agents/workflow/state_manager.py +756 -756
- tapps_agents/workflow/state_persistence_config.py +331 -331
- tapps_agents/workflow/status_monitor.py +449 -449
- tapps_agents/workflow/step_checkpoint.py +314 -314
- tapps_agents/workflow/step_details.py +201 -201
- tapps_agents/workflow/story_models.py +147 -147
- tapps_agents/workflow/streaming.py +416 -416
- tapps_agents/workflow/suggestion_engine.py +552 -552
- tapps_agents/workflow/testing_artifact.py +186 -186
- tapps_agents/workflow/timeline.py +158 -158
- tapps_agents/workflow/token_integration.py +209 -209
- tapps_agents/workflow/validation.py +217 -217
- tapps_agents/workflow/visual_feedback.py +391 -391
- tapps_agents/workflow/workflow_chain.py +95 -95
- tapps_agents/workflow/workflow_summary.py +219 -219
- tapps_agents/workflow/worktree_manager.py +724 -724
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/METADATA +672 -672
- tapps_agents-3.6.0.dist-info/RECORD +758 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/licenses/LICENSE +22 -22
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +0 -324
- tapps_agents-3.5.40.dist-info/RECORD +0 -760
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,513 +1,513 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Browser Controller for UI Rendering and Interaction
|
|
3
|
-
|
|
4
|
-
Provides headless browser control for visual feedback collection.
|
|
5
|
-
Supports Playwright with optional Selenium fallback.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import logging
|
|
11
|
-
import time
|
|
12
|
-
from dataclasses import dataclass
|
|
13
|
-
from enum import Enum
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
from typing import Any
|
|
16
|
-
|
|
17
|
-
from .hardware_profiler import HardwareProfile, HardwareProfiler
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
# Try to import Playwright, fallback to mock if not available
|
|
22
|
-
try:
|
|
23
|
-
from playwright.sync_api import Browser, BrowserContext, Page, sync_playwright
|
|
24
|
-
|
|
25
|
-
HAS_PLAYWRIGHT = True
|
|
26
|
-
except ImportError:
|
|
27
|
-
HAS_PLAYWRIGHT = False
|
|
28
|
-
# Keep these as `Any` so runtime imports don't break annotations when Playwright
|
|
29
|
-
# isn't installed (and so mypy doesn't infer `NoneType` here).
|
|
30
|
-
Browser = Any
|
|
31
|
-
Page = Any
|
|
32
|
-
BrowserContext = Any
|
|
33
|
-
# Do not warn at import time. Many environments (especially Cursor-first setups)
|
|
34
|
-
# may rely on Browser automation via Cursor MCP rather than the Python Playwright
|
|
35
|
-
# package. We'll log only when/if browser operations are actually requested.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class BrowserType(Enum):
|
|
39
|
-
"""Browser types."""
|
|
40
|
-
|
|
41
|
-
CHROMIUM = "chromium"
|
|
42
|
-
FIREFOX = "firefox"
|
|
43
|
-
WEBKIT = "webkit"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class RenderingMode(Enum):
|
|
47
|
-
"""Rendering mode for browser operations."""
|
|
48
|
-
|
|
49
|
-
LOCAL = "local" # Local browser rendering
|
|
50
|
-
CLOUD = "cloud" # Cloud rendering service (for NUC devices)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@dataclass
|
|
54
|
-
class ScreenshotOptions:
|
|
55
|
-
"""Options for screenshot capture."""
|
|
56
|
-
|
|
57
|
-
full_page: bool = False
|
|
58
|
-
clip: dict[str, float] | None = None # {x, y, width, height}
|
|
59
|
-
quality: int = 90 # 0-100
|
|
60
|
-
timeout: float = 30.0
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@dataclass
|
|
64
|
-
class InteractionEvent:
|
|
65
|
-
"""User interaction event."""
|
|
66
|
-
|
|
67
|
-
event_type: str # "click", "type", "scroll", "hover", "keypress"
|
|
68
|
-
selector: str | None = None
|
|
69
|
-
text: str | None = None
|
|
70
|
-
key: str | None = None
|
|
71
|
-
coordinates: tuple[float, float] | None = None
|
|
72
|
-
timestamp: float = 0.0
|
|
73
|
-
metadata: dict[str, Any] | None = None
|
|
74
|
-
|
|
75
|
-
def __post_init__(self):
|
|
76
|
-
if self.metadata is None:
|
|
77
|
-
self.metadata = {}
|
|
78
|
-
if self.timestamp == 0.0:
|
|
79
|
-
self.timestamp = time.time()
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class BrowserController:
|
|
83
|
-
"""Controls headless browser for UI rendering."""
|
|
84
|
-
|
|
85
|
-
def __init__(
|
|
86
|
-
self,
|
|
87
|
-
hardware_profile: HardwareProfile | None = None,
|
|
88
|
-
browser_type: BrowserType = BrowserType.CHROMIUM,
|
|
89
|
-
headless: bool = True,
|
|
90
|
-
):
|
|
91
|
-
"""
|
|
92
|
-
Initialize browser controller.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
hardware_profile: Hardware profile for optimization
|
|
96
|
-
browser_type: Browser type to use
|
|
97
|
-
headless: Run browser in headless mode
|
|
98
|
-
"""
|
|
99
|
-
self.hardware_profile = hardware_profile or HardwareProfiler().detect_profile()
|
|
100
|
-
self.browser_type = browser_type
|
|
101
|
-
self.headless = headless
|
|
102
|
-
self.rendering_mode = self._get_rendering_mode()
|
|
103
|
-
|
|
104
|
-
self.playwright: Any | None = None
|
|
105
|
-
self.browser: Browser | None = None
|
|
106
|
-
self.context: BrowserContext | None = None
|
|
107
|
-
self.page: Page | None = None
|
|
108
|
-
|
|
109
|
-
self.interaction_history: list[InteractionEvent] = []
|
|
110
|
-
|
|
111
|
-
def _get_rendering_mode(self) -> RenderingMode:
|
|
112
|
-
"""Rendering mode. Workstation-like (hardware taxonomy removed)."""
|
|
113
|
-
return RenderingMode.LOCAL
|
|
114
|
-
|
|
115
|
-
def start(self) -> bool:
|
|
116
|
-
"""
|
|
117
|
-
Start browser instance.
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
True if successful, False otherwise
|
|
121
|
-
"""
|
|
122
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
123
|
-
logger.info("Using cloud rendering fallback (NUC device)")
|
|
124
|
-
return True # Cloud rendering doesn't need local browser
|
|
125
|
-
|
|
126
|
-
if not HAS_PLAYWRIGHT:
|
|
127
|
-
logger.info(
|
|
128
|
-
"Playwright (Python package) not installed; local browser operations are disabled. "
|
|
129
|
-
"If you're running inside Cursor and have Playwright configured as an MCP tool, "
|
|
130
|
-
"use Cursor Skills/Background Agents for browser automation. "
|
|
131
|
-
"For CLI/browser_controller usage, install Playwright: `pip install playwright` "
|
|
132
|
-
"and run `python -m playwright install`."
|
|
133
|
-
)
|
|
134
|
-
return False
|
|
135
|
-
|
|
136
|
-
try:
|
|
137
|
-
self.playwright = sync_playwright().start()
|
|
138
|
-
if self.playwright is None:
|
|
139
|
-
logger.error("Failed to initialize Playwright")
|
|
140
|
-
return False
|
|
141
|
-
|
|
142
|
-
browser_map = {
|
|
143
|
-
BrowserType.CHROMIUM: self.playwright.chromium,
|
|
144
|
-
BrowserType.FIREFOX: self.playwright.firefox,
|
|
145
|
-
BrowserType.WEBKIT: self.playwright.webkit,
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
browser_launcher = browser_map.get(
|
|
149
|
-
self.browser_type, self.playwright.chromium
|
|
150
|
-
)
|
|
151
|
-
self.browser = browser_launcher.launch(headless=self.headless)
|
|
152
|
-
self.context = self.browser.new_context()
|
|
153
|
-
self.page = self.context.new_page()
|
|
154
|
-
|
|
155
|
-
logger.info(f"Browser started: {self.browser_type.value}")
|
|
156
|
-
return True
|
|
157
|
-
except Exception as e:
|
|
158
|
-
logger.error(f"Failed to start browser: {e}")
|
|
159
|
-
return False
|
|
160
|
-
|
|
161
|
-
def stop(self):
|
|
162
|
-
"""Stop browser instance."""
|
|
163
|
-
if self.page:
|
|
164
|
-
self.page.close()
|
|
165
|
-
self.page = None
|
|
166
|
-
if self.context:
|
|
167
|
-
self.context.close()
|
|
168
|
-
self.context = None
|
|
169
|
-
if self.browser:
|
|
170
|
-
self.browser.close()
|
|
171
|
-
self.browser = None
|
|
172
|
-
if self.playwright:
|
|
173
|
-
self.playwright.stop()
|
|
174
|
-
self.playwright = None
|
|
175
|
-
|
|
176
|
-
logger.info("Browser stopped")
|
|
177
|
-
|
|
178
|
-
def navigate(
|
|
179
|
-
self, url: str, wait_until: str = "load", timeout: float = 30.0
|
|
180
|
-
) -> bool:
|
|
181
|
-
"""
|
|
182
|
-
Navigate to URL.
|
|
183
|
-
|
|
184
|
-
Args:
|
|
185
|
-
url: URL to navigate to
|
|
186
|
-
wait_until: Wait condition ("load", "domcontentloaded", "networkidle")
|
|
187
|
-
timeout: Navigation timeout in seconds
|
|
188
|
-
|
|
189
|
-
Returns:
|
|
190
|
-
True if successful, False otherwise
|
|
191
|
-
"""
|
|
192
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
193
|
-
logger.info(f"Cloud navigation to: {url}")
|
|
194
|
-
return True # Mock for cloud rendering
|
|
195
|
-
|
|
196
|
-
if not self.page:
|
|
197
|
-
logger.error("Browser not started")
|
|
198
|
-
return False
|
|
199
|
-
|
|
200
|
-
try:
|
|
201
|
-
self.page.goto(url, wait_until=wait_until, timeout=timeout * 1000)
|
|
202
|
-
logger.info(f"Navigated to: {url}")
|
|
203
|
-
return True
|
|
204
|
-
except Exception as e:
|
|
205
|
-
logger.error(f"Navigation failed: {e}")
|
|
206
|
-
return False
|
|
207
|
-
|
|
208
|
-
def load_html(self, html_content: str, base_url: str = "http://localhost") -> bool:
|
|
209
|
-
"""
|
|
210
|
-
Load HTML content directly.
|
|
211
|
-
|
|
212
|
-
Args:
|
|
213
|
-
html_content: HTML content to load
|
|
214
|
-
base_url: Base URL for relative resources
|
|
215
|
-
|
|
216
|
-
Returns:
|
|
217
|
-
True if successful, False otherwise
|
|
218
|
-
"""
|
|
219
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
220
|
-
logger.info("Cloud HTML load")
|
|
221
|
-
return True # Mock for cloud rendering
|
|
222
|
-
|
|
223
|
-
if not self.page:
|
|
224
|
-
logger.error("Browser not started")
|
|
225
|
-
return False
|
|
226
|
-
|
|
227
|
-
try:
|
|
228
|
-
self.page.set_content(html_content, wait_until="domcontentloaded")
|
|
229
|
-
logger.info("HTML content loaded")
|
|
230
|
-
return True
|
|
231
|
-
except Exception as e:
|
|
232
|
-
logger.error(f"HTML load failed: {e}")
|
|
233
|
-
return False
|
|
234
|
-
|
|
235
|
-
def capture_screenshot(
|
|
236
|
-
self, output_path: str, options: ScreenshotOptions | None = None
|
|
237
|
-
) -> bool:
|
|
238
|
-
"""
|
|
239
|
-
Capture screenshot of current page.
|
|
240
|
-
|
|
241
|
-
Args:
|
|
242
|
-
output_path: Path to save screenshot
|
|
243
|
-
options: Screenshot options
|
|
244
|
-
|
|
245
|
-
Returns:
|
|
246
|
-
True if successful, False otherwise
|
|
247
|
-
"""
|
|
248
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
249
|
-
logger.info(f"Cloud screenshot to: {output_path}")
|
|
250
|
-
# In real implementation, would call cloud rendering service
|
|
251
|
-
Path(output_path).touch() # Create empty file as placeholder
|
|
252
|
-
return True
|
|
253
|
-
|
|
254
|
-
if not self.page:
|
|
255
|
-
logger.error("Browser not started")
|
|
256
|
-
return False
|
|
257
|
-
|
|
258
|
-
if options is None:
|
|
259
|
-
options = ScreenshotOptions()
|
|
260
|
-
|
|
261
|
-
try:
|
|
262
|
-
screenshot_kwargs = {
|
|
263
|
-
"path": output_path,
|
|
264
|
-
"full_page": options.full_page,
|
|
265
|
-
"timeout": options.timeout * 1000,
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if options.clip:
|
|
269
|
-
screenshot_kwargs["clip"] = options.clip
|
|
270
|
-
|
|
271
|
-
if options.quality is not None:
|
|
272
|
-
screenshot_kwargs["quality"] = options.quality
|
|
273
|
-
|
|
274
|
-
self.page.screenshot(**screenshot_kwargs)
|
|
275
|
-
logger.info(f"Screenshot saved: {output_path}")
|
|
276
|
-
return True
|
|
277
|
-
except Exception as e:
|
|
278
|
-
logger.error(f"Screenshot capture failed: {e}")
|
|
279
|
-
return False
|
|
280
|
-
|
|
281
|
-
def click(self, selector: str, timeout: float = 5.0) -> bool:
|
|
282
|
-
"""
|
|
283
|
-
Click an element.
|
|
284
|
-
|
|
285
|
-
Args:
|
|
286
|
-
selector: CSS selector
|
|
287
|
-
timeout: Wait timeout in seconds
|
|
288
|
-
|
|
289
|
-
Returns:
|
|
290
|
-
True if successful, False otherwise
|
|
291
|
-
"""
|
|
292
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
293
|
-
event = InteractionEvent(event_type="click", selector=selector)
|
|
294
|
-
self.interaction_history.append(event)
|
|
295
|
-
return True
|
|
296
|
-
|
|
297
|
-
if not self.page:
|
|
298
|
-
logger.error("Browser not started")
|
|
299
|
-
return False
|
|
300
|
-
|
|
301
|
-
try:
|
|
302
|
-
self.page.click(selector, timeout=timeout * 1000)
|
|
303
|
-
event = InteractionEvent(event_type="click", selector=selector)
|
|
304
|
-
self.interaction_history.append(event)
|
|
305
|
-
logger.debug(f"Clicked: {selector}")
|
|
306
|
-
return True
|
|
307
|
-
except Exception as e:
|
|
308
|
-
logger.error(f"Click failed: {e}")
|
|
309
|
-
return False
|
|
310
|
-
|
|
311
|
-
def type_text(self, selector: str, text: str, timeout: float = 5.0) -> bool:
|
|
312
|
-
"""
|
|
313
|
-
Type text into an element.
|
|
314
|
-
|
|
315
|
-
Args:
|
|
316
|
-
selector: CSS selector
|
|
317
|
-
text: Text to type
|
|
318
|
-
timeout: Wait timeout in seconds
|
|
319
|
-
|
|
320
|
-
Returns:
|
|
321
|
-
True if successful, False otherwise
|
|
322
|
-
"""
|
|
323
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
324
|
-
event = InteractionEvent(event_type="type", selector=selector, text=text)
|
|
325
|
-
self.interaction_history.append(event)
|
|
326
|
-
return True
|
|
327
|
-
|
|
328
|
-
if not self.page:
|
|
329
|
-
logger.error("Browser not started")
|
|
330
|
-
return False
|
|
331
|
-
|
|
332
|
-
try:
|
|
333
|
-
self.page.fill(selector, text)
|
|
334
|
-
event = InteractionEvent(event_type="type", selector=selector, text=text)
|
|
335
|
-
self.interaction_history.append(event)
|
|
336
|
-
logger.debug(f"Typed into {selector}: {text}")
|
|
337
|
-
return True
|
|
338
|
-
except Exception as e:
|
|
339
|
-
logger.error(f"Type failed: {e}")
|
|
340
|
-
return False
|
|
341
|
-
|
|
342
|
-
def scroll(self, x: float = 0, y: float = 0) -> bool:
|
|
343
|
-
"""
|
|
344
|
-
Scroll the page.
|
|
345
|
-
|
|
346
|
-
Args:
|
|
347
|
-
x: Horizontal scroll amount
|
|
348
|
-
y: Vertical scroll amount
|
|
349
|
-
|
|
350
|
-
Returns:
|
|
351
|
-
True if successful, False otherwise
|
|
352
|
-
"""
|
|
353
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
354
|
-
event = InteractionEvent(event_type="scroll", coordinates=(x, y))
|
|
355
|
-
self.interaction_history.append(event)
|
|
356
|
-
return True
|
|
357
|
-
|
|
358
|
-
if not self.page:
|
|
359
|
-
logger.error("Browser not started")
|
|
360
|
-
return False
|
|
361
|
-
|
|
362
|
-
try:
|
|
363
|
-
self.page.evaluate(f"window.scrollBy({x}, {y})")
|
|
364
|
-
event = InteractionEvent(event_type="scroll", coordinates=(x, y))
|
|
365
|
-
self.interaction_history.append(event)
|
|
366
|
-
logger.debug(f"Scrolled: ({x}, {y})")
|
|
367
|
-
return True
|
|
368
|
-
except Exception as e:
|
|
369
|
-
logger.error(f"Scroll failed: {e}")
|
|
370
|
-
return False
|
|
371
|
-
|
|
372
|
-
def hover(self, selector: str, timeout: float = 5.0) -> bool:
|
|
373
|
-
"""
|
|
374
|
-
Hover over an element.
|
|
375
|
-
|
|
376
|
-
Args:
|
|
377
|
-
selector: CSS selector
|
|
378
|
-
timeout: Wait timeout in seconds
|
|
379
|
-
|
|
380
|
-
Returns:
|
|
381
|
-
True if successful, False otherwise
|
|
382
|
-
"""
|
|
383
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
384
|
-
event = InteractionEvent(event_type="hover", selector=selector)
|
|
385
|
-
self.interaction_history.append(event)
|
|
386
|
-
return True
|
|
387
|
-
|
|
388
|
-
if not self.page:
|
|
389
|
-
logger.error("Browser not started")
|
|
390
|
-
return False
|
|
391
|
-
|
|
392
|
-
try:
|
|
393
|
-
self.page.hover(selector, timeout=timeout * 1000)
|
|
394
|
-
event = InteractionEvent(event_type="hover", selector=selector)
|
|
395
|
-
self.interaction_history.append(event)
|
|
396
|
-
logger.debug(f"Hovered: {selector}")
|
|
397
|
-
return True
|
|
398
|
-
except Exception as e:
|
|
399
|
-
logger.error(f"Hover failed: {e}")
|
|
400
|
-
return False
|
|
401
|
-
|
|
402
|
-
def press_key(self, key: str) -> bool:
|
|
403
|
-
"""
|
|
404
|
-
Press a key.
|
|
405
|
-
|
|
406
|
-
Args:
|
|
407
|
-
key: Key to press (e.g., "Enter", "Escape", "Tab")
|
|
408
|
-
|
|
409
|
-
Returns:
|
|
410
|
-
True if successful, False otherwise
|
|
411
|
-
"""
|
|
412
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
413
|
-
event = InteractionEvent(event_type="keypress", key=key)
|
|
414
|
-
self.interaction_history.append(event)
|
|
415
|
-
return True
|
|
416
|
-
|
|
417
|
-
if not self.page:
|
|
418
|
-
logger.error("Browser not started")
|
|
419
|
-
return False
|
|
420
|
-
|
|
421
|
-
try:
|
|
422
|
-
self.page.press("body", key)
|
|
423
|
-
event = InteractionEvent(event_type="keypress", key=key)
|
|
424
|
-
self.interaction_history.append(event)
|
|
425
|
-
logger.debug(f"Pressed key: {key}")
|
|
426
|
-
return True
|
|
427
|
-
except Exception as e:
|
|
428
|
-
logger.error(f"Key press failed: {e}")
|
|
429
|
-
return False
|
|
430
|
-
|
|
431
|
-
def get_html(self) -> str | None:
|
|
432
|
-
"""
|
|
433
|
-
Get current page HTML.
|
|
434
|
-
|
|
435
|
-
Returns:
|
|
436
|
-
HTML content or None if failed
|
|
437
|
-
"""
|
|
438
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
439
|
-
return "<html><body>Cloud rendering</body></html>" # Mock
|
|
440
|
-
|
|
441
|
-
if not self.page:
|
|
442
|
-
logger.error("Browser not started")
|
|
443
|
-
return None
|
|
444
|
-
|
|
445
|
-
try:
|
|
446
|
-
return self.page.content()
|
|
447
|
-
except Exception as e:
|
|
448
|
-
logger.error(f"Get HTML failed: {e}")
|
|
449
|
-
return None
|
|
450
|
-
|
|
451
|
-
def get_interaction_history(self) -> list[InteractionEvent]:
|
|
452
|
-
"""
|
|
453
|
-
Get interaction history.
|
|
454
|
-
|
|
455
|
-
Returns:
|
|
456
|
-
List of interaction events
|
|
457
|
-
"""
|
|
458
|
-
return self.interaction_history.copy()
|
|
459
|
-
|
|
460
|
-
def clear_interaction_history(self):
|
|
461
|
-
"""Clear interaction history."""
|
|
462
|
-
self.interaction_history.clear()
|
|
463
|
-
|
|
464
|
-
def wait_for_selector(
|
|
465
|
-
self, selector: str, timeout: float = 5.0, state: str = "visible"
|
|
466
|
-
) -> bool:
|
|
467
|
-
"""
|
|
468
|
-
Wait for selector to appear.
|
|
469
|
-
|
|
470
|
-
Args:
|
|
471
|
-
selector: CSS selector
|
|
472
|
-
timeout: Wait timeout in seconds
|
|
473
|
-
state: Wait state ("visible", "hidden", "attached", "detached")
|
|
474
|
-
|
|
475
|
-
Returns:
|
|
476
|
-
True if element found, False otherwise
|
|
477
|
-
"""
|
|
478
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
479
|
-
return True # Mock
|
|
480
|
-
|
|
481
|
-
if not self.page:
|
|
482
|
-
logger.error("Browser not started")
|
|
483
|
-
return False
|
|
484
|
-
|
|
485
|
-
try:
|
|
486
|
-
self.page.wait_for_selector(selector, timeout=timeout * 1000, state=state)
|
|
487
|
-
return True
|
|
488
|
-
except Exception as e:
|
|
489
|
-
logger.error(f"Wait for selector failed: {e}")
|
|
490
|
-
return False
|
|
491
|
-
|
|
492
|
-
def evaluate_script(self, script: str) -> Any | None:
|
|
493
|
-
"""
|
|
494
|
-
Evaluate JavaScript in the page context.
|
|
495
|
-
|
|
496
|
-
Args:
|
|
497
|
-
script: JavaScript code to evaluate
|
|
498
|
-
|
|
499
|
-
Returns:
|
|
500
|
-
Evaluation result or None if failed
|
|
501
|
-
"""
|
|
502
|
-
if self.rendering_mode == RenderingMode.CLOUD:
|
|
503
|
-
return None # Mock
|
|
504
|
-
|
|
505
|
-
if not self.page:
|
|
506
|
-
logger.error("Browser not started")
|
|
507
|
-
return None
|
|
508
|
-
|
|
509
|
-
try:
|
|
510
|
-
return self.page.evaluate(script)
|
|
511
|
-
except Exception as e:
|
|
512
|
-
logger.error(f"Script evaluation failed: {e}")
|
|
513
|
-
return None
|
|
1
|
+
"""
|
|
2
|
+
Browser Controller for UI Rendering and Interaction
|
|
3
|
+
|
|
4
|
+
Provides headless browser control for visual feedback collection.
|
|
5
|
+
Supports Playwright with optional Selenium fallback.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from .hardware_profiler import HardwareProfile, HardwareProfiler
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# Try to import Playwright, fallback to mock if not available
|
|
22
|
+
try:
|
|
23
|
+
from playwright.sync_api import Browser, BrowserContext, Page, sync_playwright
|
|
24
|
+
|
|
25
|
+
HAS_PLAYWRIGHT = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
HAS_PLAYWRIGHT = False
|
|
28
|
+
# Keep these as `Any` so runtime imports don't break annotations when Playwright
|
|
29
|
+
# isn't installed (and so mypy doesn't infer `NoneType` here).
|
|
30
|
+
Browser = Any
|
|
31
|
+
Page = Any
|
|
32
|
+
BrowserContext = Any
|
|
33
|
+
# Do not warn at import time. Many environments (especially Cursor-first setups)
|
|
34
|
+
# may rely on Browser automation via Cursor MCP rather than the Python Playwright
|
|
35
|
+
# package. We'll log only when/if browser operations are actually requested.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class BrowserType(Enum):
|
|
39
|
+
"""Browser types."""
|
|
40
|
+
|
|
41
|
+
CHROMIUM = "chromium"
|
|
42
|
+
FIREFOX = "firefox"
|
|
43
|
+
WEBKIT = "webkit"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RenderingMode(Enum):
|
|
47
|
+
"""Rendering mode for browser operations."""
|
|
48
|
+
|
|
49
|
+
LOCAL = "local" # Local browser rendering
|
|
50
|
+
CLOUD = "cloud" # Cloud rendering service (for NUC devices)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class ScreenshotOptions:
|
|
55
|
+
"""Options for screenshot capture."""
|
|
56
|
+
|
|
57
|
+
full_page: bool = False
|
|
58
|
+
clip: dict[str, float] | None = None # {x, y, width, height}
|
|
59
|
+
quality: int = 90 # 0-100
|
|
60
|
+
timeout: float = 30.0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class InteractionEvent:
|
|
65
|
+
"""User interaction event."""
|
|
66
|
+
|
|
67
|
+
event_type: str # "click", "type", "scroll", "hover", "keypress"
|
|
68
|
+
selector: str | None = None
|
|
69
|
+
text: str | None = None
|
|
70
|
+
key: str | None = None
|
|
71
|
+
coordinates: tuple[float, float] | None = None
|
|
72
|
+
timestamp: float = 0.0
|
|
73
|
+
metadata: dict[str, Any] | None = None
|
|
74
|
+
|
|
75
|
+
def __post_init__(self):
|
|
76
|
+
if self.metadata is None:
|
|
77
|
+
self.metadata = {}
|
|
78
|
+
if self.timestamp == 0.0:
|
|
79
|
+
self.timestamp = time.time()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class BrowserController:
|
|
83
|
+
"""Controls headless browser for UI rendering."""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
hardware_profile: HardwareProfile | None = None,
|
|
88
|
+
browser_type: BrowserType = BrowserType.CHROMIUM,
|
|
89
|
+
headless: bool = True,
|
|
90
|
+
):
|
|
91
|
+
"""
|
|
92
|
+
Initialize browser controller.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
hardware_profile: Hardware profile for optimization
|
|
96
|
+
browser_type: Browser type to use
|
|
97
|
+
headless: Run browser in headless mode
|
|
98
|
+
"""
|
|
99
|
+
self.hardware_profile = hardware_profile or HardwareProfiler().detect_profile()
|
|
100
|
+
self.browser_type = browser_type
|
|
101
|
+
self.headless = headless
|
|
102
|
+
self.rendering_mode = self._get_rendering_mode()
|
|
103
|
+
|
|
104
|
+
self.playwright: Any | None = None
|
|
105
|
+
self.browser: Browser | None = None
|
|
106
|
+
self.context: BrowserContext | None = None
|
|
107
|
+
self.page: Page | None = None
|
|
108
|
+
|
|
109
|
+
self.interaction_history: list[InteractionEvent] = []
|
|
110
|
+
|
|
111
|
+
def _get_rendering_mode(self) -> RenderingMode:
|
|
112
|
+
"""Rendering mode. Workstation-like (hardware taxonomy removed)."""
|
|
113
|
+
return RenderingMode.LOCAL
|
|
114
|
+
|
|
115
|
+
def start(self) -> bool:
|
|
116
|
+
"""
|
|
117
|
+
Start browser instance.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True if successful, False otherwise
|
|
121
|
+
"""
|
|
122
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
123
|
+
logger.info("Using cloud rendering fallback (NUC device)")
|
|
124
|
+
return True # Cloud rendering doesn't need local browser
|
|
125
|
+
|
|
126
|
+
if not HAS_PLAYWRIGHT:
|
|
127
|
+
logger.info(
|
|
128
|
+
"Playwright (Python package) not installed; local browser operations are disabled. "
|
|
129
|
+
"If you're running inside Cursor and have Playwright configured as an MCP tool, "
|
|
130
|
+
"use Cursor Skills/Background Agents for browser automation. "
|
|
131
|
+
"For CLI/browser_controller usage, install Playwright: `pip install playwright` "
|
|
132
|
+
"and run `python -m playwright install`."
|
|
133
|
+
)
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
self.playwright = sync_playwright().start()
|
|
138
|
+
if self.playwright is None:
|
|
139
|
+
logger.error("Failed to initialize Playwright")
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
browser_map = {
|
|
143
|
+
BrowserType.CHROMIUM: self.playwright.chromium,
|
|
144
|
+
BrowserType.FIREFOX: self.playwright.firefox,
|
|
145
|
+
BrowserType.WEBKIT: self.playwright.webkit,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
browser_launcher = browser_map.get(
|
|
149
|
+
self.browser_type, self.playwright.chromium
|
|
150
|
+
)
|
|
151
|
+
self.browser = browser_launcher.launch(headless=self.headless)
|
|
152
|
+
self.context = self.browser.new_context()
|
|
153
|
+
self.page = self.context.new_page()
|
|
154
|
+
|
|
155
|
+
logger.info(f"Browser started: {self.browser_type.value}")
|
|
156
|
+
return True
|
|
157
|
+
except Exception as e:
|
|
158
|
+
logger.error(f"Failed to start browser: {e}")
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
def stop(self):
|
|
162
|
+
"""Stop browser instance."""
|
|
163
|
+
if self.page:
|
|
164
|
+
self.page.close()
|
|
165
|
+
self.page = None
|
|
166
|
+
if self.context:
|
|
167
|
+
self.context.close()
|
|
168
|
+
self.context = None
|
|
169
|
+
if self.browser:
|
|
170
|
+
self.browser.close()
|
|
171
|
+
self.browser = None
|
|
172
|
+
if self.playwright:
|
|
173
|
+
self.playwright.stop()
|
|
174
|
+
self.playwright = None
|
|
175
|
+
|
|
176
|
+
logger.info("Browser stopped")
|
|
177
|
+
|
|
178
|
+
def navigate(
|
|
179
|
+
self, url: str, wait_until: str = "load", timeout: float = 30.0
|
|
180
|
+
) -> bool:
|
|
181
|
+
"""
|
|
182
|
+
Navigate to URL.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
url: URL to navigate to
|
|
186
|
+
wait_until: Wait condition ("load", "domcontentloaded", "networkidle")
|
|
187
|
+
timeout: Navigation timeout in seconds
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
True if successful, False otherwise
|
|
191
|
+
"""
|
|
192
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
193
|
+
logger.info(f"Cloud navigation to: {url}")
|
|
194
|
+
return True # Mock for cloud rendering
|
|
195
|
+
|
|
196
|
+
if not self.page:
|
|
197
|
+
logger.error("Browser not started")
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
self.page.goto(url, wait_until=wait_until, timeout=timeout * 1000)
|
|
202
|
+
logger.info(f"Navigated to: {url}")
|
|
203
|
+
return True
|
|
204
|
+
except Exception as e:
|
|
205
|
+
logger.error(f"Navigation failed: {e}")
|
|
206
|
+
return False
|
|
207
|
+
|
|
208
|
+
def load_html(self, html_content: str, base_url: str = "http://localhost") -> bool:
|
|
209
|
+
"""
|
|
210
|
+
Load HTML content directly.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
html_content: HTML content to load
|
|
214
|
+
base_url: Base URL for relative resources
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
True if successful, False otherwise
|
|
218
|
+
"""
|
|
219
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
220
|
+
logger.info("Cloud HTML load")
|
|
221
|
+
return True # Mock for cloud rendering
|
|
222
|
+
|
|
223
|
+
if not self.page:
|
|
224
|
+
logger.error("Browser not started")
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
self.page.set_content(html_content, wait_until="domcontentloaded")
|
|
229
|
+
logger.info("HTML content loaded")
|
|
230
|
+
return True
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.error(f"HTML load failed: {e}")
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
def capture_screenshot(
|
|
236
|
+
self, output_path: str, options: ScreenshotOptions | None = None
|
|
237
|
+
) -> bool:
|
|
238
|
+
"""
|
|
239
|
+
Capture screenshot of current page.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
output_path: Path to save screenshot
|
|
243
|
+
options: Screenshot options
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
True if successful, False otherwise
|
|
247
|
+
"""
|
|
248
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
249
|
+
logger.info(f"Cloud screenshot to: {output_path}")
|
|
250
|
+
# In real implementation, would call cloud rendering service
|
|
251
|
+
Path(output_path).touch() # Create empty file as placeholder
|
|
252
|
+
return True
|
|
253
|
+
|
|
254
|
+
if not self.page:
|
|
255
|
+
logger.error("Browser not started")
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
if options is None:
|
|
259
|
+
options = ScreenshotOptions()
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
screenshot_kwargs = {
|
|
263
|
+
"path": output_path,
|
|
264
|
+
"full_page": options.full_page,
|
|
265
|
+
"timeout": options.timeout * 1000,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if options.clip:
|
|
269
|
+
screenshot_kwargs["clip"] = options.clip
|
|
270
|
+
|
|
271
|
+
if options.quality is not None:
|
|
272
|
+
screenshot_kwargs["quality"] = options.quality
|
|
273
|
+
|
|
274
|
+
self.page.screenshot(**screenshot_kwargs)
|
|
275
|
+
logger.info(f"Screenshot saved: {output_path}")
|
|
276
|
+
return True
|
|
277
|
+
except Exception as e:
|
|
278
|
+
logger.error(f"Screenshot capture failed: {e}")
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
def click(self, selector: str, timeout: float = 5.0) -> bool:
|
|
282
|
+
"""
|
|
283
|
+
Click an element.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
selector: CSS selector
|
|
287
|
+
timeout: Wait timeout in seconds
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
True if successful, False otherwise
|
|
291
|
+
"""
|
|
292
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
293
|
+
event = InteractionEvent(event_type="click", selector=selector)
|
|
294
|
+
self.interaction_history.append(event)
|
|
295
|
+
return True
|
|
296
|
+
|
|
297
|
+
if not self.page:
|
|
298
|
+
logger.error("Browser not started")
|
|
299
|
+
return False
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
self.page.click(selector, timeout=timeout * 1000)
|
|
303
|
+
event = InteractionEvent(event_type="click", selector=selector)
|
|
304
|
+
self.interaction_history.append(event)
|
|
305
|
+
logger.debug(f"Clicked: {selector}")
|
|
306
|
+
return True
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.error(f"Click failed: {e}")
|
|
309
|
+
return False
|
|
310
|
+
|
|
311
|
+
def type_text(self, selector: str, text: str, timeout: float = 5.0) -> bool:
|
|
312
|
+
"""
|
|
313
|
+
Type text into an element.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
selector: CSS selector
|
|
317
|
+
text: Text to type
|
|
318
|
+
timeout: Wait timeout in seconds
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
True if successful, False otherwise
|
|
322
|
+
"""
|
|
323
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
324
|
+
event = InteractionEvent(event_type="type", selector=selector, text=text)
|
|
325
|
+
self.interaction_history.append(event)
|
|
326
|
+
return True
|
|
327
|
+
|
|
328
|
+
if not self.page:
|
|
329
|
+
logger.error("Browser not started")
|
|
330
|
+
return False
|
|
331
|
+
|
|
332
|
+
try:
|
|
333
|
+
self.page.fill(selector, text)
|
|
334
|
+
event = InteractionEvent(event_type="type", selector=selector, text=text)
|
|
335
|
+
self.interaction_history.append(event)
|
|
336
|
+
logger.debug(f"Typed into {selector}: {text}")
|
|
337
|
+
return True
|
|
338
|
+
except Exception as e:
|
|
339
|
+
logger.error(f"Type failed: {e}")
|
|
340
|
+
return False
|
|
341
|
+
|
|
342
|
+
def scroll(self, x: float = 0, y: float = 0) -> bool:
|
|
343
|
+
"""
|
|
344
|
+
Scroll the page.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
x: Horizontal scroll amount
|
|
348
|
+
y: Vertical scroll amount
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
True if successful, False otherwise
|
|
352
|
+
"""
|
|
353
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
354
|
+
event = InteractionEvent(event_type="scroll", coordinates=(x, y))
|
|
355
|
+
self.interaction_history.append(event)
|
|
356
|
+
return True
|
|
357
|
+
|
|
358
|
+
if not self.page:
|
|
359
|
+
logger.error("Browser not started")
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
self.page.evaluate(f"window.scrollBy({x}, {y})")
|
|
364
|
+
event = InteractionEvent(event_type="scroll", coordinates=(x, y))
|
|
365
|
+
self.interaction_history.append(event)
|
|
366
|
+
logger.debug(f"Scrolled: ({x}, {y})")
|
|
367
|
+
return True
|
|
368
|
+
except Exception as e:
|
|
369
|
+
logger.error(f"Scroll failed: {e}")
|
|
370
|
+
return False
|
|
371
|
+
|
|
372
|
+
def hover(self, selector: str, timeout: float = 5.0) -> bool:
|
|
373
|
+
"""
|
|
374
|
+
Hover over an element.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
selector: CSS selector
|
|
378
|
+
timeout: Wait timeout in seconds
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
True if successful, False otherwise
|
|
382
|
+
"""
|
|
383
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
384
|
+
event = InteractionEvent(event_type="hover", selector=selector)
|
|
385
|
+
self.interaction_history.append(event)
|
|
386
|
+
return True
|
|
387
|
+
|
|
388
|
+
if not self.page:
|
|
389
|
+
logger.error("Browser not started")
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
try:
|
|
393
|
+
self.page.hover(selector, timeout=timeout * 1000)
|
|
394
|
+
event = InteractionEvent(event_type="hover", selector=selector)
|
|
395
|
+
self.interaction_history.append(event)
|
|
396
|
+
logger.debug(f"Hovered: {selector}")
|
|
397
|
+
return True
|
|
398
|
+
except Exception as e:
|
|
399
|
+
logger.error(f"Hover failed: {e}")
|
|
400
|
+
return False
|
|
401
|
+
|
|
402
|
+
def press_key(self, key: str) -> bool:
|
|
403
|
+
"""
|
|
404
|
+
Press a key.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
key: Key to press (e.g., "Enter", "Escape", "Tab")
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
True if successful, False otherwise
|
|
411
|
+
"""
|
|
412
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
413
|
+
event = InteractionEvent(event_type="keypress", key=key)
|
|
414
|
+
self.interaction_history.append(event)
|
|
415
|
+
return True
|
|
416
|
+
|
|
417
|
+
if not self.page:
|
|
418
|
+
logger.error("Browser not started")
|
|
419
|
+
return False
|
|
420
|
+
|
|
421
|
+
try:
|
|
422
|
+
self.page.press("body", key)
|
|
423
|
+
event = InteractionEvent(event_type="keypress", key=key)
|
|
424
|
+
self.interaction_history.append(event)
|
|
425
|
+
logger.debug(f"Pressed key: {key}")
|
|
426
|
+
return True
|
|
427
|
+
except Exception as e:
|
|
428
|
+
logger.error(f"Key press failed: {e}")
|
|
429
|
+
return False
|
|
430
|
+
|
|
431
|
+
def get_html(self) -> str | None:
|
|
432
|
+
"""
|
|
433
|
+
Get current page HTML.
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
HTML content or None if failed
|
|
437
|
+
"""
|
|
438
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
439
|
+
return "<html><body>Cloud rendering</body></html>" # Mock
|
|
440
|
+
|
|
441
|
+
if not self.page:
|
|
442
|
+
logger.error("Browser not started")
|
|
443
|
+
return None
|
|
444
|
+
|
|
445
|
+
try:
|
|
446
|
+
return self.page.content()
|
|
447
|
+
except Exception as e:
|
|
448
|
+
logger.error(f"Get HTML failed: {e}")
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
def get_interaction_history(self) -> list[InteractionEvent]:
|
|
452
|
+
"""
|
|
453
|
+
Get interaction history.
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
List of interaction events
|
|
457
|
+
"""
|
|
458
|
+
return self.interaction_history.copy()
|
|
459
|
+
|
|
460
|
+
def clear_interaction_history(self):
|
|
461
|
+
"""Clear interaction history."""
|
|
462
|
+
self.interaction_history.clear()
|
|
463
|
+
|
|
464
|
+
def wait_for_selector(
|
|
465
|
+
self, selector: str, timeout: float = 5.0, state: str = "visible"
|
|
466
|
+
) -> bool:
|
|
467
|
+
"""
|
|
468
|
+
Wait for selector to appear.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
selector: CSS selector
|
|
472
|
+
timeout: Wait timeout in seconds
|
|
473
|
+
state: Wait state ("visible", "hidden", "attached", "detached")
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
True if element found, False otherwise
|
|
477
|
+
"""
|
|
478
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
479
|
+
return True # Mock
|
|
480
|
+
|
|
481
|
+
if not self.page:
|
|
482
|
+
logger.error("Browser not started")
|
|
483
|
+
return False
|
|
484
|
+
|
|
485
|
+
try:
|
|
486
|
+
self.page.wait_for_selector(selector, timeout=timeout * 1000, state=state)
|
|
487
|
+
return True
|
|
488
|
+
except Exception as e:
|
|
489
|
+
logger.error(f"Wait for selector failed: {e}")
|
|
490
|
+
return False
|
|
491
|
+
|
|
492
|
+
def evaluate_script(self, script: str) -> Any | None:
|
|
493
|
+
"""
|
|
494
|
+
Evaluate JavaScript in the page context.
|
|
495
|
+
|
|
496
|
+
Args:
|
|
497
|
+
script: JavaScript code to evaluate
|
|
498
|
+
|
|
499
|
+
Returns:
|
|
500
|
+
Evaluation result or None if failed
|
|
501
|
+
"""
|
|
502
|
+
if self.rendering_mode == RenderingMode.CLOUD:
|
|
503
|
+
return None # Mock
|
|
504
|
+
|
|
505
|
+
if not self.page:
|
|
506
|
+
logger.error("Browser not started")
|
|
507
|
+
return None
|
|
508
|
+
|
|
509
|
+
try:
|
|
510
|
+
return self.page.evaluate(script)
|
|
511
|
+
except Exception as e:
|
|
512
|
+
logger.error(f"Script evaluation failed: {e}")
|
|
513
|
+
return None
|