diff --git a/.env.test b/.env.test index c79c84d..faa0771 100644 --- a/.env.test +++ b/.env.test @@ -5,3 +5,4 @@ AUTH_SECRET=test-legacy-secret MC_ALLOW_ANY_HOST=1 MC_COOKIE_SECURE= MC_COOKIE_SAMESITE=lax +MC_DISABLE_RATE_LIMIT=1 diff --git a/.github/workflows/quality-gate.yml b/.github/workflows/quality-gate.yml index a8f8515..d4bdf97 100644 --- a/.github/workflows/quality-gate.yml +++ b/.github/workflows/quality-gate.yml @@ -39,12 +39,12 @@ jobs: - name: Unit tests run: pnpm test - - name: Build - run: pnpm build - - name: Prepare E2E environment run: cp .env.test .env + - name: Build + run: pnpm build + - name: Install Playwright browsers run: pnpm exec playwright install --with-deps diff --git a/README.md b/README.md index 415052b..19ae6c0 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,6 @@ Initial login is seeded from `AUTH_USER` / `AUTH_PASS` on first run. ### Known Limitations - **CSP still includes `unsafe-inline`** — `unsafe-eval` has been removed, but inline styles remain for framework compatibility -- **Vitest stubs need real assertions** — unit test files exist but most are placeholder stubs ### Security Considerations @@ -87,6 +86,12 @@ Token usage dashboard with per-model breakdowns, trend charts, and cost analysis ### Background Automation Scheduled tasks for database backups, stale record cleanup, and agent heartbeat monitoring. Configurable via UI or API. +### Direct CLI Integration +Connect Claude Code, Codex, or any CLI tool directly to Mission Control without requiring a gateway. Register connections, send heartbeats with inline token reporting, and auto-register agents. + +### GitHub Issues Sync +Inbound sync from GitHub repositories with label and assignee mapping. Synced issues appear on the task board alongside agent-created tasks. + ### Integrations Outbound webhooks with delivery history, configurable alert rules with cooldowns, and multi-gateway connection management. Optional 1Password CLI integration for secret management. @@ -108,7 +113,7 @@ mission-control/ │ ├── lib/ │ │ ├── auth.ts # Session + API key auth, RBAC │ │ ├── db.ts # SQLite (better-sqlite3, WAL mode) -│ │ ├── migrations.ts # 15 schema migrations +│ │ ├── migrations.ts # 18 schema migrations │ │ ├── scheduler.ts # Background task scheduler │ │ ├── webhooks.ts # Outbound webhook delivery │ │ └── websocket.ts # Gateway WebSocket client @@ -128,7 +133,7 @@ mission-control/ | Charts | Recharts 3 | | Real-time | WebSocket + Server-Sent Events | | Auth | scrypt hashing, session tokens, RBAC | -| Testing | Vitest + Playwright (146 E2E tests) | +| Testing | Vitest + Playwright (165 E2E tests) | ## Authentication @@ -233,6 +238,18 @@ All endpoints require authentication unless noted. Full reference below. | `GET/POST/PUT/DELETE` | `/api/alerts` | admin | Alert rules | | `GET/POST/PUT/DELETE` | `/api/gateways` | admin | Gateway connections | | `GET/PUT/DELETE/POST` | `/api/integrations` | admin | Integration management | +| `POST` | `/api/github` | admin | Trigger GitHub Issues sync | + + + +
+Direct CLI + +| Method | Path | Role | Description | +|--------|------|------|-------------| +| `POST` | `/api/connect` | operator | Register direct CLI connection | +| `GET` | `/api/connect` | viewer | List active connections | +| `DELETE` | `/api/connect` | operator | Disconnect CLI session |
@@ -339,13 +356,15 @@ See [open issues](https://github.com/builderz-labs/mission-control/issues) for p - [x] Export endpoint row limits ([#43](https://github.com/builderz-labs/mission-control/issues/43)) - [x] Fill in Vitest unit test stubs with real assertions +- [x] Direct CLI integration — connect tools like Codex, Claude Code, or custom CLIs directly without requiring a gateway ([#61](https://github.com/builderz-labs/mission-control/pull/61)) +- [x] OpenAPI 3.1 documentation with Scalar UI ([#60](https://github.com/builderz-labs/mission-control/pull/60)) +- [x] GitHub Issues sync — inbound sync with label/assignee mapping ([#63](https://github.com/builderz-labs/mission-control/pull/63)) + **Up next:** - [ ] Agent-agnostic gateway support — connect any orchestration framework (OpenClaw, ZeroClaw, OpenFang, NeoBot, IronClaw, etc.), not just OpenClaw -- [ ] Direct CLI integration — connect tools like Codex, Claude Code, or custom CLIs directly without requiring a gateway - [ ] Native macOS app (Electron or Tauri) - [ ] First-class per-agent cost breakdowns — dedicated panel with per-agent token usage and spend (currently derivable from per-session data) -- [ ] OpenAPI / Swagger documentation - [ ] Webhook retry with exponential backoff - [ ] OAuth approval UI improvements - [ ] API token rotation UI diff --git a/src/lib/db.ts b/src/lib/db.ts index 75ea0f1..9012aa9 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -74,6 +74,9 @@ function initializeSchema() { interface CountRow { count: number } function seedAdminUserFromEnv(dbConn: Database.Database): void { + // Skip seeding during `next build` — env vars may not be available yet + if (process.env.NEXT_PHASE === 'phase-production-build') return + const count = (dbConn.prepare('SELECT COUNT(*) as count FROM users').get() as CountRow).count if (count > 0) return @@ -82,7 +85,7 @@ function seedAdminUserFromEnv(dbConn: Database.Database): void { const displayName = username.charAt(0).toUpperCase() + username.slice(1) dbConn.prepare(` - INSERT INTO users (username, display_name, password_hash, role) + INSERT OR IGNORE INTO users (username, display_name, password_hash, role) VALUES (?, ?, ?, ?) `).run(username, displayName, hashPassword(password), 'admin') diff --git a/src/lib/migrations.ts b/src/lib/migrations.ts index 420f4c9..47ac6f1 100644 --- a/src/lib/migrations.ts +++ b/src/lib/migrations.ts @@ -477,6 +477,24 @@ const migrations: Migration[] = [ CREATE INDEX IF NOT EXISTS idx_github_syncs_created_at ON github_syncs(created_at); `) } + }, + { + id: '018_token_usage', + up: (db) => { + db.exec(` + CREATE TABLE IF NOT EXISTS token_usage ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + model TEXT NOT NULL, + session_id TEXT NOT NULL, + input_tokens INTEGER NOT NULL DEFAULT 0, + output_tokens INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL DEFAULT (unixepoch()) + ); + CREATE INDEX IF NOT EXISTS idx_token_usage_session_id ON token_usage(session_id); + CREATE INDEX IF NOT EXISTS idx_token_usage_created_at ON token_usage(created_at); + CREATE INDEX IF NOT EXISTS idx_token_usage_model ON token_usage(model); + `) + } } ] diff --git a/tests/login-flow.spec.ts b/tests/login-flow.spec.ts index 37912ec..91c5a89 100644 --- a/tests/login-flow.spec.ts +++ b/tests/login-flow.spec.ts @@ -18,7 +18,8 @@ test.describe('Login Flow', () => { test('login API returns session cookie on success', async ({ request }) => { const res = await request.post('/api/auth/login', { - data: { username: 'testadmin', password: 'testpass123' } + data: { username: 'testadmin', password: 'testpass123' }, + headers: { 'x-forwarded-for': '10.88.88.1' } }) expect(res.status()).toBe(200) @@ -38,7 +39,8 @@ test.describe('Login Flow', () => { test('session cookie grants API access', async ({ request }) => { // Login to get a session const loginRes = await request.post('/api/auth/login', { - data: { username: 'testadmin', password: 'testpass123' } + data: { username: 'testadmin', password: 'testpass123' }, + headers: { 'x-forwarded-for': '10.88.88.2' } }) expect(loginRes.status()).toBe(200) @@ -50,7 +52,7 @@ test.describe('Login Flow', () => { // Use the session cookie to access /api/auth/me const meRes = await request.get('/api/auth/me', { - headers: { 'cookie': `mc-session=${sessionToken}` } + headers: { 'cookie': `mc-session=${sessionToken}`, 'x-forwarded-for': '10.88.88.2' } }) expect(meRes.status()).toBe(200) const body = await meRes.json()