Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ unavoidable and the outcome needs proof.

## Try it locally

OpenAdapt requires Python 3.10–3.12. The base package includes the compiler and
runtime:
OpenAdapt requires Python 3.10–3.12. Install the browser capability for the
bundled tutorial:

```bash
python -m pip install --upgrade openadapt
python -m pip install --upgrade 'openadapt[browser]'
```

Run the complete bundled tutorial with one command. It needs no account,
Expand Down Expand Up @@ -68,14 +68,18 @@ effect verifiers, fault cases, and deployment policy. Continue with the

## Record your workflow

The browser path is available in the base installation:
The browser path is an explicit capability, so native and remote-only installs
do not carry the Playwright driver:

```bash
openadapt flow record --backend web --url https://your-app.example --out rec
openadapt flow compile rec --out bundle --name my-workflow
openadapt flow replay bundle --url https://your-app.example --run-dir run
```

The first browser action downloads its matching Chromium build once. A native
desktop, RDP, or Citrix workflow never downloads or imports it.

Install only the capabilities needed for native or remote work:

```bash
Expand Down
40 changes: 39 additions & 1 deletion openadapt/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,13 @@ def version():


@main.command()
def doctor():
@click.option(
"--backend",
type=click.Choice(["web", "windows", "macos", "linux", "rdp", "citrix"]),
default=None,
help="Explain setup for one execution surface.",
)
def doctor(backend: str | None):
"""Check system requirements and dependencies."""
click.echo("OpenAdapt System Check")
click.echo("=" * 40)
Expand All @@ -874,6 +880,37 @@ def doctor():

from importlib.util import find_spec

click.echo("\nSelected execution surface:")
if backend and backend != "web":
click.echo(
f" [OK] {backend}: browser support is not required; "
"no Playwright or Chromium setup will run. The selected "
"native or remote driver is checked when that surface opens."
)
else:
playwright = find_spec("playwright") is not None
if not playwright:
prefix = "[SETUP]" if backend == "web" else "[--]"
click.echo(
f" {prefix} Browser: optional and not installed. Run "
"`python -m pip install 'openadapt[browser]'` before a web "
"recording or replay."
)
else:
try:
from openadapt_flow._browser_setup import _chromium_present

chromium = _chromium_present()
except Exception:
chromium = False
if chromium:
click.echo(" [OK] Browser: Playwright and Chromium are ready.")
else:
click.echo(
" [READY] Browser: Playwright is installed; the matching "
"Chromium downloads automatically on the first web action."
)

# Core packages: installed by the base `pip install openadapt`. Only
# these are treated as required; a missing one is a real problem.
click.echo("\nCore packages (installed with `pip install openadapt`):")
Expand All @@ -897,6 +934,7 @@ def doctor():
# install it rather than flagging it. Maps import name -> extra name.
click.echo("\nOptional packages (install with `pip install openadapt[...]`):")
optional = [
("playwright", "browser"),
("openadapt_capture", "capture"),
("openadapt_ml", "ml"),
("openadapt_evals", "evals"),
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ dependencies = [

[project.optional-dependencies]
# Individual packages
browser = [
# Web recording/replay only. The base launcher stays lightweight for
# native desktop, RDP, and Citrix users; Chromium remains lazy on first use.
"playwright>=1.44",
]
# Local human desktop recording. Flow owns the supported capture adapter
# contract; the direct floor prevents an already-installed pre-1.0 Capture
# from satisfying the launcher's public recording path. FFmpeg remains a
Expand Down Expand Up @@ -87,7 +92,7 @@ all = [
# Cross-platform clients are always safe to resolve. Native bindings remain
# conditional so `openadapt[all]` never pulls PyObjC onto Linux/Windows or
# PyGObject onto macOS/Windows.
"openadapt[core,grounding,retrieval,privacy,flow,windows,rdp]",
"openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp]",
"openadapt[macos]; sys_platform == 'darwin'",
"openadapt[linux]; sys_platform == 'linux'",
]
Expand Down
11 changes: 10 additions & 1 deletion tests/test_cli_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ def test_launcher_flow_and_substrate_extras_metadata():

assert metadata["dependencies"].count("openadapt-flow[hosted]>=1.20.1,<2.0.0") == 1
assert extras["flow"] == ["openadapt-flow>=1.20.1,<2.0.0"]
assert extras["browser"] == ["playwright>=1.44"]
assert extras["privacy"] == ["openadapt-flow[privacy]>=1.20.1,<2.0.0"]
assert extras["capture"] == [
"openadapt-capture>=1.0.4,<2.0.0",
Expand All @@ -223,12 +224,20 @@ def test_launcher_flow_and_substrate_extras_metadata():
]
assert extras["rdp"] == ["openadapt-flow[rdp]>=1.20.1,<2.0.0"]
assert extras["all"] == [
"openadapt[core,grounding,retrieval,privacy,flow,windows,rdp]",
"openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp]",
"openadapt[macos]; sys_platform == 'darwin'",
"openadapt[linux]; sys_platform == 'linux'",
]


def test_doctor_does_not_require_browser_for_citrix(monkeypatch):
monkeypatch.setattr("importlib.util.find_spec", lambda _name: None)
result = CliRunner().invoke(cli_main, ["doctor", "--backend", "citrix"])
assert result.exit_code == 0, result.output
assert "browser support is not required" in result.output
assert "no Playwright or Chromium setup will run" in result.output


def test_top_level_help_leads_with_flow():
"""`openadapt --help` must list flow before the other commands."""
runner = CliRunner()
Expand Down
7 changes: 6 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.