Commit Graph

2 Commits

Author SHA1 Message Date
chungyeong
4b0b07c8d4 polish(my-deepagent): rebuild Web GUI visual design — cards, pill badges, 8px grid
The first cut of static/*.html + style.css was functional but visually
bare. Rewriting with a modern dev-tool dashboard aesthetic (Linear /
Vercel / Resend palette), still vanilla CSS — no framework, no build
system (DR-3 / plan.md D3 constraint kept).

Changes
- `static/style.css`: full rewrite (192 → ~580 lines). Adds:
  - CSS custom-property design tokens: surface 0/1/2/3, accent/success/
    warning/danger/info each with a matching `*-bg` rgba.
  - Type system: Inter / Pretendard / Apple SD Gothic Neo / Noto Sans KR
    stack with tabular-nums + system features cv05/ss01.
  - 8 px spacing grid, refined border-radius scale (sm/md/lg).
  - `.card` surface with subtle inner highlight + low shadow.
  - `.badge` pill component with state-* modifiers and an animated dot
    for in-progress states (running / executing / validating /
    awaiting_artifact).
  - `.meta-panel` + `.meta-row` for key/value run detail.
  - `.budget-card` with embedded usage bar (ok/warn/over color states).
  - `.events` log with monospace, hover background, per-event-type
    accent color (run.completed green, run.failed red, etc.) and themed
    scrollbar.
  - `.chips` row for per-role persona override input.
  - Buttons with `primary` / `danger` variants and subtle press animation.
  - Compact responsive break at 720 px (single-column meta rows /
    form-grid / chips).
- `static/index.html`: page-title row + `.card` wrapper for runs table +
  `.budget-grid` for budget cards. Active nav highlight.
- `static/new.html`: form rebuilt inside a card with form-grid layout
  (repo path / branch side-by-side), `.chips` rows for per-role override.
- `static/run.html`: page-title with state badge + `.meta-panel` for
  Run ID / Repo / Worktree / Final report + action bar + cards for
  phases and live events.
- `static/app.js`: redesigned rendering helpers to match new markup:
  - New `badge(state)` helper returning a pill element.
  - `emptyCell(colspan, text, ctaHref, ctaText)` for empty-state tables.
  - Runs list: short hash + arrow link, basename for repo with full path
    in `title`, ISO timestamps trimmed to `YYYY-MM-DD HH:MM:SS`.
  - Budget cards: usage bar fill % computed from spent/cap, status class
    (ok / warn / over) flows to both the amount color and the bar color.
  - New event line uses two-column grid (`.ts` + `.body`), event-line
    class derived from event type for per-type accent coloring.
  - EventSource singleton to prevent stacking on re-renders.

XSS policy unchanged: textContent only, innerHTML/insertAdjacentHTML/
outerHTML still forbidden. The hardcoded comment at the top of `app.js`
is preserved (and the static test that asserts it).

Gates
- ruff check + mypy --strict: PASS (120 source files)
- pytest 16 API tests (read+write+sse+static): all PASS

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:30:51 +09:00
chungyeong
0630142c34 feat(my-deepagent): v0.2 PR #3 — FastAPI + SSE + minimal Web GUI (mydeepagent serve)
Closes the "GUI 미존재" gap from the user's first-session requirements
(REPL + workflow + GUI). v0.2 PR #1's Postgres migration made a second
concurrent writer safe; v0.2 PR #2a/#2b wired durable resume; this commit
ships the HTTP + browser surface that uses them.

No auth, no multi-tenant, single uvicorn worker — per DR-3 boundaries.
v0.3+ will add auth, multi-worker fanout, LISTEN/NOTIFY SSE upgrade.

Backend
- `src/my_deepagent/api/`:
  - `app.py` create_app() factory. lifespan stores db/config/personas/
    workflows on app.state. CORS allow_origin_regex http://localhost(:port)?.
    /static mount + /, /{page}.html for the HTML frontend.
  - `models.py` — pydantic v2 DTOs (extra="forbid") for every route. Auto
    OpenAPI/Swagger via FastAPI's response_model.
  - `deps.py` — get_db / get_config / get_personas / get_workflows.
  - `runner.py` — start_new_run / start_resume. Pre-allocates run_id via
    new `WorkflowEngine.run(pre_allocated_run_id=...)` so the route returns
    the id immediately while the engine runs in asyncio.create_task.
  - `sse.py` — 0.5 s poll over run_events.seq. Emits ServerSentEvent rows;
    sends `event: done` and HTTP-200-closes when run hits terminal.
  - `routes/{runs,personas,workflows,budget}.py`:
      GET  /api/runs              (list, ?limit + ?state)
      GET  /api/runs/{id}         (detail + phases + artifacts + events)
      POST /api/runs              (start; mock-able via runner.start_new_run)
      POST /api/runs/{id}/resume
      POST /api/runs/{id}/abort
      GET  /api/runs/{id}/events  (SSE; Last-Event-ID header + ?last_event_id)
      GET  /api/personas
      GET  /api/workflows
      GET  /api/budget

CLI
- `cli/serve.py` mydeepagent serve [--host 127.0.0.1] [--port 8000].
  Loud stderr warning if --host is not loopback (no auth = footgun).
  uvicorn.run(factory=True, workers=1).
- `cli/main.py` serve command registered.

Static frontend (vanilla HTML/JS/CSS, no build system)
- index.html — runs list + budget summary
- new.html — start-run form (workflow select, repo path, requirements,
  per-role persona override)
- run.html — run detail + live SSE event log + Resume/Abort buttons
- app.js — fetch + EventSource. XSS policy HARDCODED at file top:
  textContent only, innerHTML/insertAdjacentHTML/outerHTML forbidden.
- style.css — dark theme, single file.

Engine
- WorkflowEngine.run(... pre_allocated_run_id: UUID|None = None). None →
  uuid4() (existing behavior). Set → use that UUID. Backward compatible.

Tests
- tests/integration/test_api_read.py (5): list empty, get 404, personas
  seed count (12), workflows seed (>=3), budget empty.
- tests/integration/test_api_write.py (5): missing template 400, extra
  field 422, resume 404, abort 404, mock-runner happy path.
- tests/integration/test_api_sse.py (1): seed terminal run + 3 events,
  drain stream, assert types present + stream closes within 3 s.
- tests/integration/test_api_static.py (5): index/new/run HTML 200,
  app.js content-type + XSS-policy substring assertion, style.css
  content-type.
- All fixtures use httpx ASGITransport + app.router.lifespan_context
  (httpx does NOT auto-trigger FastAPI lifespan) + sqlite tmp_path.

Gates
- ruff check + ruff format --check + mypy --strict: PASS (120 source files)
- pytest non-E2E: 603 PASS (12.15 s) — +16 from new API tests
- pytest E2E real OpenRouter on Postgres: PASS 60.44 s (baseline 71–122 s
  range; well within DR-3 acceptance threshold ≤+20%)

Manual browser verification deferred to a follow-up (docker compose up,
mydeepagent serve, open http://localhost:8000).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:25:15 +09:00