Skip to main content

openadapt-tray

PyPI version Python 3.10+ License: MIT

A menu-bar icon that shows what OpenAdapt is doing and gives you a start/stop button for it. It records nothing, compiles nothing, and replays nothing. It mirrors state from the desktop app over an authenticated loopback socket and reads one number from the hosted control plane.

Which means the honest description is: this is a status surface, not an integrated desktop product. The workflow logic all lives in openadapt-flow.

Documentation · openadapt-desktop · OpenAdapt launcher

Install

pip install openadapt-tray
openadapt-tray            # openadapt-tray-gui is the same entry point,
                          # registered under gui_scripts so Windows
                          # launches it without a console window

Needs a graphical session. With no desktop IPC server and no hosted token it will sit there offline, which is correct behaviour rather than a failure.

The menu

Built from whatever local and hosted state currently exists:

Start Recording (<configured hotkey>)
Recent Captures
<N automations need attention>    # only when count > 0
Open Desktop App
Open Cloud Dashboard
Account: connected · <N> days left
Pause Sync / Sync (offline)
Settings...
Quit

During a local operation the recording item becomes Starting, Stop Recording, Stopping, or Compiling. The account row turns into a sign-in action, an expiry warning, or an unavailable status, and clicking it opens the credential settings page. These labels follow events; the tray isn't doing the work.

The icon is always the OpenAdapt mark, tinted by state so you can read it at a glance: brand blue idle, amber starting or stopping, red while recording, purple while compiling, red on error. One high-resolution transparent master gets tinted, so the states can't drift into different-looking marks.

Recording and sync are two independent channels. A machine can compile a fresh recording while a previous push is still syncing, or sit idle and offline.

How it talks to things

openadapt-tray (status mirror + launcher)
    |                                   \
    | authenticated loopback IPC         \ HTTPS: GET needs-attention count
    v                                     v
openadapt-desktop (local cockpit)     hosted control plane (app.openadapt.ai)
    |
    v
openadapt-flow (compile / replay / halt / repair / teach)

Local control goes to openadapt-desktop. The desktop app writes a discovery file at ~/.openadapt/desktop_ipc.json, the tray reads it and opens an authenticated loopback connection, then sends start/stop, open-library, open-teach, and pause/resume-sync, and consumes status events coming back. If discovery fails, the tray launches openadapt-desktop and waits about ten seconds.

Hosted state is one narrow read:

GET <hosted_url>/api/needs-attention/count
Authorization: Bearer <ingest token>
→ { "count": 0, "credential": {
     "expires_at": "2026-08-05T12:00:00Z",
     "expires_in_days": 8,
     "expiring_soon": true,
     "legacy_non_expiring": false,
     "warning_days": 14
   } }

The token comes from OPENADAPT_INGEST_TOKEN or the OS keychain, never from tray.json, and it's never stored or logged. Polling defaults to 60 seconds, clamps to a 30-second floor, and backs off when offline. The control plane decides when a credential enters its 14-day warning window; the tray shows one actionable notification per credential and expiry, surviving a tray restart, and keeps only a non-secret identity digest to deduplicate.

No screenshots, bundles, or capture artifacts go through this poller.

Clicking the needs-attention row routes by lane. On cloud it opens <hosted_url>/dashboard. On byoc with desktop IPC connected it sends open_teach locally, so workflow data stays inside the customer environment. On byoc with no desktop IPC it currently falls back to the hosted dashboard, and a regulated deployment should not treat that fallback as a validated PHI-safe path. Gate it or change it before production.

What this doesn't do yet

The tray's client behaviour is covered by unit tests: the state machine, the IPC framing, the menus, the icon tinting, and mocked hosted HTTP. None of that proves a working installer, a live hosted service, or an authoring loop that runs end to end.

  • openadapt-desktop's main serves the exact discovery-socket and command contract this tray expects, but the two have not been validated together end to end, and no signed generally-available desktop build ships that server.
  • No packaged installer proves tray startup, permissions, or auto-start on macOS, Windows, and Linux.
  • Hosted polling is tested against mocks. Nothing here validates a live service contract.
  • Recent-capture View still calls a legacy launcher command before falling back to a file browser.
  • Sign-in opens a settings page. There's no interactive authentication.
  • The tray doesn't certify a workflow or verify its effects. Nothing here is a safety control.

Configuration

Non-secret settings live at ~/Library/Application Support/openadapt/tray.json on macOS, %APPDATA%/openadapt/tray.json on Windows, and ${XDG_CONFIG_HOME:-~/.config}/openadapt/tray.json on Linux:

{
  "hotkeys": {
    "toggle_recording": "<ctrl>+<shift>+r",
    "open_dashboard": "<ctrl>+<shift>+d",
    "stop_recording": "<ctrl>+<ctrl>+<ctrl>"
  },
  "captures_directory": "~/openadapt/captures",
  "desktop_ipc_port": null,
  "hosted_url": "https://app.openadapt.ai",
  "deployment_lane": "cloud",
  "poll_interval_s": 60,
  "show_notifications": true,
  "auto_start_on_login": false
}

deployment_lane takes cloud or byoc. The triple-ctrl stop hotkey is real, not a typo: the stop_on_triple_ctrl setting defaults to true and is what enables it. The ingest token does not belong in this file.

Development

git clone https://github.com/OpenAdaptAI/openadapt-tray.git
cd openadapt-tray
uv sync --extra dev
uv run pytest tests -q
uv run openadapt-tray
src/openadapt_tray/
  app.py            tray lifecycle, desktop delegation, and routing
  ipc.py            authenticated loopback IPC client
  hosted.py         needs-attention poller and lane routing
  state.py          recording, sync, and badge state
  menu.py           state-dependent tray menu
  icons.py          per-state OpenAdapt-mark tinting
  keychain.py       ingest-token lookup
  config.py         non-secret local preferences

For an OpenAdapt workflow you can actually run, use the launcher instead:

pip install 'openadapt[browser]'
openadapt quickstart

License

MIT

Metadata

Release files for openadapt-tray 0.3.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openadapt-tray 0.3.4
File Size Uploaded
openadapt_tray-0.3.4.tar.gz 287.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openadapt-tray 0.3.4
File Interpreter ABI Platform
openadapt_tray-0.3.4-py3-none-any.whl Python 3 none any Details

Total release size: 353.4 kB

Release files / openadapt_tray-0.3.4.tar.gz

Download URL openadapt_tray-0.3.4.tar.gz
Size 287.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2f8d1b7b2d2482026e744935f69147c8f8002dc2398a6ddb925ca2fd2f4a905c
BLAKE2b-256 checksum
How to use checksums
87964990ec29769583d4829b9e7ef9518ad0dffcdda50cad9636a55bb05d483f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.

Transparency log

Release files / openadapt_tray-0.3.4-py3-none-any.whl

Download URL openadapt_tray-0.3.4-py3-none-any.whl
Size 65.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
804f24ff2377b2f9b186a5f41985db9f317185b7672ffb4bc2052c96f6eb3c90
BLAKE2b-256 checksum
How to use checksums
8b0e36b32dc35c1bda3c25deb223625d85752930b2ace8794a731b36e99437da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.4 This release

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page