Cloudflare Worker that orchestrates Cursor cloud agents for the popped.dev design mode. The static Next.js export has no server runtime; this Worker exposes /api/agent/* routes consumed by the browser.
| Route | Purpose |
|---|---|
POST /api/agent/session |
Create or resume a visitor session (sessionId → agentId) |
POST /api/agent/message |
Send user message + element context; returns runId |
GET /api/agent/runs/:runId/stream |
SSE: assistant text, status, previewUrl |
GET /api/agent/runs/:runId |
Poll fallback |
POST /api/agent/runs/:runId/submit |
Merge design branch to main + attribution |
Implementation uses the Cursor Cloud Agents REST API (fetch to api.cursor.com) for Worker compatibility. Same repo settings as @cursor/sdk cloud agents.
- Repo:
https://github.com/parse-nip/popped-dev - Branch:
cursor/design/{sessionId}(workOnCurrentBranch: true) - Phase 2:
autoCreatePR: falseon create - Phase 3: submit endpoint merges the design branch to
mainvia GitHub API
System prompts embed AGENTS.md rules (facts from experience.json only, style LockedResume.tsx / globals.css, never edit locked files).
When the cloud agent pushes branch cursor/design/{sessionId}, Cloudflare Pages builds a preview deployment. Branch alias URL:
https://cursor-design-{sessionId-prefix}.popped-dev.pages.dev
Normalization: / → -, lowercase, truncate to ~28 chars. The Worker also reads the exact URL from GitHub Cloudflare Pages check runs when available.
Hash-based preview URLs (abc123.popped-dev.pages.dev) are commit-specific; branch aliases update on each push — use aliases for the design iframe.
cd workers/agent-api
npm install
npx wrangler dev
# Worker on http://127.0.0.1:8787In the Next.js app (repo root):
NEXT_PUBLIC_AGENT_API_URL=http://127.0.0.1:8787 npm run devWithout NEXT_PUBLIC_AGENT_API_URL, the client uses same-origin /api/agent/* (production Worker route).
Set via wrangler secret put or the Cloudflare dashboard:
| Name | Required | Description |
|---|---|---|
CURSOR_API_KEY |
Yes | Cursor team service-account API key |
GITHUB_TOKEN |
Yes | GitHub PAT with Contents: read and write on parse-nip/popped-dev (merge + branches; no PR scope needed) |
SESSIONS KV |
Yes | Session + run metadata + rate limits |
wrangler.toml vars (override in dashboard if needed):
| Var | Default | Description |
|---|---|---|
PAGES_PROJECT_NAME |
popped-dev |
CF Pages project for preview URL construction |
GITHUB_REPO_URL |
https://github.com/parse-nip/popped-dev |
Cloud agent repo |
GITHUB_DEFAULT_BRANCH |
main |
Base ref when creating session branches |
CORS_ORIGIN |
https://popped.dev |
Allowed origin (localhost allowed in dev) |
GITHUB_TOKEN is required because Cursor's workOnCurrentBranch: true needs the session branch (cursor/design/{sessionId}) to already exist on GitHub. The Worker creates it from GITHUB_DEFAULT_BRANCH before calling the Cursor API.
npx wrangler kv namespace create SESSIONS
# Update id in wrangler.tomlnpx wrangler secret put CURSOR_API_KEY
npx wrangler secret put GITHUB_TOKEN
npx wrangler deployAttach the Worker to your zone, e.g.:
popped.dev/api/*→popped-dev-agent-api
Or use a subdomain: api.popped.dev/* and set NEXT_PUBLIC_AGENT_API_URL=https://api.popped.dev on the Pages project.
See docs/TESTING-AND-RESET.md and scripts/reset-test-state.sh.
- Contributor name required (matches
popped.dev:contributor-namein localStorage) - Rate limit: 5 runs/hour per IP + session (KV)
- Merge cooldown: 30 minutes per IP after publishing — blocks new cloud agents (one agent per Cursor chat)
CURSOR_API_KEYnever sent to the browser
A cron trigger runs hourly (0 * * * *) and removes abandoned design sessions:
| What | When |
|---|---|
cursor/design/* Git branch |
No activity for CLEANUP_TTL_MS (default 1 hour) |
| Session + run KV keys | Same TTL, refreshed on each message |
| Branches with open PRs | Kept (also marked protected on submit) |
| Orphan branches (no KV session) | Deleted if no open PR |
Tune TTL in wrangler.toml:
CLEANUP_TTL_MS = "3600000" # 1 hour in msManual reset still available via scripts/reset-test-state.sh.
- Cursor dashboard: GitHub integration authorized for
parse-nip/popped-dev CURSOR_API_KEYstored as Worker secret- Cloudflare Pages ↔ GitHub connected; build
npm run build, outputout - Worker route
popped.dev/api/*configured - KV namespace bound as
SESSIONS
wrangler dev+ Next dev withNEXT_PUBLIC_AGENT_API_URL- Design mode → select resume heading → send chat message
- Agent pushes branch → Pages preview builds → iframe shows changes
- Publish → merge to main → live site rebuilds
npm run verify:lockedpasses on PR branch