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
71 changes: 71 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
name: typecheck + test (Node ${{ matrix.node }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: ['20', '22']
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: pnpm

- name: Install
run: pnpm install --frozen-lockfile=false

- name: Typecheck
run: pnpm typecheck

- name: Test
run: pnpm -r --workspace-concurrency=1 test

build:
name: production build (main only)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: '20'
cache: pnpm

- name: Install
run: pnpm install --frozen-lockfile=false

- name: Build
run: pnpm build

- name: Smoke-check the bundle
run: |
test -s packages/server/dist/index.js
test -s packages/ui/dist/index.html

- name: Upload bundle artifact
uses: actions/upload-artifact@v4
with:
name: clawcontrol-build
path: |
packages/server/dist
packages/ui/dist
bin
install.sh
if-no-files-found: error
69 changes: 69 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write # GitHub release upload
packages: write # GHCR push if you switch from Docker Hub

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
cache: pnpm

- name: Install
run: pnpm install --frozen-lockfile=false

- name: Typecheck + test (gate the release)
run: |
pnpm typecheck
pnpm -r --workspace-concurrency=1 test

- name: Build
run: pnpm build

- name: Publish to npm
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
if: secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_TOKEN != ''
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build + push Docker image
if: secrets.DOCKERHUB_USERNAME != '' && secrets.DOCKERHUB_TOKEN != ''
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/clawcontrol:latest
${{ secrets.DOCKERHUB_USERNAME }}/clawcontrol:${{ github.ref_name }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
generate_release_notes: true
files: |
install.sh
39 changes: 39 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Source — never publish raw .ts; the bundle in dist/ is what callers run.
packages/*/src/
packages/*/tsconfig.json
packages/*/build.mjs
packages/*/vite.config.ts
packages/*/tailwind.config.js
packages/*/postcss.config.js
packages/*/index.html
packages/*/tests/
packages/*/__tests__/

# Dev dirs we never want in the tarball.
design/
chats/
.git/
.github/
.vscode/
.idea/

# Local config + transient files.
.env
.env.*
*.log
*.tsbuildinfo

# Lockfiles + tooling — npm consumers don't need these.
pnpm-lock.yaml
pnpm-workspace.yaml
.gitignore

# Workspace + monorepo bookkeeping that doesn't apply post-publish.
node_modules/

# Big planning docs we keep in the repo but don't ship to users.
PROJECT_BRIEF.md
docs/

# Build artifacts we'd rather rebuild than ship verbatim.
coverage/
152 changes: 152 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Changelog

All notable changes to ClawControl. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versions follow
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.0] — 2026-04-26 — first cut

Built across ten phases against `PROJECT_BRIEF.md`. Every commit on the
release branch (`claude/review-project-brief-R6PXw`) is one phase.

### Phase 1 — Monorepo skeleton
- pnpm workspaces with `packages/ui` (Vite + React + TS + Tailwind, dark
default, Phase 1 accent palette) and `packages/server` (Express 4 + ws +
better-sqlite3 + node-cron + zod + dotenv, tsx for dev, esbuild for prod).
- `bin/clawcontrol.js` stub, `install.sh` scaffold, multi-stage Dockerfile,
`docker-compose.yml`, root `package.json` with `bin` + parallel
`start`/`build` scripts.

### Phase 2 — SQLite persistence
- `packages/server/src/db/`: append-only migrations runner, V1 creates the
brief's 13 tables with CHECK constraints + hot-path indexes, opens with
`journal_mode=WAL · foreign_keys=ON · synchronous=NORMAL · busy_timeout=5000`.
- Idempotent dev seed: Nova SaaS Co (`Build #1 AI productivity suite to $1M MRR`),
7 named agents with reports-to relationships, 15-node 4-level goal tree,
10 tasks across every status, 4 heartbeats, 4 memory collections, 6 skills,
20 audit entries, 3 pending board approvals.

### Phase 3 — REST API + WebSocket broadcaster
- 12 routers, one file per resource — agents, tasks, goals, organizations,
budgets, heartbeats, memory, skills, api-keys, channels, board, audit.
All zod-validated; full CRUD + the brief's special endpoints (pause/resume/
restart/clone, approve/reject, ancestry, org-chart, budget summary +
override, run-now, search, test, test-connection, etc.).
- Middleware stack: CORS → JSON → request log → rate limit → bearer auth →
routes → error handler.
- WS at `/ws` broadcasts every event in the brief; budget crossing 100%
auto-pauses the agent and emits both `agent:status_changed` and
`budget:limit_hit`.

### Phase 4 — Model adapters + AES-256-GCM key vault
- One file per provider: anthropic, openai (dynamic discovery + static
fallback), gemini, openrouter (eager catalog refresh), ollama, codex.
Common `ModelAdapter` interface with `listModels` / `testConnection` /
`estimateCostCents`. Static pricing tables + per-provider cost math.
- AES-256-GCM at rest with a 32-byte master key in `~/.clawcontrol/secret.key`
(mode 0600). Versioned ciphertext (`v1.iv.tag.ct`) with GCM tamper detection.
- API keys never leave the encrypted store — `GET /api/api-keys` returns
labels only; `POST /api/api-keys/:id/test-connection` decrypts and dispatches.

### Phase 5 — OpenClaw integration (offline-resilient)
- `openclaw-client.ts`: WS singleton with exponential backoff (1s→30s),
state machine persisted to `system_config.openclaw_status`, inbound event
mapping to DB writes + WS fan-out, outbound `sendTaskToAgent` /
`pauseAgent` / `resumeAgent` / `getAgentStatus` that throw a typed
`OpenClawUnreachableError` when offline.
- `process-manager.ts`: pm2 → systemd → raw PID detection, start/stop/restart
+ `getProcessInfo()` with CPU/RSS via `ps`, `tail -F` log streaming.
- `config-reader.ts`: read/patch `~/.openclaw/config.json` with atomic write
+ restart via process-manager.
- `heartbeat-dispatcher.ts`: single chokepoint; queues when offline, drains
oldest-first on reconnect, audits every fire.

### Phase 6 — Doctor engine (works when OpenClaw is dead)
- 10 checks (one file each): process · gateway · api-keys · node-version ·
disk-space · memory · chrome · mmr-integrity · openclaw-version ·
permissions. 10s timeout per check.
- 5 auto-fixes (one file each): process / gateway (restart OpenClaw),
disk-space (find -mtime cleanup), chrome (auto-detect + write config),
permissions (mkdir + chmod + chown). Output streams as `doctor:fix_output`
+ `doctor:fix_completed`.
- Routes: `GET /api/doctor` · `POST /run/:check` · `POST /fix/:check` ·
`GET /history` (persisted to `doctor_runs` migration v2, last 50) ·
`POST /export` (system fingerprint).
- Hard rule baked into the architecture: nothing under `doctor/` imports
`openclaw-client`.

### Phase 7 — Scheduler · Backups · Updates · Instance manager
- `scheduler-service`: loads enabled heartbeats at boot, registers a
node-cron job per row, computes real `next_run_at` via cron-parser. Live
add/remove/toggle without server restart.
- `backup-service`: WAL checkpoint → tar.gz of DB + `~/.openclaw/config.json`
+ MMR tree, optional AES-256-GCM encryption, retention enforcement.
`restoreBackup` stops OpenClaw + scheduler, swaps files, restarts.
Daily cron at `0 2 * * *`.
- `update-service`: `checkForUpdates` against GitHub releases (8s timeout),
`performUpdate` runs an 8-stage pipeline emitting `update:progress`.
Auto-check cron at 03:00.
- `instance-manager`: spawn additional OpenClaw processes on different
ports for one-click scaling; registry persisted to `~/.clawcontrol/instances.json`.

### Phase 8 — Live React UI
- Replaced the placeholder App with 19 routed pages, all consuming live
data via REST + WebSocket. Mock data is gone.
- `api/api-client.ts`: typed fetch wrapper with bearer auth, 401 →
`/setup`, 500 → red toast. Resource methods for every server route.
- `api/websocket-client.ts`: singleton WS with exponential backoff;
`wsClient.on('agent:status_changed', …)` emitter.
- 12 hooks listed in the brief, each refetching on its WS event(s).
`useDoctor` adds 30s auto-refresh.
- 19 pages: Dashboard (live metrics + agent activity + mission progress +
pending board + sign-off + heartbeats) · Agents (cards + slide-over
with pause/resume/restart/clone/delete + create modal) · Mission Board
(5-column kanban + approve/reject) · Doctor (10 checks + auto-fix
streaming + history + export) · Backups · Goals · Org Chart · Heartbeats
· Budgets (override+resume) · Models · Skills · Organizations · Memory ·
Channels · Browser · Audit (filters + CSV export) · Settings · Setup.
- Tailwind extended with the brief's exact backgrounds (`#0F1117 / #1A1F2E
/ #252B3B`) and accents (`cyan / amber / purple / green`).
- Consistent loading/error/empty states everywhere; red `OfflineBanner`
with "Run Doctor" link.

### Phase 9 — Portable CLI · install.sh · Docker
- `bin/clawcontrol.js`: full implementation (530 lines) replacing the
Phase 1 stub. ASCII banner, Node ≥18 gate, NO_COLOR-aware ANSI.
Interactive wizard via @inquirer/prompts on first run (gateway URL with
auto-detect, optional admin password → argon2id + bearer token, backup
storage with S3 prompt, default LLM provider with API key collected
for first-boot ingestion, 32-byte secret.key generated).
- All 11 subcommands: start / stop / status / backup / doctor / update /
reset [--hard] / export <p> / import <p> / logs / --version / --help.
- `install.sh`: macOS / Linux / WSL detection, Node ≥20 via nvm or
Homebrew, sudo-aware install, `--no-start` flag, `CLAWCONTROL_PACKAGE`
override.
- 3-stage Dockerfile with `tini` for signal handling, slim runtime image.
- `docker-compose.yml` with optional `--profile ollama` and
`--profile openclaw` services + persistent volumes + healthcheck.
- npm publish metadata: `bin`, `files` allow-list, `publishConfig: public`,
MIT, repository, keywords, `prepublishOnly: pnpm build`. `.npmignore`
strips `src/`, lockfiles, `design/`, build artifacts.

### Phase 10 — Final polish
- `ToastBus`: WS-driven react-hot-toast notifications (green: agent
started · task done · backup completed · update installed; amber:
budget at 80% · OpenClaw reconnecting; red: budget limit hit · Doctor
critical · OpenClaw crashed · backup failed · update failed).
- Keyboard shortcuts via `ShortcutsProvider`: Cmd+K fuzzy palette over
agents/tasks/goals/nav/actions · Cmd+D Doctor · Cmd+B backup now ·
Cmd+/ shortcut sheet · `g` then `a`/`m`/`o` chord (1.2s window).
- Mobile: sidebar hidden at `<768px` and replaced by a 5-tab bottom bar
with safe-area-inset-bottom respect; full nav surface still available
via the palette.
- README · CONTRIBUTING (adapter pattern walkthrough) · docs/architecture
· docs/doctor-checks · this file.
- vitest test suites for the server (encryption roundtrip · agents CRUD ·
doctor offline · budget auto-pause · backup roundtrip · anthropic
adapter mocked) and the UI (page renders with mock hooks · WS reconnect
· ErrorBoundary catches render errors).
- GitHub Actions: `ci.yml` (typecheck + test + build) and `release.yml`
(npm publish + Docker push + GitHub release with `install.sh` asset).
Loading
Loading