agentenv-framework-protocol 0.1.490__tar.gz → 0.1.493__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.
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/PKG-INFO +17 -7
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/README.md +16 -6
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/pyproject.toml +1 -1
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/client.py +3 -3
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_session_posix.py +46 -7
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/app.py +2 -1
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/app_runtime.py +6 -6
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/checks.py +18 -10
- agentenv_framework_protocol-0.1.493/src/agentenv_protocol/computer/conformance/desk.py +65 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/fixture.py +32 -44
- agentenv_framework_protocol-0.1.493/src/agentenv_protocol/computer/conformance/wayland_fixture.py +299 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website_browser/control.py +5 -2
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website_browser/shapes.py +1 -1
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_client.py +34 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_app_server.py +86 -1
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_conformance.py +63 -2
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_website_browser.py +21 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/.gitignore +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/LICENSE +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/NOTICE +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/THIRD_PARTY_NOTICES.md +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/advanced_agent.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/basic_agent.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/custom_extensions_agent.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/multimodal_agent.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/reference_model.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/streaming_agent.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/.gitignore +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/Dockerfile +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/Dockerfile.dockerignore +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/README.md +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/data/seed.json +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/env.toml +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/site.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/web/index.html +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/web/package-lock.json +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/web/package.json +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/web/src/main.js +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/web/src/style.css +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/examples/website/notes/web/vite.config.js +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/_triggers.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/extensions.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/framework.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/proxy.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/registry.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/staging.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/tasks/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/a2a_agent/tasks/v1.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/agent_env_environment.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/browser_cdp.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_declare.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_job.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_linux.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_load.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_macos.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_reaping.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_session_windows.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_with_fds.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/_x11.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/app_data.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/apps.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/card.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/checks.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/__main__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/client.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/mp4.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/page_fixture.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/conformance/report.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/dataplane.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/driver.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/errors.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/extensions.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/frames.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/keys.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/launch.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/operations.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/platform.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/prefix.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/queue.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/recording.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/results.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/run_install_step.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/runtime.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/seed.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/serve.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/serve_app.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/served.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/session.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/settings.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/skill.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/spaces.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/svc.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/computer/tools.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/errors.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/manifest.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/preflight.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/tool_access.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/transfers.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/types.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/clock.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/conformance.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/environment.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/errors.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/readiness.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/routes.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website/session.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website_browser/__init__.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website_browser/data.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website_browser/devtools.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/src/agentenv_protocol/website_browser/environment.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/computer_fake.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/probe_fake.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_agent.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_agent_examples.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_cdp_config.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_object_transfers.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_peer_agents.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_proxy.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_staging.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_a2a_trajectory_log.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_agent_env_environment.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_browser_cdp.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_browser_data.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_browser_state.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_app_data.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_conformance_checks.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_conformance_page.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_conformance_recording.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_dataplane.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_declare.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_driver.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_extensions.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_frames.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_macos.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_page.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_queue.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_recording.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_seed.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_serve.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_served.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_session_windows.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_spaces.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_svc_forward.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_computer_tool_prefix.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_control_token.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_data_objects.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_errors.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_extension_methods.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_intake.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_manifest.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_preflight.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_tool_access.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_tool_annotations.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_tool_client.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_tool_context.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_tool_context_fastmcp.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_tool_context_injection.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_website_api.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_website_conformance.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_website_environment.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_website_mcp.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_website_notes_example.py +0 -0
- {agentenv_framework_protocol-0.1.490 → agentenv_framework_protocol-0.1.493}/tests/test_website_readiness.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentenv-framework-protocol
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.493
|
|
4
4
|
Summary: Open data-plane protocol and server SDK for agent environments.
|
|
5
5
|
Project-URL: Homepage, https://www.agentenvframework.com
|
|
6
6
|
Project-URL: Documentation, https://www.agentenvframework.com/docs
|
|
@@ -757,15 +757,21 @@ driver booted with a control token is probed with it: `--token`, by default
|
|
|
757
757
|
| Level | Checks | Needs on the computer |
|
|
758
758
|
|---|---|---|
|
|
759
759
|
| `basic` | P0 ready and a screenshot; P1 the card and tool schemas; P2 native frames, regions and JPEG; P5 refusals and their codes; X the declared extensions (`computer-display`, `computer-spaces`, `computer-keyboard`, `computer-exec`, `data/get`, per-role tool access, the pointer) | nothing |
|
|
760
|
-
| `full` | `basic`, then P3 colours land where the mapping says; P4 clicks show their effect; P5 refusals change nothing; P6 typed and pressed keys arrive; P7 scrolls and drags arrive; P9 commands and `data/add` run as one unprivileged user; X pointer and keyboard input, `data/add`, and a recording's segments matching their files with no gap, a repeated `request_id` joining it and `max_concurrent` held; on a page, X the page tools: a prompt answered, a file uploaded, a reload, navigation, back and forward, and tabs | Linux, `python3` with Tk (on a page, `python3` and `computer-page/v1`), a 1280x800 screen or larger, `computer-exec` and `data/add` |
|
|
760
|
+
| `full` | `basic`, then P3 colours land where the mapping says; P4 clicks show their effect; P5 refusals change nothing; P6 typed and pressed keys arrive; P7 scrolls and drags arrive; P9 commands and `data/add` run as one unprivileged user; X pointer and keyboard input, `data/add`, and a recording's segments matching their files with no gap, a repeated `request_id` joining it and `max_concurrent` held; on a page, X the page tools: a prompt answered, a file uploaded, a reload, navigation, back and forward, and tabs | Linux, `python3` with Tk (on Wayland, with PyGObject and GTK 3 instead; on a page, `python3` and `computer-page/v1`), a 1280x800 screen or larger, `computer-exec` and `data/add` |
|
|
761
761
|
|
|
762
|
-
At `full` the probe writes the Fixture Desk, a Tk window (`conformance/fixture.py`),
|
|
763
|
-
`~/.agentenv-probe` with `data/add` and starts it with `computer-exec`. It aims at the boxes the
|
|
762
|
+
At `full` the probe writes the Fixture Desk, a Tk window (`conformance/fixture.py`), and its layout
|
|
763
|
+
(`conformance/desk.py`) into `~/.agentenv-probe` with `data/add` and starts it with `computer-exec`. It aims at the boxes the
|
|
764
764
|
window's widgets report, reads back what reached them, and then removes the fixture. On X11 the
|
|
765
765
|
window is maximized and kept above other windows, raising itself again when one covers it, so a
|
|
766
766
|
window an app keeps on top doesn't take the clicks aimed at it; elsewhere it is full screen. When the
|
|
767
767
|
fixture can't be staged, the `full` checks are unverified rather than failed.
|
|
768
768
|
|
|
769
|
+
On a driver whose platform's display is Wayland, the desk is drawn with GTK 3 as a native Wayland client
|
|
770
|
+
(`conformance/wayland_fixture.py`, staged beside `desk.py` in the Tk window's place), since Tk runs
|
|
771
|
+
only under XWayland. It goes full screen, takes its origin from Hyprland's IPC, keeps the clipboard
|
|
772
|
+
through wl-clipboard. There the probe selects a text box's text with GTK's own Ctrl+A rather than
|
|
773
|
+
Tk's Ctrl+/.
|
|
774
|
+
|
|
769
775
|
On a driver whose card says `screen: "page"`, the desk is an HTML page with the same widgets at the
|
|
770
776
|
same places (`conformance/page_fixture.py`). The probe starts its server on loopback, which writes
|
|
771
777
|
what the page reports to the same files, and opens the desk with `computer_tabs` in a tab of its
|
|
@@ -852,7 +858,8 @@ class MailApp(ComputerAppEnvironment):
|
|
|
852
858
|
it runs on (`platform.detect()`).
|
|
853
859
|
- **`launch=Launch(...)`** says how it starts in the desktop session: with it (`at="boot"`) or when
|
|
854
860
|
the agent opens it (`"demand"`). `ready=Ready(...)` says when it's ready, within `timeout_s` of its
|
|
855
|
-
launch: a window matching `launch.window` by its class, instance or title, a
|
|
861
|
+
launch: a window matching `launch.window` by its class, instance or title (on Hyprland, a Wayland
|
|
862
|
+
app ID or an X window's class, its program's file name, and its title), a port answering on
|
|
856
863
|
loopback, or a command exiting 0. `env` sets variables in the app's environment, over its server's. `@ready_check` marks a method for readiness that needs logic (a
|
|
857
864
|
sync one runs in a thread), and `@install_step(scope=...)` one a computer runs as it installs the
|
|
858
865
|
app, after its run scripts, offline and without the class's `__init__`. Each runs on every OS the
|
|
@@ -886,13 +893,16 @@ desktop session, and `ready` (GET: an `AppReadiness` of `ready`, `problems`, `la
|
|
|
886
893
|
and `timeout_s`), which runs the app's readiness checks then, at once, each within 10 s. A server
|
|
887
894
|
that can't start, or whose start in the session fails (adding the launcher, say), writes why to
|
|
888
895
|
`.agentenv/server.failure` in the folder and exits, and the driver reports that in its place. Once
|
|
889
|
-
the
|
|
896
|
+
the session is up, the X display when `DISPLAY` names one or on Hyprland its Wayland display and its
|
|
897
|
+
IPC, the server adds the app's launcher to the session user's
|
|
890
898
|
menu, which runs `python -m agentenv_protocol.computer.launch <folder>` to ask the server, so the
|
|
891
899
|
server starts every copy of the app, and launches a `boot` app, once per boot. An app that runs with
|
|
892
900
|
no desktop, a website browser alone, gets no launcher. The driver finds each app by the socket in its
|
|
893
901
|
folder: the computer is ready once every app's server answers and each `boot` app, and each app with
|
|
894
902
|
no launch, says it's ready; a `demand` app counts once something launches it. What the server does
|
|
895
|
-
in the session (`agentenv_protocol.computer.session`) is Linux's.
|
|
903
|
+
in the session (`agentenv_protocol.computer.session`) is Linux's. On Hyprland it lists windows through
|
|
904
|
+
Hyprland's IPC (`hyprctl -j clients`), which sees native Wayland windows and XWayland's alike, where
|
|
905
|
+
X's `xprop` sees only XWayland's.
|
|
896
906
|
|
|
897
907
|
The server serves the app's data plane and its skill. With `paths` alone, `data/add` writes the files
|
|
898
908
|
it's given under them and refuses any other path, or one a link leads out of them; `data/get`
|
|
@@ -692,15 +692,21 @@ driver booted with a control token is probed with it: `--token`, by default
|
|
|
692
692
|
| Level | Checks | Needs on the computer |
|
|
693
693
|
|---|---|---|
|
|
694
694
|
| `basic` | P0 ready and a screenshot; P1 the card and tool schemas; P2 native frames, regions and JPEG; P5 refusals and their codes; X the declared extensions (`computer-display`, `computer-spaces`, `computer-keyboard`, `computer-exec`, `data/get`, per-role tool access, the pointer) | nothing |
|
|
695
|
-
| `full` | `basic`, then P3 colours land where the mapping says; P4 clicks show their effect; P5 refusals change nothing; P6 typed and pressed keys arrive; P7 scrolls and drags arrive; P9 commands and `data/add` run as one unprivileged user; X pointer and keyboard input, `data/add`, and a recording's segments matching their files with no gap, a repeated `request_id` joining it and `max_concurrent` held; on a page, X the page tools: a prompt answered, a file uploaded, a reload, navigation, back and forward, and tabs | Linux, `python3` with Tk (on a page, `python3` and `computer-page/v1`), a 1280x800 screen or larger, `computer-exec` and `data/add` |
|
|
695
|
+
| `full` | `basic`, then P3 colours land where the mapping says; P4 clicks show their effect; P5 refusals change nothing; P6 typed and pressed keys arrive; P7 scrolls and drags arrive; P9 commands and `data/add` run as one unprivileged user; X pointer and keyboard input, `data/add`, and a recording's segments matching their files with no gap, a repeated `request_id` joining it and `max_concurrent` held; on a page, X the page tools: a prompt answered, a file uploaded, a reload, navigation, back and forward, and tabs | Linux, `python3` with Tk (on Wayland, with PyGObject and GTK 3 instead; on a page, `python3` and `computer-page/v1`), a 1280x800 screen or larger, `computer-exec` and `data/add` |
|
|
696
696
|
|
|
697
|
-
At `full` the probe writes the Fixture Desk, a Tk window (`conformance/fixture.py`),
|
|
698
|
-
`~/.agentenv-probe` with `data/add` and starts it with `computer-exec`. It aims at the boxes the
|
|
697
|
+
At `full` the probe writes the Fixture Desk, a Tk window (`conformance/fixture.py`), and its layout
|
|
698
|
+
(`conformance/desk.py`) into `~/.agentenv-probe` with `data/add` and starts it with `computer-exec`. It aims at the boxes the
|
|
699
699
|
window's widgets report, reads back what reached them, and then removes the fixture. On X11 the
|
|
700
700
|
window is maximized and kept above other windows, raising itself again when one covers it, so a
|
|
701
701
|
window an app keeps on top doesn't take the clicks aimed at it; elsewhere it is full screen. When the
|
|
702
702
|
fixture can't be staged, the `full` checks are unverified rather than failed.
|
|
703
703
|
|
|
704
|
+
On a driver whose platform's display is Wayland, the desk is drawn with GTK 3 as a native Wayland client
|
|
705
|
+
(`conformance/wayland_fixture.py`, staged beside `desk.py` in the Tk window's place), since Tk runs
|
|
706
|
+
only under XWayland. It goes full screen, takes its origin from Hyprland's IPC, keeps the clipboard
|
|
707
|
+
through wl-clipboard. There the probe selects a text box's text with GTK's own Ctrl+A rather than
|
|
708
|
+
Tk's Ctrl+/.
|
|
709
|
+
|
|
704
710
|
On a driver whose card says `screen: "page"`, the desk is an HTML page with the same widgets at the
|
|
705
711
|
same places (`conformance/page_fixture.py`). The probe starts its server on loopback, which writes
|
|
706
712
|
what the page reports to the same files, and opens the desk with `computer_tabs` in a tab of its
|
|
@@ -787,7 +793,8 @@ class MailApp(ComputerAppEnvironment):
|
|
|
787
793
|
it runs on (`platform.detect()`).
|
|
788
794
|
- **`launch=Launch(...)`** says how it starts in the desktop session: with it (`at="boot"`) or when
|
|
789
795
|
the agent opens it (`"demand"`). `ready=Ready(...)` says when it's ready, within `timeout_s` of its
|
|
790
|
-
launch: a window matching `launch.window` by its class, instance or title, a
|
|
796
|
+
launch: a window matching `launch.window` by its class, instance or title (on Hyprland, a Wayland
|
|
797
|
+
app ID or an X window's class, its program's file name, and its title), a port answering on
|
|
791
798
|
loopback, or a command exiting 0. `env` sets variables in the app's environment, over its server's. `@ready_check` marks a method for readiness that needs logic (a
|
|
792
799
|
sync one runs in a thread), and `@install_step(scope=...)` one a computer runs as it installs the
|
|
793
800
|
app, after its run scripts, offline and without the class's `__init__`. Each runs on every OS the
|
|
@@ -821,13 +828,16 @@ desktop session, and `ready` (GET: an `AppReadiness` of `ready`, `problems`, `la
|
|
|
821
828
|
and `timeout_s`), which runs the app's readiness checks then, at once, each within 10 s. A server
|
|
822
829
|
that can't start, or whose start in the session fails (adding the launcher, say), writes why to
|
|
823
830
|
`.agentenv/server.failure` in the folder and exits, and the driver reports that in its place. Once
|
|
824
|
-
the
|
|
831
|
+
the session is up, the X display when `DISPLAY` names one or on Hyprland its Wayland display and its
|
|
832
|
+
IPC, the server adds the app's launcher to the session user's
|
|
825
833
|
menu, which runs `python -m agentenv_protocol.computer.launch <folder>` to ask the server, so the
|
|
826
834
|
server starts every copy of the app, and launches a `boot` app, once per boot. An app that runs with
|
|
827
835
|
no desktop, a website browser alone, gets no launcher. The driver finds each app by the socket in its
|
|
828
836
|
folder: the computer is ready once every app's server answers and each `boot` app, and each app with
|
|
829
837
|
no launch, says it's ready; a `demand` app counts once something launches it. What the server does
|
|
830
|
-
in the session (`agentenv_protocol.computer.session`) is Linux's.
|
|
838
|
+
in the session (`agentenv_protocol.computer.session`) is Linux's. On Hyprland it lists windows through
|
|
839
|
+
Hyprland's IPC (`hyprctl -j clients`), which sees native Wayland windows and XWayland's alike, where
|
|
840
|
+
X's `xprop` sees only XWayland's.
|
|
831
841
|
|
|
832
842
|
The server serves the app's data plane and its skill. With `paths` alone, `data/add` writes the files
|
|
833
843
|
it's given under them and refuses any other path, or one a link leads out of them; `data/get`
|
|
@@ -80,7 +80,7 @@ def granted_headers(url: str, grants: dict[str | None, dict[str, str]]) -> dict[
|
|
|
80
80
|
async def _rpc(base_url: str, method: str, params: dict, timeout: int, verify: bool,
|
|
81
81
|
headers: dict[str, str] | None) -> dict:
|
|
82
82
|
request = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params}
|
|
83
|
-
url = f"{base_url}{RPC_PATH}"
|
|
83
|
+
url = f"{base_url.rstrip('/')}{RPC_PATH}"
|
|
84
84
|
async with httpx.AsyncClient(verify=verify) as client:
|
|
85
85
|
response = await client.post(url, json=request, timeout=timeout, headers=request_headers(url, headers))
|
|
86
86
|
response.raise_for_status()
|
|
@@ -117,7 +117,7 @@ async def get_data(
|
|
|
117
117
|
|
|
118
118
|
async def get_card(base_url: str, timeout: int = 10, verify: bool = True) -> dict:
|
|
119
119
|
async with httpx.AsyncClient(verify=verify) as client:
|
|
120
|
-
response = await client.get(f"{base_url}{WELL_KNOWN_PATH}", timeout=timeout)
|
|
120
|
+
response = await client.get(f"{base_url.rstrip('/')}{WELL_KNOWN_PATH}", timeout=timeout)
|
|
121
121
|
response.raise_for_status()
|
|
122
122
|
return response.json()
|
|
123
123
|
|
|
@@ -341,7 +341,7 @@ async def invoke_extension(base_url: str, card: dict, uri: str, params: dict | N
|
|
|
341
341
|
raise ValueError(f"extension {uri} does not advertise method {method!r} (advertises: {offered})")
|
|
342
342
|
if not op["endpoint"]:
|
|
343
343
|
raise ValueError(f"extension {uri} has no endpoint in params")
|
|
344
|
-
url = f"{base_url}{op['endpoint']}"
|
|
344
|
+
url = f"{base_url.rstrip('/')}{op['endpoint']}"
|
|
345
345
|
sent = request_headers(url, headers)
|
|
346
346
|
async with httpx.AsyncClient(verify=verify) as client:
|
|
347
347
|
if op["method"] == "GET":
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""What a computer app's own server does in the desktop session on Linux and macOS (``session``): wait for the X
|
|
2
|
-
session, launch the app into a session of its own and stop or pause it
|
|
3
|
-
launcher and list the
|
|
2
|
+
session, or on Hyprland for its Wayland and IPC sockets, launch the app into a session of its own and stop or pause it
|
|
3
|
+
by its process group, add its freedesktop menu launcher and list the session's windows, X's with ``xprop`` or
|
|
4
|
+
Hyprland's with ``hyprctl``."""
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
|
|
6
7
|
import asyncio
|
|
7
8
|
import contextlib
|
|
9
|
+
import json
|
|
8
10
|
import os
|
|
9
11
|
import re
|
|
10
12
|
import signal
|
|
@@ -21,9 +23,13 @@ WITH_FDS = Path(__file__).with_name("_with_fds.py")
|
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
async def wait_until_up() -> None:
|
|
24
|
-
"""Until the
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
"""Until the session's sockets are there: on Hyprland its Wayland display's and its IPC's, else the X display's."""
|
|
27
|
+
if signature := _hyprland():
|
|
28
|
+
runtime = Path(os.environ["XDG_RUNTIME_DIR"])
|
|
29
|
+
sockets = [runtime / os.environ["WAYLAND_DISPLAY"], runtime / "hypr" / signature / ".socket.sock"]
|
|
30
|
+
else:
|
|
31
|
+
sockets = [Path(f"/tmp/.X11-unix/X{os.environ['DISPLAY'].lstrip(':').split('.')[0]}")]
|
|
32
|
+
while not all(socket.exists() for socket in sockets):
|
|
27
33
|
await asyncio.sleep(POLL_S)
|
|
28
34
|
|
|
29
35
|
|
|
@@ -101,7 +107,11 @@ def add_launcher(name: str, folder: Path) -> Path:
|
|
|
101
107
|
async def windows() -> list[dict[str, str]]:
|
|
102
108
|
"""The window manager's top-level windows, each as its WM_CLASS ``instance`` and ``class`` and its ``title``, read
|
|
103
109
|
with ``xprop``: ``_NET_WM_NAME``, or ``WM_NAME`` for a window that sets only that; a window that closes while it's
|
|
104
|
-
read is left out.
|
|
110
|
+
read is left out. On Hyprland, its mapped clients as its IPC lists them, native Wayland and XWayland alike: its
|
|
111
|
+
``class``, a Wayland app ID or an X window's WM_CLASS class, and its ``title``, and as its ``instance``, which a
|
|
112
|
+
Wayland window hasn't, its program's file name, as on Windows."""
|
|
113
|
+
if _hyprland():
|
|
114
|
+
return await _clients()
|
|
105
115
|
listed = await _xprop("-root", "_NET_CLIENT_LIST")
|
|
106
116
|
found = []
|
|
107
117
|
for window in re.findall(r"0x[0-9a-f]+", listed.partition("#")[2]):
|
|
@@ -119,8 +129,37 @@ def _strings(value: str) -> list[str]:
|
|
|
119
129
|
return [re.sub(r"\\(.)", r"\1", text) for text in re.findall(r'"((?:[^"\\]|\\.)*)"', value)]
|
|
120
130
|
|
|
121
131
|
|
|
132
|
+
def _hyprland() -> str | None:
|
|
133
|
+
"""The Hyprland instance whose session this is, by its signature; None outside one."""
|
|
134
|
+
return os.environ.get("HYPRLAND_INSTANCE_SIGNATURE") or None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async def _clients() -> list[dict[str, str]]:
|
|
138
|
+
"""Hyprland's mapped clients, by their program, class and title; none when its IPC doesn't answer."""
|
|
139
|
+
try:
|
|
140
|
+
clients = json.loads(await _output("hyprctl", "-j", "clients") or "[]")
|
|
141
|
+
except ValueError:
|
|
142
|
+
return []
|
|
143
|
+
return [{"instance": _program(client.get("pid")), "class": client.get("class") or "",
|
|
144
|
+
"title": client.get("title") or ""}
|
|
145
|
+
for client in clients if isinstance(client, dict) and client.get("mapped")]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _program(pid: object) -> str:
|
|
149
|
+
"""The file name of the program process ``pid`` runs, or "" when it can't be read."""
|
|
150
|
+
try:
|
|
151
|
+
return Path(os.readlink(f"/proc/{int(pid)}/exe")).name
|
|
152
|
+
except (OSError, TypeError, ValueError):
|
|
153
|
+
return ""
|
|
154
|
+
|
|
155
|
+
|
|
122
156
|
async def _xprop(*args: str) -> str:
|
|
123
|
-
|
|
157
|
+
return await _output("xprop", *args)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
async def _output(*argv: str) -> str:
|
|
161
|
+
"""What ``argv`` prints, or nothing when it fails; killed when the listing it's part of is cut short."""
|
|
162
|
+
process = await asyncio.create_subprocess_exec(*argv, stdout=asyncio.subprocess.PIPE,
|
|
124
163
|
stderr=asyncio.subprocess.DEVNULL)
|
|
125
164
|
try:
|
|
126
165
|
out, _ = await process.communicate()
|
|
@@ -187,7 +187,8 @@ class Launch(BaseModel):
|
|
|
187
187
|
at: Literal["boot", "demand"] = "boot"
|
|
188
188
|
command: list[str]
|
|
189
189
|
#: Matches the app's window by ``class``, ``instance`` and ``title``, as given: on X11 its WM_CLASS class and
|
|
190
|
-
#: instance; on
|
|
190
|
+
#: instance; on Hyprland its class, a Wayland app ID or an X window's WM_CLASS class, and its program's file name;
|
|
191
|
+
#: on Windows its window class and its program's file name, lowercased, such as ``notepad.exe``.
|
|
191
192
|
window: Optional[dict[str, str]] = None
|
|
192
193
|
ready: Optional[Ready] = None
|
|
193
194
|
#: Variables set in the app's environment, over its server's.
|
|
@@ -80,14 +80,14 @@ class AppRuntime:
|
|
|
80
80
|
self.checking: dict[str, asyncio.Future] = {}
|
|
81
81
|
|
|
82
82
|
async def start(self) -> None:
|
|
83
|
-
"""Once its X display
|
|
84
|
-
unless the computer has launched it since it booted, at boot or on demand, or its boot
|
|
85
|
-
(``boot_launch_held``): a server that restarts leaves the app be, or with ``relaunch_on_start``,
|
|
86
|
-
only the server that launched it can talk to, stops it and launches it again, whether it was
|
|
87
|
-
or on demand."""
|
|
83
|
+
"""Once its session is up, its X display or Hyprland, when it has one, add the app's menu launcher on a desktop
|
|
84
|
+
and launch a boot app, unless the computer has launched it since it booted, at boot or on demand, or its boot
|
|
85
|
+
launch is held (``boot_launch_held``): a server that restarts leaves the app be, or with ``relaunch_on_start``,
|
|
86
|
+
for an app only the server that launched it can talk to, stops it and launches it again, whether it was
|
|
87
|
+
launched at boot or on demand."""
|
|
88
88
|
if self.launch is None:
|
|
89
89
|
return
|
|
90
|
-
if os.environ.get("DISPLAY"):
|
|
90
|
+
if os.environ.get("DISPLAY") or os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
|
|
91
91
|
await session.wait_until_up()
|
|
92
92
|
if self.desktop:
|
|
93
93
|
session.add_launcher(os.environ["ENVIRONMENT_NAME"], Path.cwd())
|
|
@@ -116,8 +116,15 @@ class Probe:
|
|
|
116
116
|
self.layout: dict = {}
|
|
117
117
|
self.fixture_pid: str | None = None
|
|
118
118
|
self.page = self.features.get("screen") == "page"
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
platform = self.features.get("platform") or {}
|
|
120
|
+
#: Whether the desk is the GTK one: on Wayland, where Tk runs only under XWayland, which a Wayland driver may
|
|
121
|
+
#: not reach.
|
|
122
|
+
self.gtk = not self.page and platform.get("display") == "wayland"
|
|
123
|
+
self.fixture = "page_fixture.py" if self.page else "wayland_fixture.py" if self.gtk else "fixture.py"
|
|
124
|
+
self.windows = platform.get("os") == "windows"
|
|
125
|
+
#: The chord that selects a text box's text: Tk's Ctrl+/, which the Tk desk binds everywhere, or on the GTK
|
|
126
|
+
#: desk GTK's own Ctrl+A.
|
|
127
|
+
self.select_all = ["Control", "a"] if self.gtk else ["Control", "/"]
|
|
121
128
|
self.desk = ""
|
|
122
129
|
self.tab: str | None = None
|
|
123
130
|
self.opened: list[str] = []
|
|
@@ -562,12 +569,13 @@ class Probe:
|
|
|
562
569
|
# full: the fixture
|
|
563
570
|
|
|
564
571
|
async def _stage(self) -> str | None:
|
|
565
|
-
"""The Fixture Desk started, or what stopped it: the Tk desk
|
|
566
|
-
desk opened in a tab of the probe's own."""
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
572
|
+
"""The Fixture Desk started, or what stopped it: the Tk desk or on Wayland the GTK desk, either beside the layout
|
|
573
|
+
they share (desk.py), or on a page, the HTML desk's server, with the desk opened in a tab of the probe's own."""
|
|
574
|
+
for file in [self.fixture] if self.page else [self.fixture, "desk.py"]:
|
|
575
|
+
source = resources.files(__package__).joinpath(file).read_bytes()
|
|
576
|
+
added = await self.driver.add_file(f"{STAGE}/{file}", source)
|
|
577
|
+
if "error" in added:
|
|
578
|
+
return f"data/add refused it: {added['error'].get('message')}"
|
|
571
579
|
if self.windows:
|
|
572
580
|
await self.driver.run(*WIN_REMOVE, f"{STAGE}/state")
|
|
573
581
|
started = await self.driver.run(*WIN_START, f"{STAGE}/{self.fixture}", f"{STAGE}/state", f"{STAGE}/fixture.log")
|
|
@@ -781,7 +789,7 @@ class Probe:
|
|
|
781
789
|
|
|
782
790
|
async def clear() -> None:
|
|
783
791
|
await focus("name")
|
|
784
|
-
await self.act("computer_key", {"keys":
|
|
792
|
+
await self.act("computer_key", {"keys": self.select_all})
|
|
785
793
|
await self.act("computer_key", {"keys": ["Backspace"]})
|
|
786
794
|
|
|
787
795
|
async def unicode() -> str | None:
|
|
@@ -796,7 +804,7 @@ class Probe:
|
|
|
796
804
|
|
|
797
805
|
async def chord() -> str | None:
|
|
798
806
|
await focus("name")
|
|
799
|
-
await self.act("computer_key", {"keys":
|
|
807
|
+
await self.act("computer_key", {"keys": self.select_all})
|
|
800
808
|
await self.act("computer_key", {"keys": ["Backspace"]})
|
|
801
809
|
return await self.settled(lambda s: s["name"] == "", lambda s: s["name"])
|
|
802
810
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""The Fixture Desk's layout and what it records, shared by the Tk desk (fixture.py) and the GTK desk
|
|
2
|
+
(wayland_fixture.py), so the two can't drift apart. The probe stages it beside whichever desk it starts. It imports no
|
|
3
|
+
toolkit, since a Wayland computer may have no Tk."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
#: The colours a grid cell cycles through, one more on each click.
|
|
11
|
+
COLOURS = ("#ffffff", "#ff0000", "#00aa00", "#0000ff")
|
|
12
|
+
CELL = 50
|
|
13
|
+
GRID = 8
|
|
14
|
+
#: Single-pixel markers on a black field, at odd and even pixels: (x, y) within the field, then the colour.
|
|
15
|
+
MARKERS = ((11, 11, "#ff0000"), (12, 11, "#00ff00"), (11, 12, "#0000ff"), (12, 12, "#ffff00"), (37, 20, "#ff00ff"),
|
|
16
|
+
(20, 37, "#00ffff"))
|
|
17
|
+
CLIPBOARD = "agentenv-probe clipboard"
|
|
18
|
+
MIN_SIZE = (1280, 800)
|
|
19
|
+
#: Where each widget's top left goes, the name box's label at "label" and the list's frame at "list".
|
|
20
|
+
AT = {"label": (40, 40), "name": (140, 40), "save": (560, 34), "subscribe": (40, 100), "notes": (760, 40),
|
|
21
|
+
"list": (40, 160), "grid": (400, 160), "pad": (840, 160), "wheel": (840, 500), "field": (1080, 500)}
|
|
22
|
+
#: The widgets sized in pixels: the list's frame, the drawing pad, the wheel and the marker field.
|
|
23
|
+
SIZE = {"list": {"width": 300, "height": 400}, "pad": {"width": 400, "height": 300},
|
|
24
|
+
"wheel": {"width": 200, "height": 120}, "field": {"width": 48, "height": 48}}
|
|
25
|
+
#: The list's rows, Item 1 to Item ITEMS.
|
|
26
|
+
ITEMS = 100
|
|
27
|
+
#: How many of the latest keys and drags state.json keeps, and about how many points of a drag's path.
|
|
28
|
+
KEYS_KEPT, DRAGS_KEPT, PATH_POINTS = 100, 20, 50
|
|
29
|
+
#: How often the desk reads inbox.txt.
|
|
30
|
+
INBOX_EVERY_MS = 200
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def new_state(width: int, height: int, windowing: str) -> dict:
|
|
34
|
+
"""What state.json holds before anything reaches the desk."""
|
|
35
|
+
return {"name": "", "notes": "", "keys": [], "saved_count": 0, "subscribed": False, "selected": None,
|
|
36
|
+
"scroll_top": 0.0, "cells": {}, "right_clicks": 0, "double_clicks": 0, "drags": [],
|
|
37
|
+
"wheel": {"up": 0, "down": 0, "left": 0, "right": 0}, "clipboard": None, "loaded": None,
|
|
38
|
+
"screen": [width, height], "windowing": windowing}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def write_state(state_dir: Path, state: dict) -> None:
|
|
42
|
+
"""state.json replaced whole, so the probe never reads half of one."""
|
|
43
|
+
part = state_dir / ".state.json.part"
|
|
44
|
+
part.write_text(json.dumps(state))
|
|
45
|
+
os.replace(part, state_dir / "state.json")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def write_layout(state_dir: Path, boxes: dict, row_height: int, field: tuple[int, int]) -> None:
|
|
49
|
+
"""layout.json: each widget's box on the screen, a list row's height, the markers' pixels from the field's top left,
|
|
50
|
+
and the cell size and clipboard text the probe checks against."""
|
|
51
|
+
layout = {**boxes, "list_row_height": row_height,
|
|
52
|
+
"markers": [[field[0] + x, field[1] + y, colour] for x, y, colour in MARKERS], "cell": CELL,
|
|
53
|
+
"clipboard": CLIPBOARD}
|
|
54
|
+
(state_dir / "layout.json").write_text(json.dumps(layout))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def record_key(state: dict, key: str) -> None:
|
|
58
|
+
state["keys"] = (state["keys"] + [key])[-KEYS_KEPT:]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def record_drag(state: dict, stroke: list) -> None:
|
|
62
|
+
"""A finished drag: its ends, its number of points and its path thinned to about PATH_POINTS points."""
|
|
63
|
+
path = stroke[::max(1, len(stroke) // PATH_POINTS)] + stroke[-1:]
|
|
64
|
+
drag = {"start": stroke[0], "end": stroke[-1], "points": len(stroke), "path": path}
|
|
65
|
+
state["drags"] = (state["drags"] + [drag])[-DRAGS_KEPT:]
|
|
@@ -3,26 +3,19 @@ Desk, whose widgets' screen boxes are written to <state-dir>/layout.json and eve
|
|
|
3
3
|
whatever lands in <state-dir>/inbox.txt, to <state-dir>/state.json, so the probe can aim at it and read back what
|
|
4
4
|
arrived. It records the same effects under X11, macOS's Aqua and Windows, whose Tk names keys, binds select-all and
|
|
5
5
|
reports the wheel differently. On X11 it stays above an app's windows (``keep_on_top``); elsewhere it's full screen.
|
|
6
|
-
Run as ``python3 fixture.py <state-dir
|
|
6
|
+
Its layout and what it records are desk.py's, which the GTK desk shares. Run as ``python3 fixture.py <state-dir>``
|
|
7
|
+
beside desk.py; it needs a 1280x800 screen or larger."""
|
|
7
8
|
from __future__ import annotations
|
|
8
9
|
|
|
9
10
|
import ctypes
|
|
10
|
-
import json
|
|
11
|
-
import os
|
|
12
11
|
import sys
|
|
13
12
|
import time
|
|
14
13
|
import tkinter as tk
|
|
15
14
|
from pathlib import Path
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
GRID = 8
|
|
21
|
-
#: Single-pixel markers on a black field, at odd and even pixels: (x, y) within the field, then the colour.
|
|
22
|
-
MARKERS = ((11, 11, "#ff0000"), (12, 11, "#00ff00"), (11, 12, "#0000ff"), (12, 12, "#ffff00"), (37, 20, "#ff00ff"),
|
|
23
|
-
(20, 37, "#00ffff"))
|
|
24
|
-
CLIPBOARD = "agentenv-probe clipboard"
|
|
25
|
-
MIN_SIZE = (1280, 800)
|
|
16
|
+
from desk import (AT, CELL, CLIPBOARD, COLOURS, GRID, INBOX_EVERY_MS, ITEMS, MARKERS, MIN_SIZE, SIZE, new_state,
|
|
17
|
+
record_drag, record_key, write_layout, write_state)
|
|
18
|
+
|
|
26
19
|
#: X11's keysyms for the keys Aqua's Tk names by their character.
|
|
27
20
|
KEYSYMS = {",": "comma", " ": "space"}
|
|
28
21
|
#: A wheel notch's <MouseWheel> delta where Tk scales the wheel: Windows', and Tk 9's everywhere.
|
|
@@ -46,15 +39,10 @@ def main(state_dir: Path) -> None:
|
|
|
46
39
|
else:
|
|
47
40
|
root.attributes("-fullscreen", True)
|
|
48
41
|
root.configure(bg="#f0f0f0")
|
|
49
|
-
state
|
|
50
|
-
"scroll_top": 0.0, "cells": {}, "right_clicks": 0, "double_clicks": 0, "drags": [],
|
|
51
|
-
"wheel": {"up": 0, "down": 0, "left": 0, "right": 0}, "clipboard": None, "loaded": None,
|
|
52
|
-
"screen": [width, height], "windowing": root.tk.call("tk", "windowingsystem")}
|
|
42
|
+
state = new_state(width, height, root.tk.call("tk", "windowingsystem"))
|
|
53
43
|
|
|
54
44
|
def save() -> None:
|
|
55
|
-
|
|
56
|
-
part.write_text(json.dumps(state))
|
|
57
|
-
os.replace(part, state_dir / "state.json")
|
|
45
|
+
write_state(state_dir, state)
|
|
58
46
|
|
|
59
47
|
if width < MIN_SIZE[0] or height < MIN_SIZE[1]:
|
|
60
48
|
state["error"] = f"the screen is {width}x{height}; the fixture needs {MIN_SIZE[0]}x{MIN_SIZE[1]}"
|
|
@@ -63,13 +51,13 @@ def main(state_dir: Path) -> None:
|
|
|
63
51
|
return
|
|
64
52
|
|
|
65
53
|
font = ("DejaVu Sans", 16)
|
|
66
|
-
tk.Label(root, text="Name:", font=font, bg="#f0f0f0")
|
|
54
|
+
_place(tk.Label(root, text="Name:", font=font, bg="#f0f0f0"), "label")
|
|
67
55
|
name = tk.Entry(root, font=font, width=30)
|
|
68
|
-
name
|
|
56
|
+
_place(name, "name")
|
|
69
57
|
|
|
70
58
|
def key(event) -> None:
|
|
71
59
|
state["name"] = name.get()
|
|
72
|
-
|
|
60
|
+
record_key(state, KEYSYMS.get(event.keysym, event.keysym))
|
|
73
61
|
state["clipboard"] = _clipboard(root)
|
|
74
62
|
save()
|
|
75
63
|
|
|
@@ -88,7 +76,7 @@ def main(state_dir: Path) -> None:
|
|
|
88
76
|
save()
|
|
89
77
|
|
|
90
78
|
button = tk.Button(root, text="Save", font=font, command=on_save, width=8)
|
|
91
|
-
button
|
|
79
|
+
_place(button, "save")
|
|
92
80
|
|
|
93
81
|
subscribed = tk.BooleanVar(value=False)
|
|
94
82
|
|
|
@@ -98,10 +86,10 @@ def main(state_dir: Path) -> None:
|
|
|
98
86
|
|
|
99
87
|
subscribe = tk.Checkbutton(root, text="Subscribe", font=font, variable=subscribed, command=on_subscribe,
|
|
100
88
|
bg="#f0f0f0")
|
|
101
|
-
subscribe
|
|
89
|
+
_place(subscribe, "subscribe")
|
|
102
90
|
|
|
103
91
|
notes = tk.Text(root, font=("DejaVu Sans", 14), width=28, height=3)
|
|
104
|
-
notes
|
|
92
|
+
_place(notes, "notes")
|
|
105
93
|
|
|
106
94
|
def note(event) -> None:
|
|
107
95
|
state["notes"] = notes.get("1.0", "end-1c")
|
|
@@ -110,7 +98,7 @@ def main(state_dir: Path) -> None:
|
|
|
110
98
|
notes.bind("<KeyRelease>", note)
|
|
111
99
|
|
|
112
100
|
frame = tk.Frame(root)
|
|
113
|
-
frame
|
|
101
|
+
_place(frame, "list", **SIZE["list"])
|
|
114
102
|
listbox = tk.Listbox(frame, font=("DejaVu Sans", 14), exportselection=False)
|
|
115
103
|
listbox.pack(side="left", fill="both", expand=True)
|
|
116
104
|
bar = tk.Scrollbar(frame, command=listbox.yview)
|
|
@@ -122,7 +110,7 @@ def main(state_dir: Path) -> None:
|
|
|
122
110
|
save()
|
|
123
111
|
|
|
124
112
|
listbox.configure(yscrollcommand=scrolled)
|
|
125
|
-
for i in range(1,
|
|
113
|
+
for i in range(1, ITEMS + 1):
|
|
126
114
|
listbox.insert("end", f"Item {i}")
|
|
127
115
|
|
|
128
116
|
def select(event) -> None:
|
|
@@ -133,7 +121,7 @@ def main(state_dir: Path) -> None:
|
|
|
133
121
|
listbox.bind("<<ListboxSelect>>", select)
|
|
134
122
|
|
|
135
123
|
grid = tk.Canvas(root, width=GRID * CELL, height=GRID * CELL, bg="#ffffff", highlightthickness=0)
|
|
136
|
-
grid
|
|
124
|
+
_place(grid, "grid")
|
|
137
125
|
for row in range(GRID):
|
|
138
126
|
for col in range(GRID):
|
|
139
127
|
grid.create_rectangle(col * CELL, row * CELL, (col + 1) * CELL, (row + 1) * CELL, fill=COLOURS[0],
|
|
@@ -155,8 +143,8 @@ def main(state_dir: Path) -> None:
|
|
|
155
143
|
grid.bind("<Button-3>", lambda e: cell(e, True))
|
|
156
144
|
grid.bind("<Double-Button-1>", double)
|
|
157
145
|
|
|
158
|
-
pad = tk.Canvas(root,
|
|
159
|
-
pad
|
|
146
|
+
pad = tk.Canvas(root, **SIZE["pad"], bg="#fffbe6", highlightthickness=0)
|
|
147
|
+
_place(pad, "pad")
|
|
160
148
|
stroke: list = []
|
|
161
149
|
|
|
162
150
|
def press(event) -> None:
|
|
@@ -170,8 +158,7 @@ def main(state_dir: Path) -> None:
|
|
|
170
158
|
def release(event) -> None:
|
|
171
159
|
if stroke:
|
|
172
160
|
stroke.append((event.x, event.y))
|
|
173
|
-
|
|
174
|
-
"path": stroke[::max(1, len(stroke) // 50)] + stroke[-1:]}])[-20:]
|
|
161
|
+
record_drag(state, stroke)
|
|
175
162
|
save()
|
|
176
163
|
stroke.clear()
|
|
177
164
|
|
|
@@ -179,8 +166,8 @@ def main(state_dir: Path) -> None:
|
|
|
179
166
|
pad.bind("<B1-Motion>", motion)
|
|
180
167
|
pad.bind("<ButtonRelease-1>", release)
|
|
181
168
|
|
|
182
|
-
wheel = tk.Canvas(root,
|
|
183
|
-
wheel
|
|
169
|
+
wheel = tk.Canvas(root, **SIZE["wheel"], bg="#e6f0ff", highlightthickness=0)
|
|
170
|
+
_place(wheel, "wheel")
|
|
184
171
|
|
|
185
172
|
def wheeled(horizontal: bool, steps: int) -> None:
|
|
186
173
|
way = ("left" if horizontal else "up") if steps > 0 else ("right" if horizontal else "down")
|
|
@@ -202,8 +189,8 @@ def main(state_dir: Path) -> None:
|
|
|
202
189
|
if tk.TkVersion >= 8.7:
|
|
203
190
|
wheel.bind("<TouchpadScroll>", lambda e: scrolled(e.delta))
|
|
204
191
|
|
|
205
|
-
field = tk.Canvas(root,
|
|
206
|
-
field
|
|
192
|
+
field = tk.Canvas(root, **SIZE["field"], bg="#000000", highlightthickness=0, bd=0)
|
|
193
|
+
_place(field, "field")
|
|
207
194
|
for x, y, colour in MARKERS:
|
|
208
195
|
field.create_rectangle(x, y, x + 1, y + 1, fill=colour, outline="", width=0)
|
|
209
196
|
|
|
@@ -212,28 +199,29 @@ def main(state_dir: Path) -> None:
|
|
|
212
199
|
if message.exists() and (text := message.read_text()) != state["loaded"]:
|
|
213
200
|
state["loaded"] = text
|
|
214
201
|
save()
|
|
215
|
-
root.after(
|
|
202
|
+
root.after(INBOX_EVERY_MS, poll_inbox)
|
|
216
203
|
|
|
217
|
-
def
|
|
204
|
+
def layout() -> None:
|
|
218
205
|
root.update_idletasks()
|
|
219
206
|
boxes = {key: [w.winfo_rootx(), w.winfo_rooty(), w.winfo_width(), w.winfo_height()]
|
|
220
207
|
for key, w in (("name", name), ("save", button), ("subscribe", subscribe), ("notes", notes),
|
|
221
208
|
("list", listbox), ("grid", grid), ("pad", pad), ("wheel", wheel), ("field", field))}
|
|
222
|
-
boxes["list_row_height"] = listbox.bbox(0)[3] if listbox.bbox(0) else 0
|
|
223
|
-
boxes["markers"] = [[field.winfo_rootx() + x, field.winfo_rooty() + y, colour] for x, y, colour in MARKERS]
|
|
224
|
-
boxes["cell"] = CELL
|
|
225
|
-
boxes["clipboard"] = CLIPBOARD
|
|
226
209
|
save()
|
|
227
|
-
(state_dir
|
|
210
|
+
write_layout(state_dir, boxes, listbox.bbox(0)[3] if listbox.bbox(0) else 0,
|
|
211
|
+
(field.winfo_rootx(), field.winfo_rooty()))
|
|
228
212
|
|
|
229
213
|
root.clipboard_clear()
|
|
230
214
|
root.clipboard_append(CLIPBOARD)
|
|
231
215
|
state["clipboard"] = CLIPBOARD
|
|
232
|
-
root.after(300,
|
|
216
|
+
root.after(300, layout)
|
|
233
217
|
root.after(500, poll_inbox)
|
|
234
218
|
root.mainloop()
|
|
235
219
|
|
|
236
220
|
|
|
221
|
+
def _place(widget: tk.Widget, at: str, **size: int) -> None:
|
|
222
|
+
widget.place(x=AT[at][0], y=AT[at][1], **size)
|
|
223
|
+
|
|
224
|
+
|
|
237
225
|
def keep_on_top(root: tk.Tk) -> None:
|
|
238
226
|
"""Keep the fixture over the windows an app opens, which would take the probe's clicks: maximized and above other
|
|
239
227
|
windows rather than full screen, since xfwm4 keeps a window an app puts above over a full-screen one, and raised
|