cmdop 0.1.16__tar.gz → 0.1.17__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.
- cmdop-0.1.17/PKG-INFO +249 -0
- cmdop-0.1.17/README.md +208 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/pyproject.toml +6 -1
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/__init__.py +1 -1
- cmdop-0.1.17/src/cmdop/services/browser/js/__init__.py +49 -0
- cmdop-0.1.17/src/cmdop/services/browser/js/core.py +38 -0
- cmdop-0.1.16/src/cmdop/services/browser/js.py → cmdop-0.1.17/src/cmdop/services/browser/js/fetch.py +1 -34
- cmdop-0.1.17/src/cmdop/services/browser/js/interaction.py +109 -0
- cmdop-0.1.17/src/cmdop/services/browser/js/scroll.py +133 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/models.py +32 -0
- cmdop-0.1.17/src/cmdop/services/browser/parsing.py +104 -0
- cmdop-0.1.17/src/cmdop/services/browser/sync/session.py +432 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/transport/discovery.py +25 -1
- cmdop-0.1.16/PKG-INFO +0 -464
- cmdop-0.1.16/README.md +0 -426
- cmdop-0.1.16/src/cmdop/services/browser/sync/session.py +0 -171
- {cmdop-0.1.16 → cmdop-0.1.17}/.gitignore +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/LICENSE +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/agent_messages_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/agent_messages_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/agent_messages_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/common_types_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/common_types_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/common_types_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/control_messages_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/control_messages_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/control_messages_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/archive_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/archive_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/archive_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/changes_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/changes_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/changes_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/common_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/common_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/common_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/directory_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/directory_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/directory_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/file_crud_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/file_crud_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/file_crud_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/hls_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/hls_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/hls_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/requests_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/requests_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/requests_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/search_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/search_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/search_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/transfer_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/transfer_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations/transfer_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_operations_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/archive_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/archive_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/archive_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/directory_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/directory_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/directory_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/file_crud_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/file_crud_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/file_crud_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/hls_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/hls_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/hls_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/search_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/search_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc/search_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/file_rpc_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/message_pool.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/py.typed +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/agent_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/agent_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/agent_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/browser_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/browser_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/browser_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/device_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/device_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/device_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/extract_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/extract_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/extract_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/health_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/health_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/health_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/history_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/history_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/history_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/lifecycle_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/lifecycle_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/lifecycle_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/push_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/push_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/push_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/session_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/session_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/session_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/terminal_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/terminal_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages/terminal_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/rpc_messages_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/service_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/service_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/service_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/tunnel_pb2.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/tunnel_pb2.pyi +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/_generated/tunnel_pb2_grpc.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/enums.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/logger.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machine_sharing/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machine_sharing/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machine_sharing/models.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machine_sharing/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machines/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machines/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machines/models.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/machines__api__machines/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/retry.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/schema.json +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/machines/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/enums.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/logger.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/retry.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/schema.json +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__oauth/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__oauth/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__oauth/models.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__oauth/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__system/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__system/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__system/models.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/system/system__api__system/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/enums.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/logger.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/retry.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/schema.json +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/models.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/sync_client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/client.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/config.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/discovery.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/exceptions.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/helpers/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/helpers/cleaner.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/helpers/formatting.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/logging.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/models/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/models/agent.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/models/base.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/models/config.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/models/extract.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/models/files.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/models/terminal.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/py.typed +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/agent.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/base.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/aio/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/aio/service.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/aio/session.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/base/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/base/service.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/base/session.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/sync/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/browser/sync/service.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/extract.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/files.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/services/terminal.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/streaming/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/streaming/base.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/streaming/handlers.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/streaming/terminal.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/transport/__init__.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/transport/auth.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/transport/base.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/transport/local.py +0 -0
- {cmdop-0.1.16 → cmdop-0.1.17}/src/cmdop/transport/remote.py +0 -0
cmdop-0.1.17/PKG-INFO
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cmdop
|
|
3
|
+
Version: 0.1.17
|
|
4
|
+
Summary: Python SDK for CMDOP agent interaction
|
|
5
|
+
Project-URL: Homepage, https://cmdop.com
|
|
6
|
+
Project-URL: Documentation, https://cmdop.com
|
|
7
|
+
Project-URL: Repository, https://github.com/markolofsen/cmdop-client
|
|
8
|
+
Author: CMDOP Team
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,automation,cmdop,terminal
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
23
|
+
Requires-Dist: grpcio>=1.60.0
|
|
24
|
+
Requires-Dist: httpx>=0.27.0
|
|
25
|
+
Requires-Dist: lxml>=5.0.0
|
|
26
|
+
Requires-Dist: protobuf>=4.25.0
|
|
27
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
28
|
+
Requires-Dist: pydantic>=2.5.0
|
|
29
|
+
Requires-Dist: rich>=13.0.0
|
|
30
|
+
Requires-Dist: toon-python>=0.1.2
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: beautifulsoup4>=4.12.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: grpcio-tools>=1.60.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: mypy>=1.8.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-grpc-aio>=0.3.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# cmdop
|
|
43
|
+
|
|
44
|
+
**Any machine. One API.**
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from cmdop import CMDOPClient
|
|
48
|
+
|
|
49
|
+
with CMDOPClient.remote(api_key="cmd_xxx") as server:
|
|
50
|
+
server.terminal.execute("docker restart app")
|
|
51
|
+
server.files.write("/etc/nginx/nginx.conf", new_config)
|
|
52
|
+
logs = server.files.read("/var/log/app.log")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
No SSH. No VPN. No open ports.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## How
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Your Code ──── Cloud Relay ──── Agent (on server)
|
|
63
|
+
│
|
|
64
|
+
Outbound only, works through any NAT/firewall
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Agent connects out. Your code connects to relay. Done.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install cmdop
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from cmdop import CMDOPClient, AsyncCMDOPClient
|
|
79
|
+
|
|
80
|
+
# Remote (via cloud relay)
|
|
81
|
+
with CMDOPClient.remote(api_key="cmd_xxx") as client:
|
|
82
|
+
client.files.list("/home")
|
|
83
|
+
|
|
84
|
+
# Local (direct IPC)
|
|
85
|
+
with CMDOPClient.local() as client:
|
|
86
|
+
client.terminal.execute("ls -la")
|
|
87
|
+
|
|
88
|
+
# Async
|
|
89
|
+
async with AsyncCMDOPClient.remote(api_key="cmd_xxx") as client:
|
|
90
|
+
await client.files.read("/etc/hostname")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Terminal
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
session = server.terminal.create()
|
|
99
|
+
server.terminal.send_input(session.session_id, "kubectl get pods\n")
|
|
100
|
+
output = server.terminal.get_history(session.session_id)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
| Method | Description |
|
|
104
|
+
|--------|-------------|
|
|
105
|
+
| `create(shell)` | Start session |
|
|
106
|
+
| `send_input(id, data)` | Send commands |
|
|
107
|
+
| `get_history(id)` | Get output |
|
|
108
|
+
| `resize(id, cols, rows)` | Resize |
|
|
109
|
+
| `send_signal(id, signal)` | SIGINT/SIGTERM |
|
|
110
|
+
| `close(id)` | End session |
|
|
111
|
+
|
|
112
|
+
## Files
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
server.files.list("/var/log")
|
|
116
|
+
server.files.read("/etc/nginx/nginx.conf")
|
|
117
|
+
server.files.write("/tmp/config.json", b'{"key": "value"}')
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| Method | Description |
|
|
121
|
+
|--------|-------------|
|
|
122
|
+
| `list(path)` | List dir |
|
|
123
|
+
| `read(path)` | Read file |
|
|
124
|
+
| `write(path, content)` | Write file |
|
|
125
|
+
| `delete(path)` | Delete |
|
|
126
|
+
| `copy/move(src, dst)` | Copy/Move |
|
|
127
|
+
| `mkdir(path)` | Create dir |
|
|
128
|
+
| `info(path)` | Metadata |
|
|
129
|
+
|
|
130
|
+
## Agent
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from pydantic import BaseModel
|
|
134
|
+
|
|
135
|
+
class Health(BaseModel):
|
|
136
|
+
status: str
|
|
137
|
+
cpu: float
|
|
138
|
+
issues: list[str]
|
|
139
|
+
|
|
140
|
+
result = server.agent.run("Check server health", output_schema=Health)
|
|
141
|
+
health: Health = result.output # Typed!
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Browser
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
with client.browser.create_session() as b:
|
|
150
|
+
b.navigate("https://shop.com/products")
|
|
151
|
+
|
|
152
|
+
# DOM extraction
|
|
153
|
+
products = b.extract_data(".product-card", '{"name": "h2", "price": ".price"}', limit=100)
|
|
154
|
+
|
|
155
|
+
# Get HTML for BeautifulSoup parsing
|
|
156
|
+
html = b.get_html("[role='feed']")
|
|
157
|
+
soup = b.parse_html(html) # Returns BeautifulSoup object
|
|
158
|
+
|
|
159
|
+
# Scroll & extract pattern
|
|
160
|
+
for _ in range(10):
|
|
161
|
+
html = b.get_html(".listings")
|
|
162
|
+
# ... parse with soup ...
|
|
163
|
+
b.scroll("down", 800)
|
|
164
|
+
b.wait_seconds(1.0)
|
|
165
|
+
|
|
166
|
+
# JS fetch (bypass CORS, inherit cookies)
|
|
167
|
+
data = b.fetch_json("https://api.site.com/v1/items")
|
|
168
|
+
|
|
169
|
+
# Parallel fetch
|
|
170
|
+
results = b.fetch_all({
|
|
171
|
+
"users": "https://api.site.com/v1/users",
|
|
172
|
+
"orders": "https://api.site.com/v1/orders",
|
|
173
|
+
}, credentials=True)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| Method | Description |
|
|
177
|
+
|--------|-------------|
|
|
178
|
+
| `navigate(url)` | Go to URL |
|
|
179
|
+
| `click(selector)` | Click element |
|
|
180
|
+
| `type(selector, text)` | Type text |
|
|
181
|
+
| `wait_for(selector, ms)` | Wait for element |
|
|
182
|
+
| `wait_seconds(n)` | Sleep |
|
|
183
|
+
| `extract(selector, attr)` | Get text/attr |
|
|
184
|
+
| `get_html(selector)` | Get HTML |
|
|
185
|
+
| `get_text(selector)` | Get text |
|
|
186
|
+
| `parse_html(html)` | → BeautifulSoup |
|
|
187
|
+
| `extract_data(item, fields, limit)` | Bulk extract |
|
|
188
|
+
| `fetch_json(url)` | JS fetch → dict |
|
|
189
|
+
| `fetch_all(urls, credentials)` | Parallel fetch |
|
|
190
|
+
| `execute_js(code)` | Run async JS |
|
|
191
|
+
| `screenshot()` | PNG bytes |
|
|
192
|
+
| `scroll(dir, amount)` | Scroll page |
|
|
193
|
+
| `scroll_to(selector)` | Scroll to element |
|
|
194
|
+
| `get_scroll_info()` | Position + page size |
|
|
195
|
+
| `infinite_scroll(fn, limit)` | Smart scroll loop |
|
|
196
|
+
| `hover(selector)` | Hover |
|
|
197
|
+
| `select(selector, value)` | Dropdown select |
|
|
198
|
+
| `close_modal()` | Close dialogs |
|
|
199
|
+
| `get/set_cookies()` | Cookie management |
|
|
200
|
+
|
|
201
|
+
## SDKBaseModel
|
|
202
|
+
|
|
203
|
+
Auto-cleaning Pydantic model for scraped data:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from cmdop import SDKBaseModel
|
|
207
|
+
|
|
208
|
+
class Product(SDKBaseModel):
|
|
209
|
+
__base_url__ = "https://shop.com"
|
|
210
|
+
name: str = "" # " iPhone 15 \n" → "iPhone 15"
|
|
211
|
+
price: int = 0 # "$1,299.00" → 1299
|
|
212
|
+
rating: float = 0 # "4.5 stars" → 4.5
|
|
213
|
+
url: str = "" # "/p/123" → "https://shop.com/p/123"
|
|
214
|
+
|
|
215
|
+
products = Product.from_list(raw["items"]) # Auto dedupe + filter
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Utilities
|
|
221
|
+
|
|
222
|
+
**Logging:**
|
|
223
|
+
```python
|
|
224
|
+
from cmdop import get_logger
|
|
225
|
+
log = get_logger(__name__)
|
|
226
|
+
log.info("Starting") # Rich console + auto file logging
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**TOON Format (30-50% token savings):**
|
|
230
|
+
```python
|
|
231
|
+
from cmdop import json_to_toon, JsonCleaner
|
|
232
|
+
toon = json_to_toon({"name": "Alice", "age": 25})
|
|
233
|
+
# → "name: Alice\nage: 25"
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Requirements
|
|
239
|
+
|
|
240
|
+
- Python 3.10+
|
|
241
|
+
- CMDOP agent on target
|
|
242
|
+
|
|
243
|
+
## Links
|
|
244
|
+
|
|
245
|
+
[cmdop.com](https://cmdop.com)
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT
|
cmdop-0.1.17/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# cmdop
|
|
2
|
+
|
|
3
|
+
**Any machine. One API.**
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from cmdop import CMDOPClient
|
|
7
|
+
|
|
8
|
+
with CMDOPClient.remote(api_key="cmd_xxx") as server:
|
|
9
|
+
server.terminal.execute("docker restart app")
|
|
10
|
+
server.files.write("/etc/nginx/nginx.conf", new_config)
|
|
11
|
+
logs = server.files.read("/var/log/app.log")
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
No SSH. No VPN. No open ports.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## How
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Your Code ──── Cloud Relay ──── Agent (on server)
|
|
22
|
+
│
|
|
23
|
+
Outbound only, works through any NAT/firewall
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Agent connects out. Your code connects to relay. Done.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install cmdop
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from cmdop import CMDOPClient, AsyncCMDOPClient
|
|
38
|
+
|
|
39
|
+
# Remote (via cloud relay)
|
|
40
|
+
with CMDOPClient.remote(api_key="cmd_xxx") as client:
|
|
41
|
+
client.files.list("/home")
|
|
42
|
+
|
|
43
|
+
# Local (direct IPC)
|
|
44
|
+
with CMDOPClient.local() as client:
|
|
45
|
+
client.terminal.execute("ls -la")
|
|
46
|
+
|
|
47
|
+
# Async
|
|
48
|
+
async with AsyncCMDOPClient.remote(api_key="cmd_xxx") as client:
|
|
49
|
+
await client.files.read("/etc/hostname")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Terminal
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
session = server.terminal.create()
|
|
58
|
+
server.terminal.send_input(session.session_id, "kubectl get pods\n")
|
|
59
|
+
output = server.terminal.get_history(session.session_id)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Method | Description |
|
|
63
|
+
|--------|-------------|
|
|
64
|
+
| `create(shell)` | Start session |
|
|
65
|
+
| `send_input(id, data)` | Send commands |
|
|
66
|
+
| `get_history(id)` | Get output |
|
|
67
|
+
| `resize(id, cols, rows)` | Resize |
|
|
68
|
+
| `send_signal(id, signal)` | SIGINT/SIGTERM |
|
|
69
|
+
| `close(id)` | End session |
|
|
70
|
+
|
|
71
|
+
## Files
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
server.files.list("/var/log")
|
|
75
|
+
server.files.read("/etc/nginx/nginx.conf")
|
|
76
|
+
server.files.write("/tmp/config.json", b'{"key": "value"}')
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|--------|-------------|
|
|
81
|
+
| `list(path)` | List dir |
|
|
82
|
+
| `read(path)` | Read file |
|
|
83
|
+
| `write(path, content)` | Write file |
|
|
84
|
+
| `delete(path)` | Delete |
|
|
85
|
+
| `copy/move(src, dst)` | Copy/Move |
|
|
86
|
+
| `mkdir(path)` | Create dir |
|
|
87
|
+
| `info(path)` | Metadata |
|
|
88
|
+
|
|
89
|
+
## Agent
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from pydantic import BaseModel
|
|
93
|
+
|
|
94
|
+
class Health(BaseModel):
|
|
95
|
+
status: str
|
|
96
|
+
cpu: float
|
|
97
|
+
issues: list[str]
|
|
98
|
+
|
|
99
|
+
result = server.agent.run("Check server health", output_schema=Health)
|
|
100
|
+
health: Health = result.output # Typed!
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Browser
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
with client.browser.create_session() as b:
|
|
109
|
+
b.navigate("https://shop.com/products")
|
|
110
|
+
|
|
111
|
+
# DOM extraction
|
|
112
|
+
products = b.extract_data(".product-card", '{"name": "h2", "price": ".price"}', limit=100)
|
|
113
|
+
|
|
114
|
+
# Get HTML for BeautifulSoup parsing
|
|
115
|
+
html = b.get_html("[role='feed']")
|
|
116
|
+
soup = b.parse_html(html) # Returns BeautifulSoup object
|
|
117
|
+
|
|
118
|
+
# Scroll & extract pattern
|
|
119
|
+
for _ in range(10):
|
|
120
|
+
html = b.get_html(".listings")
|
|
121
|
+
# ... parse with soup ...
|
|
122
|
+
b.scroll("down", 800)
|
|
123
|
+
b.wait_seconds(1.0)
|
|
124
|
+
|
|
125
|
+
# JS fetch (bypass CORS, inherit cookies)
|
|
126
|
+
data = b.fetch_json("https://api.site.com/v1/items")
|
|
127
|
+
|
|
128
|
+
# Parallel fetch
|
|
129
|
+
results = b.fetch_all({
|
|
130
|
+
"users": "https://api.site.com/v1/users",
|
|
131
|
+
"orders": "https://api.site.com/v1/orders",
|
|
132
|
+
}, credentials=True)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
| Method | Description |
|
|
136
|
+
|--------|-------------|
|
|
137
|
+
| `navigate(url)` | Go to URL |
|
|
138
|
+
| `click(selector)` | Click element |
|
|
139
|
+
| `type(selector, text)` | Type text |
|
|
140
|
+
| `wait_for(selector, ms)` | Wait for element |
|
|
141
|
+
| `wait_seconds(n)` | Sleep |
|
|
142
|
+
| `extract(selector, attr)` | Get text/attr |
|
|
143
|
+
| `get_html(selector)` | Get HTML |
|
|
144
|
+
| `get_text(selector)` | Get text |
|
|
145
|
+
| `parse_html(html)` | → BeautifulSoup |
|
|
146
|
+
| `extract_data(item, fields, limit)` | Bulk extract |
|
|
147
|
+
| `fetch_json(url)` | JS fetch → dict |
|
|
148
|
+
| `fetch_all(urls, credentials)` | Parallel fetch |
|
|
149
|
+
| `execute_js(code)` | Run async JS |
|
|
150
|
+
| `screenshot()` | PNG bytes |
|
|
151
|
+
| `scroll(dir, amount)` | Scroll page |
|
|
152
|
+
| `scroll_to(selector)` | Scroll to element |
|
|
153
|
+
| `get_scroll_info()` | Position + page size |
|
|
154
|
+
| `infinite_scroll(fn, limit)` | Smart scroll loop |
|
|
155
|
+
| `hover(selector)` | Hover |
|
|
156
|
+
| `select(selector, value)` | Dropdown select |
|
|
157
|
+
| `close_modal()` | Close dialogs |
|
|
158
|
+
| `get/set_cookies()` | Cookie management |
|
|
159
|
+
|
|
160
|
+
## SDKBaseModel
|
|
161
|
+
|
|
162
|
+
Auto-cleaning Pydantic model for scraped data:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from cmdop import SDKBaseModel
|
|
166
|
+
|
|
167
|
+
class Product(SDKBaseModel):
|
|
168
|
+
__base_url__ = "https://shop.com"
|
|
169
|
+
name: str = "" # " iPhone 15 \n" → "iPhone 15"
|
|
170
|
+
price: int = 0 # "$1,299.00" → 1299
|
|
171
|
+
rating: float = 0 # "4.5 stars" → 4.5
|
|
172
|
+
url: str = "" # "/p/123" → "https://shop.com/p/123"
|
|
173
|
+
|
|
174
|
+
products = Product.from_list(raw["items"]) # Auto dedupe + filter
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Utilities
|
|
180
|
+
|
|
181
|
+
**Logging:**
|
|
182
|
+
```python
|
|
183
|
+
from cmdop import get_logger
|
|
184
|
+
log = get_logger(__name__)
|
|
185
|
+
log.info("Starting") # Rich console + auto file logging
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**TOON Format (30-50% token savings):**
|
|
189
|
+
```python
|
|
190
|
+
from cmdop import json_to_toon, JsonCleaner
|
|
191
|
+
toon = json_to_toon({"name": "Alice", "age": 25})
|
|
192
|
+
# → "name: Alice\nage: 25"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Requirements
|
|
198
|
+
|
|
199
|
+
- Python 3.10+
|
|
200
|
+
- CMDOP agent on target
|
|
201
|
+
|
|
202
|
+
## Links
|
|
203
|
+
|
|
204
|
+
[cmdop.com](https://cmdop.com)
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cmdop"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.17"
|
|
4
4
|
description = "Python SDK for CMDOP agent interaction"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -32,6 +32,9 @@ dependencies = [
|
|
|
32
32
|
"toon-python>=0.1.2",
|
|
33
33
|
# Logging
|
|
34
34
|
"rich>=13.0.0",
|
|
35
|
+
# HTML parsing (browser SDK)
|
|
36
|
+
"beautifulsoup4>=4.12.0",
|
|
37
|
+
"lxml>=5.0.0",
|
|
35
38
|
]
|
|
36
39
|
|
|
37
40
|
[project.optional-dependencies]
|
|
@@ -44,6 +47,8 @@ dev = [
|
|
|
44
47
|
"ruff>=0.1.0",
|
|
45
48
|
# For proto generation (still uses grpcio-tools with betterproto plugin)
|
|
46
49
|
"grpcio-tools>=1.60.0",
|
|
50
|
+
# HTML parsing for tests
|
|
51
|
+
"beautifulsoup4>=4.12.0",
|
|
47
52
|
]
|
|
48
53
|
|
|
49
54
|
[project.urls]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""JavaScript builders for browser automation.
|
|
2
|
+
|
|
3
|
+
This module provides JavaScript code generators for common browser operations:
|
|
4
|
+
- Core: JSON parsing, async wrappers
|
|
5
|
+
- Fetch: HTTP requests from browser context
|
|
6
|
+
- Scroll: Page scrolling and infinite scroll
|
|
7
|
+
- Interaction: Hover, select, modals
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from cmdop.services.browser.js.core import (
|
|
11
|
+
parse_json_result,
|
|
12
|
+
build_async_js,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from cmdop.services.browser.js.fetch import (
|
|
16
|
+
build_fetch_js,
|
|
17
|
+
build_fetch_all_js,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from cmdop.services.browser.js.scroll import (
|
|
21
|
+
build_scroll_js,
|
|
22
|
+
build_scroll_to_bottom_js,
|
|
23
|
+
build_infinite_scroll_js,
|
|
24
|
+
build_get_scroll_info_js,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from cmdop.services.browser.js.interaction import (
|
|
28
|
+
build_hover_js,
|
|
29
|
+
build_select_js,
|
|
30
|
+
build_close_modal_js,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
# Core
|
|
35
|
+
"parse_json_result",
|
|
36
|
+
"build_async_js",
|
|
37
|
+
# Fetch
|
|
38
|
+
"build_fetch_js",
|
|
39
|
+
"build_fetch_all_js",
|
|
40
|
+
# Scroll
|
|
41
|
+
"build_scroll_js",
|
|
42
|
+
"build_scroll_to_bottom_js",
|
|
43
|
+
"build_infinite_scroll_js",
|
|
44
|
+
"build_get_scroll_info_js",
|
|
45
|
+
# Interaction
|
|
46
|
+
"build_hover_js",
|
|
47
|
+
"build_select_js",
|
|
48
|
+
"build_close_modal_js",
|
|
49
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Core JavaScript utilities for browser automation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def parse_json_result(result: str) -> dict | list | None:
|
|
10
|
+
"""Parse JSON result from JS execution."""
|
|
11
|
+
if result:
|
|
12
|
+
try:
|
|
13
|
+
data = json.loads(result)
|
|
14
|
+
if isinstance(data, dict) and "__error" in data:
|
|
15
|
+
return None
|
|
16
|
+
return data
|
|
17
|
+
except json.JSONDecodeError:
|
|
18
|
+
return None
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_async_js(code: str) -> str:
|
|
23
|
+
"""
|
|
24
|
+
Wrap JS code in async IIFE with error handling.
|
|
25
|
+
|
|
26
|
+
The code should return a value (will be JSON.stringify'd).
|
|
27
|
+
Use for async operations like fetch, Promise.all, etc.
|
|
28
|
+
"""
|
|
29
|
+
return f"""
|
|
30
|
+
(async function() {{
|
|
31
|
+
try {{
|
|
32
|
+
const result = await (async () => {{ {code} }})();
|
|
33
|
+
return JSON.stringify(result);
|
|
34
|
+
}} catch(e) {{
|
|
35
|
+
return JSON.stringify({{__error: e.message}});
|
|
36
|
+
}}
|
|
37
|
+
}})()
|
|
38
|
+
"""
|
cmdop-0.1.16/src/cmdop/services/browser/js.py → cmdop-0.1.17/src/cmdop/services/browser/js/fetch.py
RENAMED
|
@@ -1,28 +1,8 @@
|
|
|
1
|
-
"""JavaScript
|
|
1
|
+
"""Fetch JavaScript builders for browser automation."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def build_async_js(code: str) -> str:
|
|
10
|
-
"""
|
|
11
|
-
Wrap JS code in async IIFE with error handling.
|
|
12
|
-
|
|
13
|
-
The code should return a value (will be JSON.stringify'd).
|
|
14
|
-
Use for async operations like fetch, Promise.all, etc.
|
|
15
|
-
"""
|
|
16
|
-
return f"""
|
|
17
|
-
(async function() {{
|
|
18
|
-
try {{
|
|
19
|
-
const result = await (async () => {{ {code} }})();
|
|
20
|
-
return JSON.stringify(result);
|
|
21
|
-
}} catch(e) {{
|
|
22
|
-
return JSON.stringify({{__error: e.message}});
|
|
23
|
-
}}
|
|
24
|
-
}})()
|
|
25
|
-
"""
|
|
26
6
|
|
|
27
7
|
|
|
28
8
|
def build_fetch_js(url: str) -> str:
|
|
@@ -94,16 +74,3 @@ def build_fetch_all_js(
|
|
|
94
74
|
|
|
95
75
|
return results;
|
|
96
76
|
"""
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def parse_json_result(result: str) -> dict | list | None:
|
|
100
|
-
"""Parse JSON result from JS execution."""
|
|
101
|
-
if result:
|
|
102
|
-
try:
|
|
103
|
-
data = json.loads(result)
|
|
104
|
-
if isinstance(data, dict) and "__error" in data:
|
|
105
|
-
return None
|
|
106
|
-
return data
|
|
107
|
-
except json.JSONDecodeError:
|
|
108
|
-
return None
|
|
109
|
-
return None
|