- TypeScript 94.1%
- JavaScript 5.9%
| .claude/skills | ||
| bin | ||
| docs | ||
| extensions | ||
| prompts | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| ecosystem.config.cjs | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| solarcompany-maj-2026-analyza-zmien.md | ||
| tsconfig.json | ||
kokpit-wa-bridge
Standalone WhatsApp ↔ Paperclip ↔ Claude bridge for PK01.
Replaces the old paperclip-whatsapp-bridge plugin (retired 2026-05-08 after multipart/permission issues — see ~/.claude/plans/ten-7-je-asi-playful-dahl.md for context).
What it does
- Connects to WhatsApp via Baileys 7.x with on-disk auth state in
~/.kokpit-wa-bridge/auth/. - For each registered customer (= one WhatsApp group), forwards every inbound message into a fixed Paperclip "channel issue" as a comment, with media uploaded as attachments.
- Asks Claude (via the local
claudeCLI) for a customer-facing reply in the Liaison persona, posts it as a Paperclip comment. - Polls Paperclip every 5 s for new comments on each channel issue and forwards customer-facing ones to WhatsApp. Comments containing
<!-- internal -->stay internal; comments shaped**Name (+phone):** …(our own inbound echoes) are filtered out as anti-loop.
Architecture
Single Node 22+ process under PM2. SQLite on disk for customer registry, dedup, polling cursor. No plugin SDK, no embedded postgres dependency at runtime (only used once for auth-state migration). Multipart uploads to Paperclip via axios + form-data — the combination that the multer middleware actually accepts (Node native FormData + Blob does not work).
~/projects/kokpit-wa-bridge/
├── src/
│ ├── index.ts # entry, wires everything
│ ├── config.ts # env vars, customer seed
│ ├── db.ts # SQLite schema + seed
│ ├── customers.ts # registry CRUD
│ ├── dedup.ts # waMsgId tracking, outbound cursor
│ ├── format.ts # canonical comment + media-tag formatting
│ ├── wa-client.ts # Baileys socket wrapper
│ ├── paperclip.ts # axios HTTP client
│ ├── inbound.ts # WA → Paperclip pipeline
│ ├── liaison.ts # claude CLI → reply comment
│ ├── outbound.ts # polling loop, Paperclip → WA
│ └── logger.ts # pino
├── bin/
│ ├── migrate-baileys-auth.ts # one-shot, paperclip postgres → fs
│ ├── add-customer.ts # CLI: add to registry
│ ├── create-notification-token.ts # CLI: create Mia Notifications token
│ └── list-customers.ts # CLI: dump registry
├── prompts/
│ └── liaison.md # Claude system prompt (customer-facing voice)
└── data/ # gitignored runtime state (sqlite db)
Setup (first install)
pnpm install(ornpm install)cp .env.example .envand fill inPAPERCLIP_API_KEY(board token),WA_PHONE_NUMBER. Anthropic auth comes from your localclaudeCLI — no API key needed.npm run build- Migrate Baileys auth state from the old paperclip plugin so you don't re-pair via QR. (One-shot — only needed during the original 2026-05-08 cutover. Already done; keep notes for the next time we need to extract Baileys creds: query the
plugin_statetable wherenamespace='wa-auth', write each row'svalue_jsonto~/.kokpit-wa-bridge/auth/${state_key with -}.json. The script that did this lives only in git history at commit before 0.1.1.) - Cutover (uninstall old plugin, start new bridge):
paperclipai plugin uninstall whatsapp-bridge --force --api-base http://127.0.0.1:3100 pm2 start ecosystem.config.cjs pm2 save pm2 logs kokpit-wa-bridge --lines 20— verify "WhatsApp paired" appears.
Add a customer
node dist/bin/add-customer.js \
--slug new-customer \
--jid 1203...@g.us \
--channel-issue <paperclip-issue-uuid> \
--project <paperclip-project-uuid> \
--goal <paperclip-goal-uuid> \
--git ssh://git@git.kokpit.sk:2222/forge-admin/<repo>.git
pm2 restart kokpit-wa-bridge
You'll need to:
- Create the Paperclip Project + Goal + channel issue (
originKind=plugin:whatsapp-bridge:channel,originId=<jid>) yourself in the dashboard or via API. - Make sure your WhatsApp phone has joined that group already (the bridge doesn't auto-join).
- Restart the bridge so the in-memory customer registry rehydrates from SQLite.
Mia Notifications API
The bridge can accept application events and queue them to the customer's WhatsApp group through the same outbox Mia uses.
Customer-facing guide with examples: docs/mia-notifications-zakaznici.md.
Create a customer-scoped integration token:
node dist/bin/create-notification-token.js --slug <customer-slug> --name <integration-name>
Use the returned token as an application secret, usually named MIA_NOTIFICATIONS_TOKEN.
curl -X POST http://127.0.0.1:4112/v1/events \
-H "Authorization: Bearer $MIA_NOTIFICATIONS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "booking.created",
"title": "Nová objednávka",
"priority": "normal",
"message": "Zákazník: Jana Novakova\nObjednávka vytvorená: 26. 5. 2026, 09:12\nTermín rezervácie: 28. 5. 2026 o 14:00\nSlužba: Konzultácia\nKontakt: +421900123456, jana@example.com",
"data": {
"clientName": "Jana Novakova",
"createdAt": "2026-05-26T07:12:00.000Z",
"date": "2026-05-28",
"time": "14:00",
"serviceType": "Konzultácia",
"phone": "+421900123456",
"email": "jana@example.com"
}
}'
OpenAPI is available at GET /openapi.json; health check is GET /health.
For customer previews, use the bridge wrapper instead of calling preview-up
directly. It injects customer-scoped runtime secrets without printing their
values:
node dist/bin/preview-up-customer.js <preview-slug> <preview-id> /home/pk01/.kokpit-wa-bridge/repos/<customer-slug> --customer <customer-slug>
After a successful production deploy, CI/CD should stop all PR preview containers for the deployed app:
node /home/pk01/projects/kokpit-wa-bridge/dist/bin/cleanup-preview-containers.js --slug <preview-slug>
Supported slug aliases include adaia-page -> adaia and kniznica-sc -> kniznica.
For SolarCompany use --slug solarcompany.
Operations
pm2 logs kokpit-wa-bridge --lines 50
pm2 restart kokpit-wa-bridge
pm2 stop kokpit-wa-bridge
sqlite3 ~/.kokpit-wa-bridge/kokpit.sqlite "SELECT slug, wa_group_jid FROM customers;"
Re-pair (when WhatsApp logs out)
If pm2 logs shows kind: "logged_out" or you see a fresh QR string in the logs:
pm2 restart kokpit-wa-bridge- Open the QR pairing page logged as
qr-server pairing page available, or copy the QR string frompm2 logs kokpit-wa-bridge --lines 5. - Open WhatsApp on phone → Linked Devices → Link a device → scan.
License
MIT — Peter Kramarik peter.kramarik@pk01.sk