Skip to content

Pipelex/template-chatbot-claudecode

Repository files navigation

Claude Code Chatbot

A tiny Next.js chat UI for the claude-code-sandbox-blaxel sandbox. Built with assistant-ui.

The chatbot is a thin client: it gets a private preview to your already-deployed sandbox and proxies messages through. Provider auth (Anthropic / Bedrock) lives in the sandbox repo, not here.

Prerequisites

⚠️ This template requires the claude-code-sandbox-blaxel sandbox to be deployed to Blaxel first. The chatbot is a thin client — it does not bundle the sandbox. Deploying this template alone will not give you a working app.

  1. Install Blaxel CLI and log in:
    bl login YOUR-WORKSPACE
  2. Deploy the sandbox once, with your provider creds:
    git clone https://github.com/pipelex/claude-code-sandbox-blaxel.git
    cd claude-code-sandbox-blaxel
    cp .env.sample .env   # fill ANTHROPIC_API_KEY (or Bedrock vars)
    bl deploy

    ⚠️ Do not commit .env — it contains your API key. Confirm it's in .gitignore before you push.

Run locally

npm install
bl serve --hotreload

Open http://127.0.0.1:1338.

Deploy

bl deploy

That deploys only the chatbot — it assumes the sandbox is already deployed (see Prerequisites).

Deploy both at once (chatbot + sandbox)

If you're standing this up for the first time, run the convenience script:

./scripts/deploy-all.sh

It clones claude-code-sandbox-blaxel next to this repo (if missing), deploys the sandbox, then deploys the chatbot. Override the sandbox location with SANDBOX_DIR=..., or skip the sandbox step with SKIP_SANDBOX=1. The script refuses to deploy the sandbox if its .env (with ANTHROPIC_API_KEY) is missing.

The sandbox is intentionally a separate git repo, not vendored here. One sandbox can back multiple chatbot deployments; upgrading the sandbox image doesn't force a chatbot redeploy.

Access modes

Out of the box, the deployed chatbot is gated by Blaxel platform auth — hitting its URL without a Blaxel bearer token returns 401. This is the secure default and the right choice for a private demo or an internal tool.

If you want anyone with the link to be able to use it, opt into public access explicitly — you accept the token-burn risk in exchange for the convenience:

  1. Add public = true at the top of blaxel.toml.
  2. Set PUBLIC_ACCESS=1 in the chatbot's env (Blaxel dashboard → Environment). This activates the IP-based rate limiter and the stricter per-session message cap; without it the limiter assumes you're behind auth and doesn't trust x-forwarded-for headers.
  3. Redeploy: bl deploy.

Rate-limit knobs (env vars, all have safe defaults):

Variable Default (public) Default (gated) Purpose
RATE_LIMIT_MESSAGES_PER_SESSION 30 500 Lifetime message cap per sessionId.
RATE_LIMIT_MESSAGES_PER_IP 60 1000 Rolling-window cap per source IP.
RATE_LIMIT_IP_WINDOW_MS 3600000 (1h) same Window length for the per-IP cap.
MAX_CONTENT_BYTES 16384 same Hard cap on a single message body.

When a limit trips, the API returns 429 with a structured { error: { code, message, retryAfterSeconds? } } body and a Retry-After header where applicable; the UI renders a friendly message instead of a bare status code.

Single-replica only. The limiter is in-process. If you scale the chatbot to multiple replicas, swap the in-memory Maps in src/lib/rate-limit.ts for a shared backend (Redis, DDB, Upstash) — the public surface (checkRateLimit) stays the same.

API endpoints

  • GET /api/health — returns 200 { status: "ok" } when the sandbox is reachable and a preview can be minted; 503 { status, code, message } otherwise. Useful for uptime probes and CI smoke tests.
  • POST /api/chat{ sessionId, content }. Streams SSE on success; returns { error: { code, message, … } } on failure with one of the documented error codes (SANDBOX_NOT_FOUND, SESSION_MESSAGE_LIMIT, IP_MESSAGE_LIMIT, CONTENT_TOO_LARGE, SANDBOX_UNREACHABLE, …).

What's in here

src/
├── app/
│   ├── page.tsx                  # mounts <Chat />
│   ├── layout.tsx
│   ├── globals.css
│   └── api/
│       ├── chat/route.ts         # SSE proxy → sandbox, with rate limiting
│       └── health/route.ts       # liveness/preflight check
├── components/Chat.tsx           # assistant-ui Thread + custom adapter
├── lib/
│   ├── sandbox.ts                # resolve sandbox preview + token (typed errors)
│   ├── skills.ts                 # push local skill files into the sandbox
│   └── rate-limit.ts             # per-session + per-IP message caps
└── skills/
    └── tldr/SKILL.md             # demo skill — summarize anything
scripts/
└── deploy-all.sh                 # one-shot deploy for sandbox + chatbot

Skills

On the first chat request, every file under src/skills/ is pushed into the sandbox via sandbox.fs.write at /home/agent/.claude/skills/<same-path>. The Claude Agent SDK auto-discovers any folder containing SKILL.md via settingSources: ["user", "project"]no image rebuild, no plugin install.

Add your own skill: drop a folder under src/skills/<name>/ containing a SKILL.md (with YAML frontmatter name: and description:), and restart. No code changes needed — src/lib/skills.ts walks the directory.

Try the demo: paste a long article into the chat and ask "tldr?" — the tldr skill activates and Claude returns a tight one-sentence headline plus 3–5 supporting bullets.

⚠️ Heads up — the push overwrites existing skills in the sandbox. On the first chat request, the template pushes everything under src/skills/ into /home/agent/.claude/skills/ and replaces whatever is already there. If you previously installed skills into the same sandbox by other means (e.g. npx skills add, claude plugin install, or manual sandbox.fs.write calls), deploying this template will wipe them — you'll be left with only the skills shipped under src/skills/. Move any skills you want to keep into src/skills/ before deploying, or point the template at a different sandbox.

License

MIT

About

template-chatbot-claudecode

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors