sycommon-python-lib 0.2.1b5__tar.gz → 0.2.1b6__tar.gz
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.
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/PKG-INFO +4 -4
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/pyproject.toml +4 -4
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/__init__.py +1 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/browser.py +214 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/capabilities/__init__.py +1 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/capabilities/console.py +85 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/capabilities/dom.py +53 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/capabilities/network.py +119 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/capabilities/page_errors.py +91 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/capabilities/performance.py +67 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/capabilities/screenshot.py +61 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/client.py +148 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/protocol.py +133 -0
- sycommon_python_lib-0.2.1b6/src/sycli/cdp/repl.py +267 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/cli.py +52 -0
- sycommon_python_lib-0.2.1b6/src/sycli/commands/cdp_cmd.py +271 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/llm_tokens.py +8 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon_python_lib.egg-info/PKG-INFO +4 -4
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon_python_lib.egg-info/SOURCES.txt +13 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon_python_lib.egg-info/requires.txt +3 -3
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/README.md +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/setup.cfg +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/core/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/core/console.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/core/models.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/core/project.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/core/utils.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/templates/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/templates/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/templates/base/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/command/templates/web/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/__main__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/agents/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/agents/factory.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/agents/prompts.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/chat_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/create_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/evaluate_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/init_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/memory_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/resume_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/rollback_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/commands/run_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/core/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/core/backend.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/core/config.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/core/display.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/core/git_integration.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/core/llm.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/core/state.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/evaluate/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/evaluate/api_evaluator.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/memory/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/memory/compressor.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/memory/full.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/memory/hot.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/memory/manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/memory/warm.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/mode/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/mode/create.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/mode/optimize.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/models/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/models/config_models.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/convergence.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/diagnosis.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/engine.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/environment.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/experience.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/harness_prompts.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/history.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/pre_validation.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/reward.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/strategy_bandit.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/strategy_generator.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/rl/strategy_prompts.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycli/skills/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/agent_manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/chat_events.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/deep_agent.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/multi_agent_team.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/sandbox/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/sandbox/file_ops.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/sandbox/http_sandbox_backend.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/sandbox/sandbox_recovery.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/agent/sandbox/session.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/auth/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/auth/ldap_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/ElasticsearchConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/LangfuseConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/RedisConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/SentryConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/XxlJobConfig.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/database/async_base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/database/async_database_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/database/elasticsearch_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/database/redis_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/database/token_usage_db_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/heartbeat_process/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/embedding.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/get_llm.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/llm_logger.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/output_fixing_runnable.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/struct_token.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/sy_langfuse.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/token_usage_es_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/token_usage_mysql_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/llm/usage_token.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/logging/async_sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/logging/logger_levels.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/logging/process_logger.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/background_execution.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/sandbox.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/token_tracking.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/middleware/traceid.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/sandbox.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/token_usage.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/token_usage_mysql.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/models/xxljob_handler_config.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/notice/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/notice/uvicorn_monitor.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/sentry/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/sentry/sy_sentry.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/services.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/nacos_client_base.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/nacos_config_manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/nacos_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/nacos_service_registration.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tests/deep_agent_server.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tests/test_deep_agent.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tests/test_email.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tests/test_mq.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/async_utils.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/env.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/merge_headers.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/syemail.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/xxljob/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon/xxljob/xxljob_service.py +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
- {sycommon_python_lib-0.2.1b5 → sycommon_python_lib-0.2.1b6}/tests/test_sycli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1b6
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -9,7 +9,7 @@ Requires-Dist: aiohttp>=3.13.5
|
|
|
9
9
|
Requires-Dist: aiomysql>=0.3.2
|
|
10
10
|
Requires-Dist: anyio>=4.12.1
|
|
11
11
|
Requires-Dist: decorator>=5.2.1
|
|
12
|
-
Requires-Dist: deepagents>=0.5.
|
|
12
|
+
Requires-Dist: deepagents>=0.5.2
|
|
13
13
|
Requires-Dist: elasticsearch>=9.3.0
|
|
14
14
|
Requires-Dist: fastapi>=0.135.3
|
|
15
15
|
Requires-Dist: jinja2>=3.1.6
|
|
@@ -17,7 +17,7 @@ Requires-Dist: kafka-python>=2.3.1
|
|
|
17
17
|
Requires-Dist: langchain>=1.2.15
|
|
18
18
|
Requires-Dist: langchain-core>=1.2.28
|
|
19
19
|
Requires-Dist: langchain-openai>=1.1.11
|
|
20
|
-
Requires-Dist: langfuse>=4.
|
|
20
|
+
Requires-Dist: langfuse>=4.2.0
|
|
21
21
|
Requires-Dist: langgraph>=1.1.6
|
|
22
22
|
Requires-Dist: langgraph-checkpoint-redis>=0.4.0
|
|
23
23
|
Requires-Dist: ldap3>=2.9.1
|
|
@@ -28,7 +28,7 @@ Requires-Dist: psutil>=7.2.2
|
|
|
28
28
|
Requires-Dist: pyxxl>=0.4.6
|
|
29
29
|
Requires-Dist: pydantic>=2.12.5
|
|
30
30
|
Requires-Dist: python-dotenv>=1.2.2
|
|
31
|
-
Requires-Dist: python-multipart>=0.0.
|
|
31
|
+
Requires-Dist: python-multipart>=0.0.26
|
|
32
32
|
Requires-Dist: pyyaml>=6.0.3
|
|
33
33
|
Requires-Dist: redis>=7.3.0
|
|
34
34
|
Requires-Dist: sentry-sdk[fastapi]>=2.57.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sycommon-python-lib"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1b6"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
"aiomysql>=0.3.2",
|
|
11
11
|
"anyio>=4.12.1",
|
|
12
12
|
"decorator>=5.2.1",
|
|
13
|
-
"deepagents>=0.5.
|
|
13
|
+
"deepagents>=0.5.2",
|
|
14
14
|
"elasticsearch>=9.3.0",
|
|
15
15
|
"fastapi>=0.135.3",
|
|
16
16
|
"jinja2>=3.1.6",
|
|
@@ -18,7 +18,7 @@ dependencies = [
|
|
|
18
18
|
"langchain>=1.2.15",
|
|
19
19
|
"langchain-core>=1.2.28",
|
|
20
20
|
"langchain-openai>=1.1.11",
|
|
21
|
-
"langfuse>=4.
|
|
21
|
+
"langfuse>=4.2.0",
|
|
22
22
|
"langgraph>=1.1.6",
|
|
23
23
|
"langgraph-checkpoint-redis>=0.4.0",
|
|
24
24
|
"ldap3>=2.9.1",
|
|
@@ -29,7 +29,7 @@ dependencies = [
|
|
|
29
29
|
"pyxxl>=0.4.6",
|
|
30
30
|
"pydantic>=2.12.5",
|
|
31
31
|
"python-dotenv>=1.2.2",
|
|
32
|
-
"python-multipart>=0.0.
|
|
32
|
+
"python-multipart>=0.0.26",
|
|
33
33
|
"pyyaml>=6.0.3",
|
|
34
34
|
"redis>=7.3.0",
|
|
35
35
|
"sentry-sdk[fastapi]>=2.57.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""sycli CDP — Chrome DevTools Protocol debugging support."""
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Browser launcher — find and start Chrome/Chromium with remote debugging.
|
|
2
|
+
|
|
3
|
+
Supports macOS, Linux, and Windows. Uses a temporary user data directory
|
|
4
|
+
and cleans up the process tree on termination.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import platform
|
|
13
|
+
import shutil
|
|
14
|
+
import subprocess
|
|
15
|
+
import tempfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import aiohttp
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
# Platform-specific Chrome search paths
|
|
23
|
+
_CHROME_PATHS: dict[str, list[str]] = {
|
|
24
|
+
"Darwin": [
|
|
25
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
26
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
27
|
+
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
|
|
28
|
+
],
|
|
29
|
+
"Linux": [
|
|
30
|
+
"/usr/bin/google-chrome",
|
|
31
|
+
"/usr/bin/google-chrome-stable",
|
|
32
|
+
"/usr/bin/chromium-browser",
|
|
33
|
+
"/usr/bin/chromium",
|
|
34
|
+
"/snap/bin/chromium",
|
|
35
|
+
],
|
|
36
|
+
"Windows": [
|
|
37
|
+
os.path.expandvars(r"%ProgramFiles%\Google\Chrome\Application\chrome.exe"),
|
|
38
|
+
os.path.expandvars(r"%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe"),
|
|
39
|
+
os.path.expandvars(r"%LocalAppData%\Google\Chrome\Application\chrome.exe"),
|
|
40
|
+
],
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BrowserProcess:
|
|
45
|
+
"""Manages a launched Chrome subprocess."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, process: subprocess.Popen, port: int, user_data_dir: str) -> None:
|
|
48
|
+
self.process = process
|
|
49
|
+
self.port = port
|
|
50
|
+
self.user_data_dir = user_data_dir
|
|
51
|
+
|
|
52
|
+
async def wait_for_ready(self, timeout: float = 10.0) -> None:
|
|
53
|
+
"""Poll the /json/version endpoint until Chrome is ready."""
|
|
54
|
+
import time
|
|
55
|
+
deadline = time.monotonic() + timeout
|
|
56
|
+
while time.monotonic() < deadline:
|
|
57
|
+
try:
|
|
58
|
+
async with aiohttp.ClientSession() as session:
|
|
59
|
+
async with session.get(
|
|
60
|
+
f"http://localhost:{self.port}/json/version",
|
|
61
|
+
timeout=aiohttp.ClientTimeout(total=2),
|
|
62
|
+
) as resp:
|
|
63
|
+
if resp.status == 200:
|
|
64
|
+
return
|
|
65
|
+
except Exception:
|
|
66
|
+
pass
|
|
67
|
+
await asyncio.sleep(0.2)
|
|
68
|
+
raise TimeoutError(f"Chrome did not become ready on port {self.port} within {timeout}s")
|
|
69
|
+
|
|
70
|
+
async def terminate(self) -> None:
|
|
71
|
+
"""Terminate the Chrome process and clean up."""
|
|
72
|
+
if self.process.poll() is None:
|
|
73
|
+
try:
|
|
74
|
+
import psutil
|
|
75
|
+
parent = psutil.Process(self.process.pid)
|
|
76
|
+
children = parent.children(recursive=True)
|
|
77
|
+
for child in children:
|
|
78
|
+
try:
|
|
79
|
+
child.terminate()
|
|
80
|
+
except psutil.NoSuchProcess:
|
|
81
|
+
pass
|
|
82
|
+
self.process.terminate()
|
|
83
|
+
try:
|
|
84
|
+
self.process.wait(timeout=5)
|
|
85
|
+
except subprocess.TimeoutExpired:
|
|
86
|
+
self.process.kill()
|
|
87
|
+
except ImportError:
|
|
88
|
+
self.process.terminate()
|
|
89
|
+
try:
|
|
90
|
+
self.process.wait(timeout=5)
|
|
91
|
+
except subprocess.TimeoutExpired:
|
|
92
|
+
self.process.kill()
|
|
93
|
+
|
|
94
|
+
# Clean up temp directory
|
|
95
|
+
if self.user_data_dir and os.path.exists(self.user_data_dir):
|
|
96
|
+
try:
|
|
97
|
+
shutil.rmtree(self.user_data_dir, ignore_errors=True)
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class BrowserLauncher:
|
|
103
|
+
"""Find and launch Chrome/Chromium with remote debugging enabled."""
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def find_chrome() -> str | None:
|
|
107
|
+
"""Return the path to a Chrome/Chromium executable, or None."""
|
|
108
|
+
system = platform.system()
|
|
109
|
+
paths = _CHROME_PATHS.get(system, [])
|
|
110
|
+
|
|
111
|
+
for path in paths:
|
|
112
|
+
if os.path.isfile(path):
|
|
113
|
+
return path
|
|
114
|
+
|
|
115
|
+
# Fallback to PATH lookup
|
|
116
|
+
for name in ("google-chrome", "google-chrome-stable", "chromium-browser", "chromium", "chrome"):
|
|
117
|
+
found = shutil.which(name)
|
|
118
|
+
if found:
|
|
119
|
+
return found
|
|
120
|
+
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
async def launch(
|
|
125
|
+
port: int = 0,
|
|
126
|
+
headless: bool = True,
|
|
127
|
+
extra_args: list[str] | None = None,
|
|
128
|
+
) -> BrowserProcess:
|
|
129
|
+
"""Launch Chrome and return a BrowserProcess handle.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
port: Remote debugging port. 0 = auto-select an available port.
|
|
133
|
+
headless: Run in headless mode.
|
|
134
|
+
extra_args: Additional Chrome command-line arguments.
|
|
135
|
+
"""
|
|
136
|
+
chrome_path = BrowserLauncher.find_chrome()
|
|
137
|
+
if not chrome_path:
|
|
138
|
+
raise FileNotFoundError(
|
|
139
|
+
"Chrome/Chromium not found. Install Chrome or use --port to connect "
|
|
140
|
+
"to an existing browser with --remote-debugging-port."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Auto-select port if 0
|
|
144
|
+
if port == 0:
|
|
145
|
+
import socket
|
|
146
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
147
|
+
s.bind(("", 0))
|
|
148
|
+
port = s.getsockname()[1]
|
|
149
|
+
|
|
150
|
+
user_data_dir = tempfile.mkdtemp(prefix="sycli-cdp-")
|
|
151
|
+
|
|
152
|
+
args = [
|
|
153
|
+
chrome_path,
|
|
154
|
+
f"--remote-debugging-port={port}",
|
|
155
|
+
f"--user-data-dir={user_data_dir}",
|
|
156
|
+
"--disable-gpu",
|
|
157
|
+
"--no-sandbox",
|
|
158
|
+
"--disable-dev-shm-usage",
|
|
159
|
+
"--disable-extensions",
|
|
160
|
+
"--disable-background-networking",
|
|
161
|
+
"--disable-sync",
|
|
162
|
+
"--no-first-run",
|
|
163
|
+
"--disable-default-apps",
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
if headless:
|
|
167
|
+
args.append("--headless=new")
|
|
168
|
+
|
|
169
|
+
if extra_args:
|
|
170
|
+
args.extend(extra_args)
|
|
171
|
+
|
|
172
|
+
logger.debug("Launching Chrome: %s", " ".join(args))
|
|
173
|
+
|
|
174
|
+
process = subprocess.Popen(
|
|
175
|
+
args,
|
|
176
|
+
stdout=subprocess.DEVNULL,
|
|
177
|
+
stderr=subprocess.PIPE,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
bp = BrowserProcess(process, port, user_data_dir)
|
|
181
|
+
await bp.wait_for_ready()
|
|
182
|
+
return bp
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async def discover_ws_url(port: int, target_id: str | None = None) -> str | None:
|
|
186
|
+
"""Query /json endpoint to find a page's WebSocket debugger URL."""
|
|
187
|
+
try:
|
|
188
|
+
async with aiohttp.ClientSession() as session:
|
|
189
|
+
async with session.get(
|
|
190
|
+
f"http://localhost:{port}/json",
|
|
191
|
+
timeout=aiohttp.ClientTimeout(total=5),
|
|
192
|
+
) as resp:
|
|
193
|
+
if resp.status != 200:
|
|
194
|
+
return None
|
|
195
|
+
targets = await resp.json()
|
|
196
|
+
if not isinstance(targets, list):
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
if target_id:
|
|
200
|
+
for t in targets:
|
|
201
|
+
if t.get("id") == target_id:
|
|
202
|
+
return t.get("webSocketDebuggerUrl")
|
|
203
|
+
|
|
204
|
+
# Default: first page target
|
|
205
|
+
for t in targets:
|
|
206
|
+
if t.get("type") == "page":
|
|
207
|
+
return t.get("webSocketDebuggerUrl")
|
|
208
|
+
|
|
209
|
+
# Fallback: any target
|
|
210
|
+
if targets:
|
|
211
|
+
return targets[0].get("webSocketDebuggerUrl")
|
|
212
|
+
except Exception:
|
|
213
|
+
return None
|
|
214
|
+
return None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CDP capability modules for browser debugging."""
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Console capability — capture Runtime console events and exceptions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from sycli.cdp.client import CDPClient
|
|
10
|
+
from sycli.cdp.protocol import enable_runtime, evaluate_js
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def capture_console(
|
|
14
|
+
client: CDPClient,
|
|
15
|
+
duration: float = 5.0,
|
|
16
|
+
evaluate: str | None = None,
|
|
17
|
+
) -> list[dict[str, Any]]:
|
|
18
|
+
"""Capture console logs for a duration.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
client: Connected CDP client.
|
|
22
|
+
duration: How long to capture (seconds).
|
|
23
|
+
evaluate: Optional JS expression to evaluate after enabling console capture.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
List of console entries: {"type", "args", "timestamp", "text"}.
|
|
27
|
+
"""
|
|
28
|
+
entries: list[dict[str, Any]] = []
|
|
29
|
+
|
|
30
|
+
def on_console_api(params: dict) -> None:
|
|
31
|
+
args = params.get("args", [])
|
|
32
|
+
text = " ".join(_arg_to_str(a) for a in args)
|
|
33
|
+
entries.append({
|
|
34
|
+
"type": params.get("type", "log"),
|
|
35
|
+
"args": [_arg_summary(a) for a in args],
|
|
36
|
+
"text": text,
|
|
37
|
+
"timestamp": params.get("timestamp", 0),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
def on_exception(params: dict) -> None:
|
|
41
|
+
detail = params.get("exceptionDetails", {})
|
|
42
|
+
exc = detail.get("exception", {})
|
|
43
|
+
entries.append({
|
|
44
|
+
"type": "exception",
|
|
45
|
+
"text": exc.get("description", detail.get("text", "Unknown exception")),
|
|
46
|
+
"args": [],
|
|
47
|
+
"timestamp": params.get("timestamp", 0),
|
|
48
|
+
"url": detail.get("url", ""),
|
|
49
|
+
"line": detail.get("lineNumber", -1),
|
|
50
|
+
"column": detail.get("columnNumber", -1),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
client.on_event("Runtime.consoleAPICalled", on_console_api)
|
|
54
|
+
client.on_event("Runtime.exceptionThrown", on_exception)
|
|
55
|
+
await enable_runtime(client)
|
|
56
|
+
|
|
57
|
+
if evaluate:
|
|
58
|
+
await evaluate_js(client, evaluate)
|
|
59
|
+
|
|
60
|
+
await asyncio.sleep(duration)
|
|
61
|
+
|
|
62
|
+
client.remove_event_handler("Runtime.consoleAPICalled", on_console_api)
|
|
63
|
+
client.remove_event_handler("Runtime.exceptionThrown", on_exception)
|
|
64
|
+
|
|
65
|
+
return entries
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _arg_to_str(arg: dict) -> str:
|
|
69
|
+
"""Convert a CDP RemoteObject to a readable string."""
|
|
70
|
+
if "value" in arg:
|
|
71
|
+
import json
|
|
72
|
+
v = arg["value"]
|
|
73
|
+
return json.dumps(v, ensure_ascii=False) if isinstance(v, (dict, list)) else str(v)
|
|
74
|
+
if "description" in arg:
|
|
75
|
+
return arg["description"]
|
|
76
|
+
return arg.get("type", "undefined")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _arg_summary(arg: dict) -> dict:
|
|
80
|
+
"""Summarize a CDP RemoteObject for structured output."""
|
|
81
|
+
return {
|
|
82
|
+
"type": arg.get("type"),
|
|
83
|
+
"value": arg.get("value"),
|
|
84
|
+
"description": arg.get("description"),
|
|
85
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""DOM capability — query and inspect DOM elements."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from sycli.cdp.client import CDPClient
|
|
8
|
+
from sycli.cdp.protocol import (
|
|
9
|
+
enable_dom,
|
|
10
|
+
enable_page,
|
|
11
|
+
get_document,
|
|
12
|
+
get_outer_html,
|
|
13
|
+
query_selector,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def query_dom(
|
|
18
|
+
client: CDPClient,
|
|
19
|
+
selector: str,
|
|
20
|
+
) -> dict[str, Any]:
|
|
21
|
+
"""Query a DOM element by CSS selector.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
client: Connected CDP client.
|
|
25
|
+
selector: CSS selector string.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Dict with "selector", "node_id", "outer_html".
|
|
29
|
+
"""
|
|
30
|
+
await enable_page(client)
|
|
31
|
+
await enable_dom(client)
|
|
32
|
+
|
|
33
|
+
doc = await get_document(client)
|
|
34
|
+
root_node_id = doc.get("root", {}).get("nodeId", 0)
|
|
35
|
+
|
|
36
|
+
result = await query_selector(client, root_node_id, selector)
|
|
37
|
+
node_id = result.get("nodeId", 0)
|
|
38
|
+
|
|
39
|
+
if node_id == 0:
|
|
40
|
+
return {
|
|
41
|
+
"selector": selector,
|
|
42
|
+
"node_id": 0,
|
|
43
|
+
"outer_html": None,
|
|
44
|
+
"found": False,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
html_result = await get_outer_html(client, node_id)
|
|
48
|
+
return {
|
|
49
|
+
"selector": selector,
|
|
50
|
+
"node_id": node_id,
|
|
51
|
+
"outer_html": html_result.get("outerHTML", ""),
|
|
52
|
+
"found": True,
|
|
53
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Network capability — capture HTTP request/response traffic."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from sycli.cdp.client import CDPClient
|
|
9
|
+
from sycli.cdp.protocol import (
|
|
10
|
+
disable_network,
|
|
11
|
+
enable_network,
|
|
12
|
+
get_response_body,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def capture_network(
|
|
17
|
+
client: CDPClient,
|
|
18
|
+
duration: float = 10.0,
|
|
19
|
+
url_filter: str | None = None,
|
|
20
|
+
include_body: bool = False,
|
|
21
|
+
) -> list[dict[str, Any]]:
|
|
22
|
+
"""Capture network requests for a duration.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
client: Connected CDP client.
|
|
26
|
+
duration: How long to capture (seconds).
|
|
27
|
+
url_filter: Optional URL substring filter.
|
|
28
|
+
include_body: Fetch response bodies for text responses.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
List of network entries with request/response info.
|
|
32
|
+
"""
|
|
33
|
+
entries: dict[str, dict[str, Any]] = {}
|
|
34
|
+
|
|
35
|
+
def on_request(params: dict) -> None:
|
|
36
|
+
req = params.get("request", {})
|
|
37
|
+
rid = params.get("requestId", "")
|
|
38
|
+
entries[rid] = {
|
|
39
|
+
"request_id": rid,
|
|
40
|
+
"method": req.get("method", ""),
|
|
41
|
+
"url": req.get("url", ""),
|
|
42
|
+
"headers": req.get("headers", {}),
|
|
43
|
+
"request_type": params.get("type", ""),
|
|
44
|
+
"timestamp": params.get("timestamp", 0),
|
|
45
|
+
"status": None,
|
|
46
|
+
"mime_type": None,
|
|
47
|
+
"size": None,
|
|
48
|
+
"timing": None,
|
|
49
|
+
"body": None,
|
|
50
|
+
"failed": False,
|
|
51
|
+
"error_text": None,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
def on_response(params: dict) -> None:
|
|
55
|
+
rid = params.get("requestId", "")
|
|
56
|
+
resp = params.get("response", {})
|
|
57
|
+
if rid in entries:
|
|
58
|
+
entries[rid]["status"] = resp.get("status")
|
|
59
|
+
entries[rid]["mime_type"] = resp.get("mimeType", "")
|
|
60
|
+
entries[rid]["size"] = resp.get("encodedDataLength") or resp.get("content", {}).get("size")
|
|
61
|
+
entries[rid]["timing"] = resp.get("timing")
|
|
62
|
+
|
|
63
|
+
def on_loading_finished(params: dict) -> None:
|
|
64
|
+
rid = params.get("requestId", "")
|
|
65
|
+
if rid in entries:
|
|
66
|
+
entries[rid]["size"] = params.get("encodedDataLength", entries[rid]["size"])
|
|
67
|
+
|
|
68
|
+
def on_loading_failed(params: dict) -> None:
|
|
69
|
+
rid = params.get("requestId", "")
|
|
70
|
+
if rid in entries:
|
|
71
|
+
entries[rid]["failed"] = True
|
|
72
|
+
entries[rid]["error_text"] = params.get("errorText", "")
|
|
73
|
+
entries[rid]["canceled"] = params.get("canceled", False)
|
|
74
|
+
|
|
75
|
+
# Enable network BEFORE any page activity
|
|
76
|
+
client.on_event("Network.requestWillBeSent", on_request)
|
|
77
|
+
client.on_event("Network.responseReceived", on_response)
|
|
78
|
+
client.on_event("Network.loadingFinished", on_loading_finished)
|
|
79
|
+
client.on_event("Network.loadingFailed", on_loading_failed)
|
|
80
|
+
await enable_network(client)
|
|
81
|
+
|
|
82
|
+
# If page is already loaded, reload to capture traffic
|
|
83
|
+
try:
|
|
84
|
+
from sycli.cdp.protocol import enable_page
|
|
85
|
+
await enable_page(client)
|
|
86
|
+
await client.send("Page.reload", {"ignoreCache": True})
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
await asyncio.sleep(duration)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
await disable_network(client)
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
# Fetch response bodies if requested
|
|
98
|
+
results = list(entries.values())
|
|
99
|
+
if include_body:
|
|
100
|
+
for entry in results:
|
|
101
|
+
if entry["failed"] or entry["status"] is None:
|
|
102
|
+
continue
|
|
103
|
+
mime = (entry.get("mime_type") or "").lower()
|
|
104
|
+
if any(t in mime for t in ("text", "json", "javascript", "html", "xml", "css")):
|
|
105
|
+
try:
|
|
106
|
+
body_result = await get_response_body(client, entry["request_id"])
|
|
107
|
+
body = body_result.get("body", "")
|
|
108
|
+
if body_result.get("base64Encoded"):
|
|
109
|
+
import base64
|
|
110
|
+
body = base64.b64decode(body).decode("utf-8", errors="replace")
|
|
111
|
+
entry["body"] = body[:10000] # Truncate large bodies
|
|
112
|
+
except Exception:
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
# Apply URL filter
|
|
116
|
+
if url_filter:
|
|
117
|
+
results = [e for e in results if url_filter in e.get("url", "")]
|
|
118
|
+
|
|
119
|
+
return results
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Page errors capability — capture JS exceptions, window.onerror, and log entries."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from sycli.cdp.client import CDPClient
|
|
9
|
+
from sycli.cdp.protocol import enable_log, enable_page, enable_runtime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def capture_errors(
|
|
13
|
+
client: CDPClient,
|
|
14
|
+
duration: float = 5.0,
|
|
15
|
+
) -> list[dict[str, Any]]:
|
|
16
|
+
"""Capture page errors for a duration.
|
|
17
|
+
|
|
18
|
+
Listens for Runtime.exceptionThrown, Page.windowOnError (via Runtime.evaluate),
|
|
19
|
+
and Log.entryAdded events.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
client: Connected CDP client.
|
|
23
|
+
duration: How long to capture (seconds).
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
List of error entries with type, message, url, line, column, stack.
|
|
27
|
+
"""
|
|
28
|
+
errors: list[dict[str, Any]] = []
|
|
29
|
+
|
|
30
|
+
def on_exception(params: dict) -> None:
|
|
31
|
+
detail = params.get("exceptionDetails", {})
|
|
32
|
+
exc = detail.get("exception", {})
|
|
33
|
+
errors.append({
|
|
34
|
+
"type": "exception",
|
|
35
|
+
"message": exc.get("description", detail.get("text", "")),
|
|
36
|
+
"url": detail.get("url", ""),
|
|
37
|
+
"line": detail.get("lineNumber", -1),
|
|
38
|
+
"column": detail.get("columnNumber", -1),
|
|
39
|
+
"stack": _format_stack(detail.get("stackTrace", {})),
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
def on_log_entry(params: dict) -> None:
|
|
43
|
+
entry = params.get("entry", {})
|
|
44
|
+
if entry.get("level") in ("error", "warning"):
|
|
45
|
+
errors.append({
|
|
46
|
+
"type": f"log_{entry.get('level', 'unknown')}",
|
|
47
|
+
"message": entry.get("text", ""),
|
|
48
|
+
"url": entry.get("url", ""),
|
|
49
|
+
"line": entry.get("lineNumber", -1),
|
|
50
|
+
"source": entry.get("source", ""),
|
|
51
|
+
"stack": _format_stack(entry.get("stackTrace", {})),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
client.on_event("Runtime.exceptionThrown", on_exception)
|
|
55
|
+
client.on_event("Log.entryAdded", on_log_entry)
|
|
56
|
+
|
|
57
|
+
await enable_page(client)
|
|
58
|
+
await enable_runtime(client)
|
|
59
|
+
await enable_log(client)
|
|
60
|
+
|
|
61
|
+
# Install window.onerror handler
|
|
62
|
+
await client.send("Runtime.evaluate", {
|
|
63
|
+
"expression": (
|
|
64
|
+
"window.onerror = function(msg, url, line, col, error) {"
|
|
65
|
+
" console.error('[window.onerror]', msg, url, line, col);"
|
|
66
|
+
"};"
|
|
67
|
+
),
|
|
68
|
+
"returnByValue": True,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
await asyncio.sleep(duration)
|
|
72
|
+
|
|
73
|
+
client.remove_event_handler("Runtime.exceptionThrown", on_exception)
|
|
74
|
+
client.remove_event_handler("Log.entryAdded", on_log_entry)
|
|
75
|
+
|
|
76
|
+
return errors
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _format_stack(stack_trace: dict) -> str:
|
|
80
|
+
"""Format a CDP StackTrace object into a readable string."""
|
|
81
|
+
frames = stack_trace.get("callFrames", [])
|
|
82
|
+
if not frames:
|
|
83
|
+
return ""
|
|
84
|
+
lines = []
|
|
85
|
+
for f in frames:
|
|
86
|
+
name = f.get("functionName", "<anonymous>")
|
|
87
|
+
url = f.get("url", "")
|
|
88
|
+
line = f.get("lineNumber", -1)
|
|
89
|
+
col = f.get("columnNumber", -1)
|
|
90
|
+
lines.append(f" at {name} ({url}:{line}:{col})")
|
|
91
|
+
return "\n".join(lines)
|