diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..27d926f --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +GOOGLE_CLIENT_ID= +# Required whenever HOST binds beyond loopback (e.g. 0.0.0.0 for phone/LAN). +ALLOWED_EMAILS= +# Required for phone/LAN (raw IP) access. Google GIS cannot use raw IP origins. +DASHBOARD_PIN= +# Google-compatible public UI origin (Vercel static host). Default below. +PUBLIC_ORIGIN=https://agent-dashboard-ctt.vercel.app +# Optional: override auto-generated secret for cookie + Bearer token signing +# (~/.config/agent-deck/session-secret) +SESSION_SECRET= +# Optional LAN bind for phone access (LaunchAgent / serve:lan also set this) +# HOST=0.0.0.0 +# PORT=3847 +# UI → Mac API base for a Vercel (or other) static build. Use an HTTPS tunnel URL +# when the UI is on HTTPS; leave empty for same-origin Mac serve. +# VITE_API_BASE=https://your-mac-api.example diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..79165c0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + cache: npm + - run: npm ci + - run: npm run lint + - run: npm test + - run: npm run build diff --git a/README.md b/README.md index 761ad01..948db19 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Agent Deck - + Local macOS dashboard for AI agent usage (Cursor, Claude Code, Codex), Mac resource meters, and your GitHub contribution calendar. -Runs entirely on your machine. Nothing is uploaded. +Runs entirely on your machine. Usage-reset lookups send your local Cursor, Codex, and Claude auth tokens from this Mac only to those vendors' usage APIs (api2.cursor.sh, auth.openai.com / chatgpt.com, api.anthropic.com) - not to Agent Deck or any other service.    -## Why localhost (not a public web host) +## Why the Mac still runs the API Your machine is the source of truth: @@ -17,41 +17,116 @@ Your machine is the source of truth: - **CPU / GPU / Memory** only exist on this Mac - **GitHub** calendar is fetched with your local `gh` auth -A remote host cannot see those safely. Keep the app on `127.0.0.1`. This GitHub repo is only the source code. +Collectors must stay on this Mac. You can host a **static UI** on Vercel (see below) that talks to the Mac API over a tunnel or LAN URL - do not deploy the collectors to a public cloud. -## Download +## Recommended setup (auto-start + dual auth) + +You do **not** need `npm run dev` day to day. That is only for developers hacking on the UI. -### Option A - Clone with Git +1. Install once: ```bash git clone https://github.com/ctt062/agent-dashboard.git cd agent-dashboard npm install -npm run dev +cp .env.example .env +``` + +2. Configure `.env`: + +```bash +GOOGLE_CLIENT_ID=your-id.apps.googleusercontent.com +ALLOWED_EMAILS=you@gmail.com +DASHBOARD_PIN=your-phone-pin +PUBLIC_ORIGIN=https://agent-dashboard-ctt.vercel.app +``` + +Authorized JavaScript origins (Google Cloud Console) - Google GIS does **not** accept raw LAN IPs: + +- `http://127.0.0.1:3847` and `http://localhost:3847` (production serve) +- `http://127.0.0.1:5174` and `http://localhost:5174` (`npm run dev`) +- `https://agent-dashboard-ctt.vercel.app` (or your `PUBLIC_ORIGIN`) + +3. Build + install login auto-start: + +```bash +npm run setup ``` -### Option B - ZIP download (no Git) +`npm run setup` defaults to LAN bind (`HOST=0.0.0.0`) and refuses to install unless `GOOGLE_CLIENT_ID`, `ALLOWED_EMAILS`, and `DASHBOARD_PIN` are set. + +After that, Agent Deck starts when you log into your Mac: + +- Desktop / localhost and the Vercel UI origin use **Google** sign-in (verified email + allowlist). +- Phone on a raw LAN IP uses **PIN** sign-in (`DASHBOARD_PIN`). + +`npm run dev` is optional (hot reload for coding). Prefer `npm run setup` for normal use. + +## Optional: static UI on Vercel + +Public UI: [https://agent-dashboard-ctt.vercel.app/](https://agent-dashboard-ctt.vercel.app/) + +1. Deploy the Vite `dist/` (SPA rewrite is in `vercel.json`). +2. Point the UI at your Mac API with either: + - build-time `VITE_API_BASE=https://your-mac-tunnel.example`, or + - runtime `public/runtime-config.js` → `window.__AGENT_DECK_API_BASE__` +3. Prefer an **HTTPS tunnel** to the Mac when the UI is on HTTPS (browsers block mixed content to bare `http://192.168.x.x`). +4. On the Mac, keep `PUBLIC_ORIGIN=https://agent-dashboard-ctt.vercel.app` so CORS allows that origin + localhost only (no wildcard, no raw LAN origins). +5. Sign-in returns a **Bearer token** stored in the browser (`sessionStorage`) and sent as `Authorization: Bearer …`. Do not rely on third-party cookies for Vercel → Mac. + +Collectors and `/api/*` still run on the Mac. + +## Download + +Day-to-day install is [Recommended setup](#recommended-setup-auto-start--dual-auth) (`.env` auth + `npm run setup`). Auth is required for dashboard data. + +### ZIP (no Git) 1. Open https://github.com/ctt062/agent-dashboard 2. Click **Code → Download ZIP** -3. Unzip, then in that folder: +3. Unzip, then continue from `cp .env.example .env` in Recommended setup. + +### One-command production serve + +After `.env` is configured (see Recommended setup), build and serve API + static UI on one port: ```bash -npm install -npm run dev +npm run serve ``` -Open **http://127.0.0.1:5174** +Open **http://127.0.0.1:3847** (Google sign-in). + +`npm start` alone also works after `npm run build` (serves `dist/` when present). Hot-reload coding: `npm run dev` → **http://127.0.0.1:5174**. + +## View on your phone (same Wi-Fi) -### Production-style start (optional) +Agent Deck is a local web app. Your Mac still runs the API; your phone opens it in Safari/Chrome and signs in with the **PIN** (Google cannot authorize raw IP origins). ```bash -npm install -npm run build -npm start +npm run serve:lan +``` + +The terminal prints a LAN URL like `http://192.168.x.x:3847`. Open that on your phone (same Wi-Fi) and enter `DASHBOARD_PIN`. On iOS you can use **Share → Add to Home Screen** for an app-like icon. + +Dev equivalent: + +```bash +npm run dev:lan ``` -`npm start` serves the API on `http://127.0.0.1:3847`. For the UI, use `npm run preview` (or keep using `npm run dev`). +Then open the printed Vite URL (port `5174`) on your phone. + +Only do this on a trusted network - LAN mode exposes local agent + Mac metrics to devices on that Wi-Fi. + +## Start at login (macOS) + +Use `npm run setup` from [Recommended setup](#recommended-setup-auto-start--dual-auth) (requires `.env` auth when LAN-bound). LaunchAgent only: + +```bash +npm run launchagent:install +``` + +Remove with `npm run launchagent:uninstall`. ## Requirements @@ -63,45 +138,64 @@ npm start - Claude Code logs under `~/.claude/projects/` - Codex sessions under `~/.codex/sessions/` -Missing collectors degrade gracefully - panels show empty or partial data instead of crashing. +Missing collectors degrade gracefully - each panel shows a short hint instead of crashing. + +## Features + +- **Date range**: Today / 7 days / 30 days / This month for agent share and charts +- **Usage resets**: Per-provider token/limit reset times (Cursor billing cycle via local dashboard API, Codex ChatGPT wham/usage windows, Claude rolling 5h/weekly with `/usage` guidance when exact times are unavailable) +- **Detailed agent stats**: period total, avg/day, active days, peak day, acceptance rate (Cursor), input/output tokens +- **Dual-series charts** plus a cross-agent comparison chart +- **Dual auth**: Google on localhost / `PUBLIC_ORIGIN`; PIN on LAN IPs; Bearer token for Vercel → Mac API; `GOOGLE_CLIENT_ID` + `ALLOWED_EMAILS` + `DASHBOARD_PIN` required for LAN bind +- **Local web app**: `npm run setup` auto-starts at login; `serve:lan` / LaunchAgent for phone on the same Wi-Fi +- **Cached collectors** (~10s TTL) with parallel collection; usage-reset lookups cache separately (~3 min). Refresh bypasses both caches ## Stack - Vite + React + TypeScript UI -- Express API on port `3847` (localhost only) +- Express API on port `3847` (Local Mac; also serves `dist/` after build) - Collectors read local files / `top` / `ioreg` / `gh api` ## What the percentages mean -Agent % is **relative share** of a local activity score across Cursor, Claude Code, and Codex on this Mac - not a vendor billing percentage. +Agent % is **relative share** of a local activity score across Cursor, Claude Code, and Codex for the selected date range - not a vendor billing percentage. | Agent | Primary signal | |-------|----------------| -| Cursor | Accepted AI lines (`aiCodeTracking.dailyStats`) + chat volume | +| Cursor | Accepted AI lines (`aiCodeTracking.dailyStats`) | | Claude Code | Tokens from `~/.claude/projects/**/*.jsonl`, else message volume | | Codex | Tokens from `~/.codex/sessions/**/*.jsonl`, else event volume | ## API -Bound to `127.0.0.1` only: +Default bind is `127.0.0.1`. Use `HOST=0.0.0.0` (or `npm run serve:lan`) for LAN/phone access. + +`/api/dashboard` and `/api/system` require auth (session cookie and/or `Authorization: Bearer …`). `/api/health` and `/api/auth/*` are reachable without a prior session (sign-in routes issue the token). | Endpoint | Description | |----------|-------------| -| `GET /api/dashboard` | Full payload (agents + system + GitHub) | -| `GET /api/system` | Mac snapshot only | -| `GET /api/health` | Liveness check | +| `GET /api/dashboard?range=1d\|7d\|30d\|month` | Full payload (agents + system + GitHub). Add `refresh=1` to bypass cache. Auth required. | +| `GET /api/system` | Mac snapshot only. Auth required. | +| `GET /api/health` | Liveness check (no auth) | +| `GET /api/auth/config` | Auth mode for this Host/Origin (`google` or `pin`) | -Override the API port with `PORT` if needed: +Override host/port if needed: ```bash -PORT=4000 npm run dev:api +HOST=0.0.0.0 PORT=4000 npm start ``` ## Privacy -- Nothing is uploaded by this app -- Do not expose port `3847` beyond localhost -- Stats come from files and tools already on your Mac +- Dashboard stats come from files and tools already on your Mac +- Google sign-in uses Google Identity Services on localhost and `PUBLIC_ORIGIN`; LAN IPs use PIN instead +- Cross-origin Vercel UI uses Bearer tokens (not third-party cookies); Mac same-origin UI may still use session cookies +- When LAN bind is enabled, `GOOGLE_CLIENT_ID`, `ALLOWED_EMAILS`, and `DASHBOARD_PIN` are required +- Failed PIN attempts are rate-limited in memory per client IP +- Usage-reset times use local Cursor / Codex / Claude credentials on this machine only to call those vendors' usage APIs; tokens are not sent to Agent Deck or any other service +- Default LaunchAgent bind is LAN-capable (`HOST=0.0.0.0`) - use only on trusted Wi-Fi +- CORS allowlists localhost + `PUBLIC_ORIGIN` only (no wildcard) +- Static UI may live on Vercel; collectors and the API stay on your Mac ## License diff --git a/docs/Screenshot.png b/docs/Screenshot.png new file mode 100644 index 0000000..53f5a1f Binary files /dev/null and b/docs/Screenshot.png differ diff --git a/index.html b/index.html index e394ce7..4af8db8 100644 --- a/index.html +++ b/index.html @@ -3,8 +3,13 @@
- + + + + + +