|
1 | | -# Phoenix Agent (Nx) |
| 1 | +<p align="center"> |
| 2 | + <img src="apps/chat/public/phoenix.png" alt="Phoenix" width="128" height="128" /> |
| 3 | +</p> |
2 | 4 |
|
3 | | -Nx monorepo: **api** (NestJS + Fastify) and **chat** (React + Vite). The API runs the agent (Gemini, Claude Code, OpenAI Codex, or mock); the chat app is the UI. |
| 5 | +<h1 align="center">Phoenix Agent</h1> |
4 | 6 |
|
5 | | -## Run API + Chat |
| 7 | +<p align="center"> |
| 8 | + <strong>Agent orchestration, chat UI, and OAuth-ready APIs—built as a production-grade Nx monorepo.</strong> |
| 9 | +</p> |
6 | 10 |
|
7 | | -Use **Bun** for installs and scripts (`bun install`, `bun run dev`). The repo keeps **`bun.lock`** (for Bun/CI/Docker) and **`package-lock.json`** (for `npm install`). Bun reads **`bun.lock` when it exists** and only migrates from `package-lock.json` if `bun.lock` is missing—do not delete `bun.lock` and then run `bun install`, or workspace installs can break with `FileNotFound` cache errors. |
| 11 | +<p align="center"> |
| 12 | + <a href="https://github.com/phoenix-playgrounds/phoenix-agent-oauth/actions/workflows/ci.yml"><img src="https://github.com/phoenix-playgrounds/phoenix-agent-oauth/actions/workflows/ci.yml/badge.svg" alt="CI" /></a> |
| 13 | + <a href="https://bun.sh"><img src="https://img.shields.io/badge/bun-1.3.11-000?logo=bun&logoColor=white" alt="Bun" /></a> |
| 14 | + <a href="https://nx.dev"><img src="https://img.shields.io/badge/Nx-22-143055?logo=nx&logoColor=white" alt="Nx" /></a> |
| 15 | + <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License MIT" /> |
| 16 | +</p> |
8 | 17 |
|
9 | | -**Quick dev** (API + chat in parallel): `bun run dev` |
| 18 | +--- |
10 | 19 |
|
11 | | -Or start separately: |
| 20 | +**Phoenix Agent** pairs a **NestJS + Fastify** API with a **React + Vite** chat client. The API drives coding agents (Gemini, Claude Code, OpenAI Codex, OpenCode, or a mock provider); the UI handles login, streaming replies, model selection, markdown rendering, and provider OAuth when you need it. |
12 | 21 |
|
13 | | -1. **Start the API** (port 3000): |
| 22 | +## Highlights |
14 | 23 |
|
15 | | - ```sh |
16 | | - bunx nx serve api |
17 | | - ``` |
| 24 | +| | | |
| 25 | +| --- | --- | |
| 26 | +| **Multi-provider** | Switch agents via `AGENT_PROVIDER`; optional stored credentials and session dirs for containerized deploys. | |
| 27 | +| **Real-time chat** | WebSocket at `/ws` with structured client/server actions, single active session semantics, and [documented close codes](docs/API.md#websocket). | |
| 28 | +| **REST + integrations** | Messages, activities, uploads, playgrounds, init scripts, and `POST /api/agent/send-message` for async hooks—see [docs/API.md](docs/API.md). | |
| 29 | +| **Structured logs** | JSON-per-line logging for containers and aggregators (`LOG_LEVEL`, request IDs, HTTP and WS context)—[details](docs/API.md#container-logging). | |
| 30 | +| **E2E & CI** | Playwright suites under `apps/e2e-api` and `apps/e2e-chat`; GitHub Actions runs lint, build, and typecheck on every push/PR. | |
| 31 | +| **Docker** | Multi-arch images published to **GHCR** per provider (`gemini`, `claude-code`, `openai-codex`, `opencode`)—see [CI workflow](.github/workflows/ci.yml). | |
18 | 32 |
|
19 | | - Set `AGENT_PROVIDER=mock` if you don’t have a provider CLI installed: |
| 33 | +## Architecture |
20 | 34 |
|
21 | | - ```sh |
22 | | - AGENT_PROVIDER=mock bunx nx serve api |
23 | | - ``` |
| 35 | +```mermaid |
| 36 | +flowchart LR |
| 37 | + subgraph client [Chat UI] |
| 38 | + Vite[Vite + React] |
| 39 | + end |
| 40 | + subgraph api [API] |
| 41 | + Nest[NestJS + Fastify] |
| 42 | + WS["/ws"] |
| 43 | + REST["/api/*"] |
| 44 | + end |
| 45 | + subgraph agents [Agent backends] |
| 46 | + P[Provider CLIs / APIs] |
| 47 | + end |
| 48 | + Vite --> REST |
| 49 | + Vite --> WS |
| 50 | + Nest --> P |
| 51 | +``` |
24 | 52 |
|
25 | | -2. **Start the chat app** (port 3100): |
| 53 | +## Quick start |
26 | 54 |
|
27 | | - ```sh |
28 | | - bunx nx serve chat |
29 | | - ``` |
| 55 | +**Prerequisites:** [Bun](https://bun.sh) (version pinned in `package.json` as `packageManager`). |
30 | 56 |
|
31 | | - With the default Vite proxy, the chat app will use `http://localhost:3000` for `/api` and `/ws`. If the API runs on another host/port, set: |
| 57 | +```sh |
| 58 | +bun install |
| 59 | +bun run dev |
| 60 | +``` |
32 | 61 |
|
33 | | - ```sh |
34 | | - API_URL=http://localhost:3000 |
35 | | - ``` |
| 62 | +- **API:** [http://localhost:3000](http://localhost:3000) — health at `/api/health` |
| 63 | +- **Chat:** [http://localhost:3100](http://localhost:3100) — Vite proxies `/api` and `/ws` to the API by default |
36 | 64 |
|
37 | | -3. Open **http://localhost:3100**. If `AGENT_PASSWORD` is set, log in with that password first, then use the chat. |
| 65 | +No provider CLI yet? Use the mock agent: |
| 66 | + |
| 67 | +```sh |
| 68 | +AGENT_PROVIDER=mock bunx nx serve api |
| 69 | +``` |
| 70 | + |
| 71 | +If the API runs on another host or port: |
| 72 | + |
| 73 | +```sh |
| 74 | +API_URL=http://localhost:3000 bunx nx serve chat |
| 75 | +``` |
| 76 | + |
| 77 | +When `AGENT_PASSWORD` is set, open the chat and sign in with that password before sending messages. |
| 78 | + |
| 79 | +### Run services separately |
| 80 | + |
| 81 | +| App | Command | Default port | |
| 82 | +| --- | --- | ---: | |
| 83 | +| API | `bunx nx serve api` | 3000 | |
| 84 | +| Chat | `bunx nx serve chat` | 3100 | |
| 85 | + |
| 86 | +## Lockfiles and installs |
| 87 | + |
| 88 | +This repo keeps **`bun.lock`** for Bun, CI, and Docker, and **`package-lock.json`** for npm compatibility. |
| 89 | + |
| 90 | +Bun prefers `bun.lock` when it exists. **Do not delete `bun.lock` and then run `bun install` without restoring it**—workspace installs can fail with cache/`FileNotFound`-style errors. For reproducible CI, commit `bun.lock` and use `bun install --frozen-lockfile` in automation (as in CI). |
38 | 91 |
|
39 | 92 | ## Environment |
40 | 93 |
|
41 | | -Copy `.env.example` to `.env` and adjust. Main variables: |
| 94 | +Copy `.env.example` to `.env` and adjust. |
| 95 | + |
| 96 | +**API (summary):** `PORT`, `CORS_ORIGINS`, `FRAME_ANCESTORS`, `AGENT_PASSWORD`, `AGENT_PROVIDER`, `MODEL_OPTIONS`, `DATA_DIR`, `SYSTEM_PROMPT_PATH`, `PHOENIX_AGENT_ID` / `CONVERSATION_ID`, provider keys (`GEMINI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, …), `SESSION_DIR`, `AGENT_CREDENTIALS_JSON`, `POST_INIT_SCRIPT`, `LOG_LEVEL`. |
42 | 97 |
|
43 | | -- **API:** `PORT`, `AGENT_PASSWORD`, `AGENT_PROVIDER` (mock, gemini, claude-code, openai-codex, opencode), `MODEL_OPTIONS`, `DATA_DIR`, `SYSTEM_PROMPT_PATH` |
44 | | -- **Chat:** `API_URL` (only if the API is not on the same origin or not proxied), `LOCK_CHAT_MODEL` (optional; when set, the model selector is disabled and only shows the model in use), `ASSISTANT_AVATAR_URL` (optional; URL for the assistant’s avatar), `USER_AVATAR_URL` (optional; URL for the user’s avatar) |
| 98 | +**Chat (optional):** `API_URL`, `LOCK_CHAT_MODEL`, `ASSISTANT_AVATAR_URL`, `USER_AVATAR_URL`. |
| 99 | + |
| 100 | +OpenCode and multi-key setups are documented inline in [`.env.example`](.env.example). |
45 | 101 |
|
46 | 102 | ## Project layout |
47 | 103 |
|
48 | | -- `apps/api` – NestJS API, WebSocket at `/ws`, REST under `/api` |
49 | | -- `apps/chat` – React chat UI (login, chat, auth modal, model selector); messages render GitHub-flavored Markdown, and the copy icon on each bubble copies the **raw** message text (including markdown). Long, fence-less TypeScript pasted as one line is reflowed and shown as a code block **in the UI only** (stored text is unchanged). |
50 | | -- `apps/e2e-api`, `apps/e2e-chat` – Playwright / Bun e2e (named so `bun test apps/api` does not pick up e2e specs by prefix) |
51 | | -- `docs/API.md` – REST and WebSocket contract |
| 104 | +| Path | Role | |
| 105 | +| --- | --- | |
| 106 | +| `apps/api` | NestJS API, WebSocket `/ws`, REST under `/api` | |
| 107 | +| `apps/chat` | React chat UI (login, OAuth modal, model selector, GFM markdown, copy raw message, TS one-line reflow in UI only) | |
| 108 | +| `apps/e2e-api`, `apps/e2e-chat` | Playwright e2e (named so `bun test apps/api` does not pick up e2e by path prefix) | |
| 109 | +| `docs/API.md` | REST, WebSocket, and container logging contract | |
52 | 110 |
|
53 | 111 | ## Scripts |
54 | 112 |
|
55 | | -| Script | Command | Description | |
56 | | -|-------------|----------------|--------------------------------| |
57 | | -| **dev** | `bun run dev` | API + chat in parallel | |
58 | | -| **build** | `bun run build`| Build all apps | |
59 | | -| **lint** | `bun run lint` | Lint all projects | |
60 | | -| **test** | `bun run test` | Run unit tests | |
| 113 | +| Script | Command | Description | |
| 114 | +| --- | --- | --- | |
| 115 | +| **dev** | `bun run dev` | API + chat in parallel | |
| 116 | +| **build** | `bun run build` | Build all apps | |
| 117 | +| **lint** | `bun run lint` | Lint all projects | |
| 118 | +| **test** | `bun run test` | Unit tests | |
61 | 119 | | **typecheck** | `bun run typecheck` | Type-check all projects | |
62 | | -| **e2e** | `bun run e2e` | Run E2E tests | |
63 | | -| **ci** | `bun run ci` | Lint, test, build, typecheck, e2e (CI pipeline) | |
| 120 | +| **e2e** | `bun run e2e` | Playwright e2e targets | |
| 121 | +| **ci** | `bun run ci` | Lint, build, and typecheck (matches the main CI job) | |
| 122 | +| **ci:test** | `bun run ci:test` | Same as **ci** plus unit tests | |
| 123 | + |
| 124 | +## Container images |
| 125 | + |
| 126 | +On push to `main` or `dev`, CI builds and pushes provider-specific images to GitHub Container Registry: |
| 127 | + |
| 128 | +`ghcr.io/phoenix-playgrounds/phoenix-agent-oauth:<provider>-<tag>` |
| 129 | + |
| 130 | +Providers align with `AGENT_PROVIDER` build args: `gemini`, `claude-code`, `openai-codex`, `opencode`. See the [Dockerfile](Dockerfile) and [CI workflow](.github/workflows/ci.yml) for build arguments and tags. |
| 131 | + |
| 132 | +## License |
64 | 133 |
|
65 | | -GitHub Actions CI uses Bun and runs `bun run ci`. For reproducible CI, run `bun install` locally once and commit `bun.lock`, then the workflow can use `bun install --frozen-lockfile` in the test job. |
| 134 | +MIT (see `package.json`). |
0 commit comments