|
| 1 | +# BotsChat |
| 2 | + |
| 3 | +A full-stack messaging application built on Cloudflare Workers. Connects to self-hosted OpenClaw AI assistant instances via WebSocket, providing end-to-end encrypted AI chat and scheduled task management. |
| 4 | + |
| 5 | +- Production: https://console.botschat.app |
| 6 | +- GitHub: https://github.com/botschat-app/botsChat |
| 7 | + |
| 8 | +## Project Structure |
| 9 | + |
| 10 | +``` |
| 11 | +botsChat/ |
| 12 | +├── packages/ |
| 13 | +│ ├── api/ # Cloudflare Worker API (Hono + D1 + R2 + DO) |
| 14 | +│ ├── web/ # React SPA frontend (Vite) |
| 15 | +│ ├── plugin/ # OpenClaw plugin (WebSocket client) |
| 16 | +│ └── e2e-crypto/ # E2E encryption library (PBKDF2 + AES-GCM) |
| 17 | +├── migrations/ # D1 database migration files |
| 18 | +├── scripts/ # Dev scripts (dev.sh, mock-openclaw.mjs) |
| 19 | +├── tests/ # Test scripts |
| 20 | +├── ios/ # Capacitor iOS shell |
| 21 | +├── android/ # Capacitor Android shell |
| 22 | +└── wrangler.toml # Cloudflare Workers configuration |
| 23 | +``` |
| 24 | + |
| 25 | +## Cloudflare Resources |
| 26 | + |
| 27 | +| Resource | Binding | |
| 28 | +|----------|---------| |
| 29 | +| D1 Database | `DB` → `botschat-db` | |
| 30 | +| R2 Bucket | `MEDIA` → `botschat-media` | |
| 31 | +| Durable Object | `CONNECTION_DO` → `ConnectionDO` | |
| 32 | + |
| 33 | +## Architecture Principles |
| 34 | + |
| 35 | +### Data Ownership |
| 36 | + |
| 37 | +| Owner | Storage | Examples | |
| 38 | +|-------|---------|----------| |
| 39 | +| **BotsChat** | D1 | Task names, channel IDs, user info, message records | |
| 40 | +| **OpenClaw** | OpenClaw CronService | Schedule, instructions, model, job run status | |
| 41 | + |
| 42 | +Key rules: |
| 43 | +- OpenClaw-owned data must be fetched in real time; never cache copies in D1 or DO Storage |
| 44 | +- D1 only stores BotsChat's own metadata (task↔channel associations, user info) |
| 45 | +- When OpenClaw data is needed, request it via DO → plugin WebSocket (`task.scan.request` → `task.scan.result`) |
| 46 | + |
| 47 | +### Interacting with OpenClaw |
| 48 | + |
| 49 | +All CronService mutations must go through the OpenClaw CLI (`openclaw cron add/edit/rm`). Never directly read/write `~/.openclaw/cron/jobs.json`. |
| 50 | + |
| 51 | +Notes: |
| 52 | +- `openclaw cron edit` for isolated sessions requires the `--message` flag |
| 53 | +- `openclaw cron add` requires the `--name` flag |
| 54 | +- On macOS, all CLI commands need `/opt/homebrew/bin` in `env.PATH` |
| 55 | + |
| 56 | +### ID Generation |
| 57 | + |
| 58 | +- BotsChat entity IDs: prefixed (`tsk_xxx`, `ch_xxx`, `u_xxx`), generated by BotsChat |
| 59 | +- OpenClaw cron job IDs: UUID format, generated by OpenClaw, returned via `task.schedule.ack` |
| 60 | + |
| 61 | +### ConnectionDO |
| 62 | + |
| 63 | +`ConnectionDO` is the WebSocket relay hub (one instance per user): |
| 64 | +- Holds the OpenClaw plugin WebSocket (tag: `"openclaw"`) |
| 65 | +- Holds browser session WebSocket(s) (tag: `"browser:<sessionId>"`) |
| 66 | +- Relays messages bidirectionally (OpenClaw ↔ browser) |
| 67 | +- Persists messages to D1 |
| 68 | +- Uses the Hibernation API for zero-cost idle connections |
| 69 | + |
| 70 | +## Local Development |
| 71 | + |
| 72 | +### Quick Start |
| 73 | + |
| 74 | +```bash |
| 75 | +./scripts/dev.sh # Full dev env: build + migrate + server + mock AI + open browser |
| 76 | +./scripts/dev.sh reset # Wipe local DB → re-migrate → start full dev env |
| 77 | +./scripts/dev.sh server # Server only (no mock, no browser) |
| 78 | +./scripts/dev.sh mock # Start Mock OpenClaw standalone (foreground) |
| 79 | +./scripts/dev.sh migrate # Only run D1 migrations |
| 80 | +./scripts/dev.sh build # Only build web frontend |
| 81 | +``` |
| 82 | + |
| 83 | +No env vars needed — `dev.sh` auto-generates `DEV_AUTH_SECRET`, starts mock AI in background, and opens browser with auto-login + E2E. |
| 84 | + |
| 85 | +### Manual Start |
| 86 | + |
| 87 | +```bash |
| 88 | +npm run build -w packages/web |
| 89 | +npx wrangler dev --config wrangler.toml --ip 0.0.0.0 \ |
| 90 | + --var ENVIRONMENT:development \ |
| 91 | + --var DEV_AUTH_SECRET:any-secret-string |
| 92 | +``` |
| 93 | + |
| 94 | +### D1 Migrations |
| 95 | + |
| 96 | +```bash |
| 97 | +npx wrangler d1 migrations apply botschat-db --local # Apply migrations |
| 98 | +rm -rf .wrangler/state && npx wrangler d1 migrations apply botschat-db --local # Full reset |
| 99 | +``` |
| 100 | + |
| 101 | +### Authentication |
| 102 | + |
| 103 | +- **Production**: Google / GitHub OAuth only |
| 104 | +- **Local dev**: `ENVIRONMENT=development` enables email registration; `DEV_AUTH_SECRET` enables the `/api/dev-auth/login` endpoint |
| 105 | + |
| 106 | +### Browser Auto-Login (Local Dev) |
| 107 | + |
| 108 | +``` |
| 109 | +http://localhost:8787/?dev_token=<DEV_AUTH_SECRET>&dev_user=<userId>&dev_e2e=<e2e_password> |
| 110 | +``` |
| 111 | + |
| 112 | +Parameters: `dev_token` (required), `dev_user` (optional, defaults to `dev-test-user`), `dev_e2e` (optional, auto-sets E2E password). |
| 113 | + |
| 114 | +## Mock OpenClaw (Local Testing) |
| 115 | + |
| 116 | +`scripts/mock-openclaw.mjs` is a zero-dependency Node.js WebSocket client that simulates the OpenClaw plugin protocol, enabling full BotsChat testing without deploying a real OpenClaw instance. |
| 117 | + |
| 118 | +### Quick Start |
| 119 | + |
| 120 | +```bash |
| 121 | +# Terminal 1: start the server |
| 122 | +./scripts/dev.sh |
| 123 | + |
| 124 | +# Terminal 2: start mock (auto-creates a pairing token) |
| 125 | +./scripts/dev.sh mock |
| 126 | +``` |
| 127 | + |
| 128 | +### Manual Start |
| 129 | + |
| 130 | +```bash |
| 131 | +node scripts/mock-openclaw.mjs --token <your_pairing_token> |
| 132 | +``` |
| 133 | + |
| 134 | +### Options |
| 135 | + |
| 136 | +| Flag | Default | Description | |
| 137 | +|------|---------|-------------| |
| 138 | +| `--token <pat>` | (required) | Pairing token | |
| 139 | +| `--url <url>` | `http://localhost:8787` | Server URL | |
| 140 | +| `--agents <list>` | `main` | Comma-separated agent IDs | |
| 141 | +| `--delay <ms>` | `300` | Reply delay (simulates thinking) | |
| 142 | +| `--stream` | `false` | Enable streaming replies (chunk by chunk) | |
| 143 | +| `--model <name>` | `mock/echo-1.0` | Default model name | |
| 144 | + |
| 145 | +### Mock Behavior |
| 146 | + |
| 147 | +| Incoming Message | Response | |
| 148 | +|-----------------|----------| |
| 149 | +| `user.message` | Echoes back as `agent.text`: "Mock reply: {text}" | |
| 150 | +| `user.media` | Acknowledges media receipt | |
| 151 | +| `user.command` | Acknowledges command | |
| 152 | +| `user.action` | Acknowledges A2UI interaction | |
| 153 | +| `task.scan.request` | Returns empty task list | |
| 154 | +| `models.request` | Returns 4 mock models | |
| 155 | +| `task.schedule` | `task.schedule.ack` confirmation | |
| 156 | +| `task.run` | Simulates 2-second execution + `job.update` | |
| 157 | +| `settings.defaultModel` | `defaultModel.updated` confirmation | |
| 158 | +| `ping` | `pong` | |
| 159 | + |
| 160 | +## Testing |
| 161 | + |
| 162 | +```bash |
| 163 | +./tests/media-api-test.sh # 17 API tests |
| 164 | +node tests/media-ws-test.mjs # 9 WebSocket tests |
| 165 | +node tests/media-browser-test.mjs # Browser test data prep |
| 166 | +npm test -w packages/e2e-crypto # E2E crypto unit tests |
| 167 | +``` |
| 168 | + |
| 169 | +## Deployment |
| 170 | + |
| 171 | +```bash |
| 172 | +npm run build -w packages/web |
| 173 | +npx wrangler deploy --config wrangler.toml |
| 174 | +npx wrangler d1 migrations apply botschat-db --remote |
| 175 | +``` |
| 176 | + |
| 177 | +Production uses `ENVIRONMENT=production` (OAuth only). Secrets are set via `wrangler secret put`. |
| 178 | + |
| 179 | +## Debugging Tips |
| 180 | + |
| 181 | +- **Port conflict**: `lsof -ti:8787 | xargs kill -9` |
| 182 | +- **D1 schema error**: `rm -rf .wrangler/state` → re-migrate |
| 183 | +- **Plugin WS URL**: `cloudUrl` must be `http://...`; the plugin auto-converts to `ws://` |
| 184 | +- **Blank screen after login**: Hard refresh (`Cmd+Shift+R`) or clear localStorage |
| 185 | + |
| 186 | +## UI Design |
| 187 | + |
| 188 | +See `.cursor/rules/design-guideline.md` for the full specification. Key principles: |
| 189 | +- Three-column layout (Icon Rail + Sidebar + Main Content + optional Detail Panel) |
| 190 | +- Dark/light dual themes via CSS variables |
| 191 | +- Flat message rows (not bubbles) for information density |
| 192 | +- Sidebar always uses dark/brand background; only Main Content follows theme |
0 commit comments