| name | bundler-gui |
| description | Workflow skill for the PumpFun Bundler watcher/GUI system. Use when working on gui.ts, the watcher supervisor, watcher subprocess (task43-watch-pump-program.ts), or the /api/watch/* routes. Covers: starting/stopping the GUI, watcher lifecycle, state file IPC, route patterns, environment variables, and safe commit workflow. |
| allowed-tools | ["Read","Write","Edit","Glob","Grep","Bash"] |
bundler-gui skill
Workflow reference for the PumpFun Bundler watcher + GUI subsystem.
Quick Start
npm install
npm run gui
http://localhost:3001
Key Files
| File | Role |
|---|
gui.ts | HTTP server + API routes (~2250 lines). Imports page template from src/ui/. |
src/ui/page.html | SPA HTML template (~1955 lines). Uses {{RPC_ENDPOINT}} and {{WATCHER_STATUS_POLL_MS}} placeholders. |
src/ui/page.ts | Template loader. buildPage(rpcEndpoint, pollMs) reads and interpolates page.html. |
scripts/task43-watch-pump-program.ts | Watcher subprocess. Spawned by GUI supervisor. Writes state to tmp/. |
src/clients/config.ts | Convict env-var schema (RPC, Jito, Geyser config). |
config.ts | Root keypair config (wallet, payer — empty in git, filled at runtime). |
tmp/pump-watch-tracking-state.json | Watcher positions + mode. Written every 5s by subprocess. |
tmp/pump-watch-supervisor-state.json | Supervisor lifecycle state. Written by gui.ts. |
.githooks/pre-commit | Secret guard. Run npm run secret:guard:setup on each clone. |
Environment Variables
GUI Server
| Var | Default | Purpose |
|---|
GUI_PORT | 3001 | HTTP port |
GUI_STRICT_PORT | false | Error if port in use instead of retrying |
RPC_URL | https://api.mainnet-beta.solana.com | Solana RPC endpoint |
JUPITER_TIMEOUT_MS | 7000 | Jupiter API timeout |
JUPITER_RETRIES | 3 | Jupiter max retries |
MAX_JSON_POST_BODY_BYTES | 64000 | Request body limit |
SIGNATURE_REPLAY_TTL_MS | 300000 | Replay attack window (5 min) |
GENERAL_RATE_LIMIT_MAX | 30 | Default rate limit per 60s |
BUNDLER_ACTION_RATE_LIMIT_MAX | 30 | Bundler action rate limit |
JUPITER_RATE_LIMIT_MAX | 30 | Jupiter rate limit |
SIGNING_ACTION_RATE_LIMIT_MAX | 15 | Signing rate limit |
SERVER_KEYS_SESSION_RATE_LIMIT_MAX | 30 | Server-key session rate limit |
Watcher Supervisor
| Var | Default | Purpose |
|---|
AUTO_WATCH_SUPERVISOR_PROGRAM | scripts/task43-watch-pump-program.ts | Path to watcher script |
AUTO_WATCH_TRACKING_STATE_FILE | tmp/pump-watch-tracking-state.json | Where watcher writes positions |
AUTO_WATCH_SUPERVISOR_STATE_FILE | tmp/pump-watch-supervisor-state.json | Supervisor lifecycle state |
AUTO_WATCH_SUPERVISOR_ARGS | --autonomous --only-create | Args passed to watcher subprocess |
AUTO_WATCH_RESTART_BASE_MS | 2000 | Restart backoff base |
AUTO_WATCH_RESTART_MAX_MS | 30000 | Restart backoff cap |
AUTO_WATCH_MAX_RESTARTS | 8 | Max restart attempts before giving up |
AUTO_WATCH_STATUS_POLL_MS | 5000 | How often supervisor state is polled |
AUTO_WATCH_CLOSE_ALL_COMMAND | (none) | Shell command to close all positions |
API Routes
Watcher Supervisor
GET /api/watch/supervisor/status — Current watcher status + tracking state
POST /api/watch/supervisor/start — Start watcher subprocess
POST /api/watch/supervisor/stop — Stop watcher subprocess
POST /api/watch/supervisor/close-all — Close all tracked positions
Core
GET /api/health — RPC + Jito health check
GET /api/keypairs — List keypair files
GET /api/server-keys — List server-side public keys
POST /api/server-keys/session — Create HMAC signing session
POST /api/bundler/action — Execute bundle buy
POST /api/jupiter/quote — Jupiter v6 quote
POST /api/jupiter/swap — Jupiter v6 swap
POST /api/jupiter/send — Send pre-built swap tx
POST /api/fund/build — Build fund-wallets transaction
POST /api/keypairs/create — Create new keypair files
Watcher State Machine
desired=started → status: stopped → starting → running
desired=stopped → status: running → stopping → stopped
crash/exit → status: restarting (exponential backoff)
max restarts hit → status: stopped (permanent until server restart)
Reading watcher state
const trackingState = readJsonFile<WatchTrackingState>(WATCHER_TRACKING_STATE_FILE);
Watcher subprocess IPC
- No pipe/socket — state shared via JSON files in
tmp/
- Child writes
tmp/pump-watch-tracking-state.json every HEARTBEAT_MS (5s)
- Parent writes
tmp/pump-watch-supervisor-state.json on state transitions
- Status API reads both files on every request
Watcher Current Limitation (TODO)
scripts/task43-watch-pump-program.ts is a heartbeat stub:
- Writes state every 5s to keep supervisor green
- Does NOT subscribe to real pump.fun program events
- TODO: Replace with
connection.onLogs(PUMP_PROGRAM_ID, cb) or Geyser gRPC
Adding a New API Route
- Add route path to
STATE_MUTATING_ROUTES Set (if POST) in gui.ts:~78
- Add rate limit to
ROUTE_RATE_LIMITS map in gui.ts:~100
- Add handler in the dispatch chain at bottom of
gui.ts (after line ~3278):
if (method === "POST" && pathname === "/api/my/route") {
return res.end(JSON.stringify({ ok: true }));
}
- Error response pattern:
return respondApiError(res, 400, "INVALID_REQUEST", "message", requestId, pathname);
Safe Commit Workflow
npm run secret:verify
git add gui.ts package.json scripts/task43-watch-pump-program.ts .planning/ .gitignore
git commit -m "Your message"
Never stage:
config.ts if it contains real keypair values
.nano-banana-config.json (API keys)
src/keypairs/*.json
blockengine.json (if it contains real key)
tmp/*.json (runtime state)
Secret Guard Setup (per machine)
npm run secret:guard:setup
This activates .githooks/pre-commit so secrets are scanned before every commit.
Troubleshooting
| Symptom | Cause | Fix |
|---|
| GUI shows watcher "stopped" immediately after start | WATCHER_PROGRAM path wrong | Check AUTO_WATCH_SUPERVISOR_PROGRAM or verify scripts/task43-watch-pump-program.ts exists |
| Watcher restarts loop and stops | Crashes 8 times | Restart GUI server; check stderr in console |
/api/health shows RPC unreachable | Bad RPC_URL or network | Check RPC_URL env var |
| Rate limit 429 on bundler actions | Too many requests in 60s window | Check BUNDLER_ACTION_RATE_LIMIT_MAX env var |
ts-node: command not found | ts-node not installed | npm install |
| Port 3001 in use | Another GUI instance running | Kill the process or set GUI_PORT |