Minimal installable wheel that registers a custom ControlProvider via the
vdisplay.control_providers entry-point group. Use it as a template for third-party
adapters without editing vdisplay core.
examples/control-plugin/
├── README.md
├── pyproject.toml
└── src/vdisplay_example_plugin/
├── __init__.py # register_plugin() entry point
└── my_provider.py # EchoControlProvider + ProviderDescriptor
Subclass vdisplay.control.base.ControlProvider and implement:
| Method | Purpose |
|---|---|
available() |
Readiness probe (ok, reason) |
snapshot() |
Build ControlSnapshot tree |
find(selector) |
Match ControlSelector → ControlNode list |
invoke() / focus() / set_value() |
Actions |
bounds() |
Pixel bounds for an element |
See my_provider.py — EchoControlProvider
returns a synthetic demo-button node for CI and integration tests.
Metadata drives routing scores, capability contracts, and diagnose control output.
Do not add a new top-level provider per browser vendor — use ApplicationProfile
(see PR-16 browser_firefox / browser_chromium).
ECHO_DESCRIPTOR = ProviderDescriptor(
provider_id="echo",
adapter_kind="example_echo",
environments=frozenset({"desktop"}),
session_kind=None, # or SessionKind.BROWSER / TERMINAL
capabilities=ECHO_CAPABILITIES,
base_score=35,
aliases=frozenset({"example-echo"}),
)pyproject.toml:
[project.entry-points."vdisplay.control_providers"]
echo = "vdisplay_example_plugin:register_plugin"register_plugin() calls register_control_provider(descriptor, factory, source="entrypoint").
vdisplay loads entry points on first get_provider_registry() call.
from vdisplay.control.plugins import register_control_provider
from vdisplay_example_plugin import ECHO_DESCRIPTOR, EchoControlProvider
register_control_provider(
ECHO_DESCRIPTOR,
lambda **kwargs: EchoControlProvider(**kwargs),
source="manual",
)Core builtins live in src/vdisplay/control/providers/ and
BUILTIN_PROVIDER_DESCRIPTORS — avoid for third-party code.
Profiles describe app class, not execution adapter. Add to
BUILTIN_APPLICATION_PROFILES only when upstreaming a generic class (e.g. web_spa).
Third-party profiles can be inferred via plugin-specific selector fields or
register_control_provider + custom scoring in a future PR.
For browser targets, use engine profiles (browser_chromium, browser_firefox) —
the browser provider stays the executor.
cd ~/github/wronai/vdisplay
source venv/bin/activate
pip install -e ".[dev]"
pip install -e examples/control-plugin
# Plugin visible in diagnostics
vdisplay diagnose control | jq '.extensions.plugins[] | select(.provider_id=="echo")'
# Agent broker
vdisplay agent serve &
curl -s http://127.0.0.1:8765/control/plugins | jq '.data.plugins[] | select(.provider_id=="echo")'Main suite includes tests/test_example_control_plugin.py (manual registration)
and contract tests ensuring builtin provider count stays 5 when no plugin is loaded.
pytest tests/test_example_control_plugin.py tests/test_control_plugins.py -q
pytest tests/contract/test_providers.py -q # builtin count unchanged-
ProviderDescriptor.provider_idis unique and lowercase -
capabilitiesmatch whatinvoke/findactually support -
session_kindset if provider requiresbrowser_open/terminal_open -
base_scorelower than primary builtins unless you intend to win auto-routing - Entry point or
register_control_provider— never patchscoring.py - Tests:
available(),find(), routing eligibility, unregister cleanup
- RFC:
docs/rfc/001-extensibility-model.md - Plugin API:
src/vdisplay/control/plugins.py - Contract tests:
tests/contract/test_providers.py - PR-12 tests:
tests/test_control_plugins.py - PR-23 platform wheels:
control-plugin-uia/,control-plugin-ax/