Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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
154 changes: 124 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Agent Deck

![Agent Deck screenshot](./src/assets/Screenshot.png)
![Agent Deck screenshot](./docs/Screenshot.png)

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.

![Agent Deck](https://img.shields.io/badge/platform-macOS-black) ![Node](https://img.shields.io/badge/node-%3E%3D22-brightgreen) ![License](https://img.shields.io/badge/license-MIT-blue)

## Why localhost (not a public web host)
## Why the Mac still runs the API

Your machine is the source of truth:

Expand All @@ -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

Expand All @@ -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

Expand Down
Binary file added docs/Screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' fill='%23000'/><text y='.9em' font-size='80' x='10'>▣</text></svg>" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="color-scheme" content="dark" />
<meta name="theme-color" content="#050505" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Agent Deck" />
<title>Agent Deck</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
Expand All @@ -15,6 +20,7 @@
</head>
<body>
<div id="root"></div>
<script src="/runtime-config.js"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading