Skip to content

feat(cowork): add Cowork FastAPI server as packaged module#156

Closed
pnewsam wants to merge 1 commit into
mainfrom
feat/cowork-server-package
Closed

feat(cowork): add Cowork FastAPI server as packaged module#156
pnewsam wants to merge 1 commit into
mainfrom
feat/cowork-server-package

Conversation

@pnewsam
Copy link
Copy Markdown
Contributor

@pnewsam pnewsam commented May 11, 2026

Note

This PR enables OTA/hot updates for Cowork server code by moving it into anton. It is a tricky situation where cowork server code is strongly coupled to both anton and cowork. Of the available options of a) keeping it in cowork, b) moving it to anton, and c) creating a new repo, this opts for option b. Please see the Cowork Server Packaging: Rationale and Approach comment, for further context.

Description

Move the Cowork FastAPI server into the Anton package as anton.cowork.server, launchable via:

python -m anton.cowork.server

Previously, the server source lived in cowork/server/ and was bundled as raw .py files inside the Electron app's extraResources. The only way to deliver server changes to users was a full desktop app release. This migration puts the server on Anton's existing release/self-update flow so Python changes ship through uv tool install updates — no Electron reinstall needed.

Companion PR (Cowork side): mindsdb/cowork#69

What's added

New package: anton/cowork/server/

  • __init__.py — exports COWORK_SERVER_VERSION and COWORK_SERVER_PROTOCOL_VERSION
  • __main__.py — module entrypoint for python -m anton.cowork.server
  • main.py — FastAPI app, /health endpoint (with protocol version), self-update + re-exec, SPA serving, uvicorn boot
  • anton_api/ — conversation manager, projects store, scratchpad runtime, cowork tools, data vault agent/probe, models, formatter
  • routes/ — all /v1/* API routes (artifacts, attachments, browse, connectors, conversations, cowork_state, datavault, integrations, pins, projects, responses, schedules, scratchpad, search, settings, utilities)
  • connectors/*.json — 150+ predefined connector definitions (shipped as package data)

pyproject.toml changes

  • New optional extra: cowork-server (fastapi, uvicorn, python-multipart)
  • New console script: anton-cowork-server
  • Explicit [tool.hatch.build.targets.wheel] packages = ["anton"]

anton/updater.py changes

  • check_and_update() now accepts an extras parameter
  • When called from the Cowork server context, self-update installs anton[cowork-server] instead of bare anton, preserving FastAPI/uvicorn deps across updates

Import migration

  • All bare imports (from anton_api ..., from routes ...) converted to relative imports (from .anton_api ..., from .routes ...)

Architecture

anton/
  anton/
    core/...          # Anton core (unchanged)
    cli.py            # CLI interface (unchanged)
    cowork/
      __init__.py     # "Cowork-specific interfaces for Anton"
      server/         # ← NEW: Cowork FastAPI server
        __init__.py
        __main__.py
        main.py
        anton_api/
        routes/
        connectors/

Cowork-specific concepts (projects, pins, attachments, artifact gallery, connector forms, scheduled tasks) remain isolated in anton.cowork.server — they are NOT promoted into anton.core. The server is an alternate interface to Anton, parallel to the CLI.

Alternatives considered

  1. Server OTA in cowork — rejected; adds a second executable-code updater with staging, rollback, and dependency compatibility complexity
  2. Separate anton-cowork-server repo/package — rejected; increases repo sprawl, the server is too coupled to Anton internals for an independent release line
  3. Move Cowork concepts into anton.core — rejected; would pollute core with desktop-specific models

Type of change

  • ⚡ New feature (non-breaking change which adds functionality)

Testing / Verification

Included tests (tests/test_cowork_server_package.py):

  • test_cowork_server_modules_import — walks every module under anton.cowork.server and verifies they all import cleanly
  • test_cowork_server_health_versions_and_protocol — calls the /health endpoint function and asserts version + protocol fields
  • test_cowork_connector_registry_loads_package_data — verifies 150+ connector JSONs load from the installed package

Steps to verify manually:

  1. pip install -e ".[cowork-server]" (or uv pip install -e ".[cowork-server]")
  2. python -m anton.cowork.server — server should boot on 127.0.0.1:26866
  3. curl http://127.0.0.1:26866/health — should return cowork_server_protocol_version: 1
  4. pytest tests/test_cowork_server_package.py -v
  5. Build wheel: hatch build — verify connector JSONs are included in the wheel

Checklist:

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have checked my code and corrected any misspellings
  • I have added or updated tests that cover my changes

Move the Cowork FastAPI server from cowork/server/ into
`anton/cowork/server/`, making it launchable as:

  python -m anton.cowork.server

This lets the desktop app receive server updates through Anton's
existing release/self-update flow instead of requiring a new Electron
app install for every Python change.

Package structure:
- anton/cowork/__init__.py — namespace package for Cowork interfaces
- anton/cowork/server/__init__.py — exports COWORK_SERVER_VERSION and
  COWORK_SERVER_PROTOCOL_VERSION
- anton/cowork/server/__main__.py — module entrypoint
- anton/cowork/server/main.py — FastAPI app, /health, self-update,
  SPA serving, uvicorn boot
- anton/cowork/server/anton_api/ — conversation manager, projects
  store, scratchpad runtime, cowork tools, data vault, models
- anton/cowork/server/routes/ — all /v1/* API routes
- anton/cowork/server/connectors/*.json — 150+ predefined connector
  definitions (shipped as package data)

Other changes:
- pyproject.toml: add `cowork-server` optional extra (fastapi,
  uvicorn, python-multipart), `anton-cowork-server` console script,
  explicit wheel packages list
- anton/updater.py: accept `extras` parameter so the Cowork server
  can self-update with `anton[cowork-server]` instead of bare `anton`
- All bare imports (from anton_api/routes) converted to relative
  imports (from .anton_api/from .routes)
- tests/test_cowork_server_package.py: import smoke test, /health
  contract test, connector registry package-data test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@pnewsam pnewsam marked this pull request as ready for review May 11, 2026 21:27
@pnewsam
Copy link
Copy Markdown
Contributor Author

pnewsam commented May 11, 2026

Cowork Server Packaging: Rationale and Approach

Objective

Give the Python server that powers Cowork a real version and update path without creating another repository or another updater.

The chosen approach is to package the Cowork FastAPI server inside the existing anton Python package as anton.cowork.server, with server-only dependencies declared behind the optional cowork-server extra.

Current System

Cowork has three runtime pieces:

  1. Electron main process: owns installation, diagnostics, process lifecycle, and starts the backend on 127.0.0.1:26866.
  2. React renderer: runs inside Electron and talks to the local backend over HTTP/SSE.
  3. Python FastAPI server: exposes the API used by the renderer and adapts Anton's Python runtime to Cowork UI workflows.

Before this migration, the server code lived in cowork/server/. The Electron app bundled those files into the .app and launched server/main.py using the Python interpreter from Anton's uv tool environment, typically ~/.local/share/uv/tools/anton/bin/python.

That created a split install model:

  • Anton and Python dependencies were installed by Cowork with uv tool install.
  • The server source files were static resources inside the Electron app bundle.

Anton already had a lightweight OTA path: check the latest GitHub release, compare it to anton.__version__, run uv tool install ... --force, and re-exec. But that only updated the installed Anton package. It could not update bundled server files such as routes, anton_api wrappers, or connector JSON definitions. Any server fix required a new Electron build, including signing, notarization, packaging, and user installation.

New Startup and Update Flow

After the migration, the server ships as part of Anton:

anton/
  cowork/
    server/
      __main__.py
      main.py
      anton_api/
      routes/
      connectors/*.json

Cowork installs Anton with the server extra:

uv tool install "anton[cowork-server] @ git+https://github.com/mindsdb/anton.git" \
  --force --reinstall --upgrade

The extra declares the FastAPI server dependencies in Anton's pyproject.toml, currently FastAPI, uvicorn, and python-multipart. The installer verifies that both the runtime dependencies and anton.cowork.server.main import from the same tool interpreter Electron will use.

Electron then starts:

python -m anton.cowork.server

On boot, the server runs Anton's self-update check before uvicorn starts. In Cowork server context it calls check_and_update(..., extras=["cowork-server"]), so runtime self-updates preserve the FastAPI/uvicorn dependencies. If an update is installed, the server re-execs explicitly in module mode:

os.execv(sys.executable, [sys.executable, "-m", "anton.cowork.server"])

Electron waits for /health and only accepts the backend if it reports a compatible cowork_server_protocol_version. During the migration window, Electron still keeps the old bundled cowork/server as a fallback if the packaged server cannot import, start, or satisfy the protocol check.

Version Selection and Compatibility

There are two version decisions:

  1. Initial install or repair install

    • Cowork installs Anton into the uv tool environment using the Git spec from getAntonGitSpec().
    • Today, without an explicit ref, that resolves to the repository default branch at install time.
    • If stricter reproducibility becomes important, the installer should pass a release tag, branch, or channel-specific ref.
  2. Runtime self-update

    • The server asks GitHub for the latest mindsdb/anton release.
    • It compares that release tag to the installed anton.__version__.
    • If the release is newer, it installs anton[cowork-server] from that tag and verifies the installed version before re-exec.

For v1, cowork_server_version is the same as anton.__version__. Electron does not require exact version equality. Instead, /health reports:

{
  "anton_version": "...",
  "cowork_server_version": "...",
  "cowork_server_protocol_version": 1
}

Electron has a required minimum protocol constant and refuses to treat the backend as ready if the reported protocol is too old. This lets server fixes ship through Anton without rebuilding Cowork, while still giving Electron a hard stop for breaking API changes.

Filesystem and Data Ownership

Moving the package does not move user data:

  • ANTON_PROJECTS_DIR still controls the Cowork desktop project directory. In Electron production, this points at the app user-data path, such as ~/Library/Application Support/Anton/projects on macOS.
  • Anton global settings, model configuration, memory, and data vault remain under ~/.anton.
  • Cowork-specific local state remains under ANTON_COWORK_STATE_DIR when provided, otherwise ~/.anton/cowork.
  • Connector JSON files now ship under anton.cowork.server.connectors and are loaded relative to the installed package.
  • CORS still allows Electron's app://- origin and local dev origins such as http://localhost:5173.

Why the Server Belongs in Anton

The Cowork server is not an independent backend service. Its durable backend is mostly the local filesystem plus Anton's Python APIs. The FastAPI layer maps Cowork UI routes to Anton operations, constructs Anton chat sessions, invokes Anton tools, and adds Cowork-specific concepts such as pins, attachments, connector forms, artifact views, and UI-local state.

Those Cowork concepts should not be promoted into anton.core; they are product/interface concerns. But the server should live on Anton's Python release line because it wraps Anton internals directly.

The intended boundary is:

  • shared Anton behavior stays in anton.core, anton.chat, anton.memory, anton.tools, etc.
  • Cowork-only HTTP/API/UI adaptation stays in anton.cowork.server.
  • CLI users can install Anton without server dependencies because FastAPI/uvicorn are behind anton[cowork-server].

Options Considered

Option A: Keep the server in Cowork and add a server update pipeline

This keeps source ownership simple, but requires a second executable-code updater inside Cowork: artifact hosting, version files, install logic, rollback behavior, CI, and compatibility rules. It also still has to coordinate with Anton releases because the server calls Anton internals directly. This adds operational complexity without removing the core coupling.

Option B: Move the server into Anton with an optional extra

This is the chosen approach. It reuses Anton's existing GitHub-release-based updater, keeps the server versioned with the Python internals it wraps, avoids another repo/pipeline, and makes Electron a thinner shell: install Anton, spawn python -m anton.cowork.server, verify /health, render UI.

The tradeoffs are acceptable: CLI-only users receive inert server source files and connector JSONs on disk, server-only fixes require Anton version bumps, and maintainers must keep Cowork concepts scoped to anton.cowork.server.

Option C: Create a standalone server package or repository

This gives the cleanest artifact boundary, but creates another package/repo, another release process, another updater, and another local development surface. Because the server depends deeply on Anton internals and Electron process lifecycle, coordinated changes would become more error-prone. It optimizes for package purity over release and development reality.

Option D: Move Cowork concepts into Anton core

This maximizes reuse but pollutes Anton core with desktop UI concepts such as pins, attachment sidecars, connector form UX, artifact gallery APIs, and Cowork local state. It was rejected because the server is an interface layer, not evidence that every Cowork concept is a shared Anton abstraction.

Decision

Package the Cowork FastAPI server as anton.cowork.server and install it from Cowork with anton[cowork-server].

This matches the dependency graph: Cowork's Python backend is a local HTTP adapter over Anton's Python runtime and the filesystem, not a separately operated service. The approach reuses Anton's release/update path, avoids another repo and updater, and lets server fixes ship without rebuilding the Electron app.

The main discipline required is preserving the namespace boundary. Cowork routes, state, connector UX, and desktop-specific models stay under anton.cowork.server; reusable capabilities graduate into Anton core only when they are genuinely shared.

Risks and Mitigations

Risk Mitigation
Missed import migration from anton_api.* or routes.* Use package-relative imports and add a test that imports every module under anton.cowork.server.
Connector JSON files missing after install Include connectors in the Hatchling package build and verify loading from the installed package/wheel.
Self-update loses server dependencies Pass extras=["cowork-server"] from the Cowork server update path.
Re-exec breaks under python -m Re-exec explicitly as [sys.executable, "-m", "anton.cowork.server"].
Electron and server drift Gate startup on /health.cowork_server_protocol_version >= <required protocol>.
Packaged server fails during rollout Keep the bundled cowork/server fallback for two Cowork release cycles and preserve diagnostics.
Fresh installs resolve Anton from default branch Consider pinning getAntonGitSpec() to a release tag or channel-specific ref if reproducible installs become more important than newest mainline code.

Operational Rule of Thumb

  • Reusable Anton behavior belongs in Anton core.
  • Cowork HTTP/API adaptation over Anton belongs in anton.cowork.server.
  • Electron lifecycle, installation UX, renderer behavior, and desktop integration stay in Cowork.
  • Breaking renderer/server API changes should bump cowork_server_protocol_version and Electron's required minimum protocol.

StpMax
StpMax approved these changes May 12, 2026
@pnewsam pnewsam marked this pull request as draft May 12, 2026 19:32
@pnewsam
Copy link
Copy Markdown
Contributor Author

pnewsam commented May 12, 2026

Closing for now -- may reopen once we have a clear path forward for the new cowork server repository

@pnewsam pnewsam closed this May 12, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators May 12, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants