Files
handler/pyproject.toml
T
0xWheatyz bbb01d0882 feat(phase-3): web UI served same-origin by the API
Add a no-build, same-origin web frontend so an operator can open a URL,
see every agent's state, and answer a paused question with no terminal
(Phase 3 DoD). The UI is a client of the existing API — no endpoint,
schema, or auth change — so the 106 existing tests pass unchanged.

- app.py serves the bundled UI from / and /static, gated on UI_ENABLED
  (default on); optional CORS_ORIGINS (default empty => no middleware)
  for hosting the UI on a separate origin. Dedicated /static prefix +
  explicit / route so API routes are never shadowed. Zero new runtime
  deps (StaticFiles/CORSMiddleware ship with Starlette).
- static/: vanilla fetch + plain CSS + vendored alpine.min.js (v3.14.8,
  no CDN). Token captured once into localStorage; all API values render
  via x-text (never x-html) to block agent-authored markup injection.
  Project switcher, agent list, checkmark panel, paginated log, shared
  feed, and Answer / Answer & Resume. Polling scoped to the selected
  agent to avoid an N+1 over the fleet.
- config.py: ui_enabled, cors_origins (+ cors_origin_list); documented
  in .env.example.
- tests/test_api_ui.py: serving, unauthenticated shell, non-shadowing
  401 regression, CORS toggle, UI_ENABLED=false. 114 tests, ruff clean.

The static assets ship in the wheel by default (they live inside the
packaged src/handler tree) — no force-include needed.
2026-07-09 20:43:23 -04:00

50 lines
1.2 KiB
TOML

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "handler"
version = "0.1.0"
description = "Remote control wrapper for Claude Code agents across isolated projects"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
dependencies = [
"fastapi>=0.115,<0.116",
"uvicorn[standard]>=0.34,<0.35",
"sqlalchemy>=2.0,<2.1",
"alembic>=1.14,<1.15",
"psycopg[binary]>=3.2,<3.3",
"pydantic>=2.10,<3.0",
"pydantic-settings>=2.7,<3.0",
"httpx>=0.28,<0.29",
]
[project.optional-dependencies]
dev = [
"pytest>=8.3,<9.0",
"respx>=0.22,<0.23",
"ruff>=0.9,<0.10",
]
[project.scripts]
handler = "handler.control.cli:main"
[tool.hatch.build.targets.wheel]
# The bundled web UI (src/handler/api/static/*) ships automatically: it lives inside the
# packaged `src/handler` tree, and hatchling includes non-.py files there by default.
packages = ["src/handler"]
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
[tool.ruff]
src = ["src", "tests"]
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "W", "UP", "B"]
# B008: FastAPI's Depends()/Query() in argument defaults is the framework's idiom.
ignore = ["B008"]