-
Notifications
You must be signed in to change notification settings - Fork 0
Local desktop optimization #191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
2e2252a
Harden local control: macOS permission preflight, 429 backoff, CLI ou…
abonneth a01caa3
Surface a stop during channel setup as a startup failure
abonneth c8705a8
Address review: cap setup backoff under ready timeout, move platform …
abonneth b3ae4e0
Cancel local-bridge sessions at interpreter exit
abonneth 6d69250
Lift holo-desktop hardening: kill switch, hai doctor, runaway budgets…
abonneth 8a430e9
Cap desktop screenshot width at 1920 by default; fix kill-switch test…
abonneth 6fae966
Arm the kill-switch watcher before bridge startup
abonneth 31f90bc
Compose ScaledDesktopDriver in the desktop bridge
abonneth 57b95d3
feat(local): scale and encode desktop screenshots at the source
abonneth 1b78c5a
Match the screenshot_b64 driver command surface
abonneth fb13e7e
Guard the scaled driver import with the install hint
abonneth 8d82af5
Pin hai-drivers 0.1.2 from PyPI
abonneth 5ef3518
Merge remote-tracking branch 'origin/main' into antoine/desktop-bridg…
abonneth 022fda3
Merge remote-tracking branch 'origin/antoine/desktop-bridge-scaling' …
abonneth d36a133
Refresh uv.lock after merging desktop-bridge-scaling
abonneth b65abcb
Stub the local driver module in tests so they run on headless CI
abonneth ddce165
Prompt for macOS permissions on the caller's thread before bridge sta…
abonneth 4b35293
Back off on rate limits during channel recreation; surface a stopped …
abonneth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| """`hai doctor`: read-only diagnostics for login, platform access, and local control, with fix-its.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import importlib.util | ||
| import socket | ||
| import sys | ||
| from dataclasses import dataclass | ||
|
|
||
| from hai_agents_common import credentials | ||
|
|
||
| DEFAULT_CHROME_DEBUG_PORT = 9222 | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class CheckResult: | ||
| name: str | ||
| ok: bool | ||
| detail: str | ||
| fix: str | None = None | ||
|
|
||
|
|
||
| def check_login(api_key: str | None) -> CheckResult: | ||
| if credentials.current_api_key(api_key): | ||
| return CheckResult("login", True, f"API key found ({credentials.source(api_key)})") | ||
| return CheckResult( | ||
| "login", | ||
| False, | ||
| "no API key configured", | ||
| fix=f"run `hai login`, set {credentials.API_KEY_VAR}, or pass --api-key", | ||
| ) | ||
|
|
||
|
|
||
| def check_platform(api_key: str | None, base_url: str | None) -> CheckResult: | ||
| endpoint = credentials.resolve_base_url(base_url) or "(SDK default)" | ||
| try: | ||
| client = credentials.make_client(api_key=api_key, base_url=base_url) | ||
| client.agents.list_agents(page=1, size=1) | ||
| except Exception as exc: | ||
| return CheckResult( | ||
| "platform", | ||
| False, | ||
| f"cannot query {endpoint}: {exc}", | ||
| fix="check the key and network; `hai whoami` shows the resolved endpoint", | ||
| ) | ||
| return CheckResult("platform", True, f"authenticated against {endpoint}") | ||
|
|
||
|
|
||
| def check_browser() -> CheckResult: | ||
| if importlib.util.find_spec("hai_drivers") is None or importlib.util.find_spec("hai_drivers.web") is None: | ||
| return CheckResult( | ||
| "browser", True, "local browser control not installed; add it with: pip install 'hai-agents[browser]'" | ||
| ) | ||
| if _port_in_use(DEFAULT_CHROME_DEBUG_PORT): | ||
| detail = f"deps installed; a debuggable Chrome is listening on port {DEFAULT_CHROME_DEBUG_PORT} (will attach)" | ||
| else: | ||
| detail = f"deps installed; port {DEFAULT_CHROME_DEBUG_PORT} is free (a Chrome will be launched on demand)" | ||
| return CheckResult("browser", True, detail) | ||
|
|
||
|
|
||
| def check_desktop() -> CheckResult: | ||
| if importlib.util.find_spec("hai_drivers") is None or importlib.util.find_spec("hai_drivers.desktop") is None: | ||
| return CheckResult( | ||
| "desktop", True, "local desktop control not installed; add it with: pip install 'hai-agents[desktop]'" | ||
| ) | ||
| if sys.platform != "darwin": | ||
| return CheckResult("desktop", True, "deps installed") | ||
| missing = _missing_macos_grants() | ||
| if missing: | ||
| from hai_agents_local.desktop import ACCESSIBILITY_SETTINGS_URL, SCREEN_RECORDING_SETTINGS_URL | ||
|
|
||
| return CheckResult( | ||
| "desktop", | ||
| False, | ||
| f"deps installed, but this terminal is missing macOS grants: {', '.join(missing)}", | ||
| fix="grant them in System Settings -> Privacy & Security, then restart this terminal: " | ||
| f"{ACCESSIBILITY_SETTINGS_URL} and {SCREEN_RECORDING_SETTINGS_URL}", | ||
| ) | ||
| return CheckResult("desktop", True, "deps installed; Accessibility and Screen Recording granted") | ||
|
|
||
|
|
||
| def _missing_macos_grants() -> list[str]: | ||
| # Non-prompting variants keep the doctor read-only; the bridge preflight does the prompting. | ||
| from ApplicationServices import AXIsProcessTrusted | ||
| from Quartz import CGPreflightScreenCaptureAccess | ||
|
|
||
| missing = [] | ||
| if not AXIsProcessTrusted(): | ||
| missing.append("Accessibility") | ||
| if not CGPreflightScreenCaptureAccess(): | ||
| missing.append("Screen Recording") | ||
| return missing | ||
|
|
||
|
|
||
| def _port_in_use(port: int) -> bool: | ||
| with socket.socket() as sock: | ||
| sock.settimeout(0.2) | ||
| return sock.connect_ex(("127.0.0.1", port)) == 0 | ||
|
|
||
|
|
||
| def run_checks(api_key: str | None, base_url: str | None) -> list[CheckResult]: | ||
| login = check_login(api_key) | ||
| checks = [login] | ||
| if login.ok: | ||
| checks.append(check_platform(api_key, base_url)) | ||
| checks.extend([check_browser(), check_desktop()]) | ||
| return checks |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.