Supersedes #2224. Updates the transitive web lockfile entry for `qs` from 6.15.1 to 6.15.2 while keeping the current-main diff limited to the `node_modules/qs` metadata. Validation: - git diff --check - npm --prefix web ci --ignore-scripts - npm --prefix web run lint
codewhale-web
Community site for CodeWhale — lives at codewhale.net.
Next.js 15 (App Router) + Tailwind, deployed to Cloudflare Workers via @opennextjs/cloudflare. Curated "Today's Dispatch" content is regenerated every 6 hours by a Cloudflare Cron Trigger that calls deepseek-v4-flash to summarise recent repo activity, and stored in Workers KV.
Local dev
cd web
npm install
cp .env.example .env.local # fill in the keys you have
npm run dev # http://localhost:3000
Required env (only for the curator + private-repo rate limits):
| Variable | What | Required? |
|---|---|---|
DEEPSEEK_API_KEY |
DeepSeek platform key (sk-...) |
only for /api/cron?task=curate |
GITHUB_TOKEN |
Fine-grained PAT, public-repo read scope | optional (raises rate limit) |
GITHUB_REPO |
Defaults to Hmbown/CodeWhale |
optional |
CRON_SECRET |
Shared secret for manual cron invocation | optional |
The site renders fine without any of them — Today's Dispatch falls back to a static editorial; the GitHub feed shows "feed not yet loaded".
Deploy to Cloudflare
You already own codewhale.net on Cloudflare and have a Workers Paid plan. The deploy is two steps:
-
Provision KV namespaces once:
npx wrangler kv namespace create CURATED_KV npx wrangler kv namespace create NEXT_INC_CACHE_KVCopy the printed
idvalues into the matchingwrangler.jsoncbindings (replace eachREPLACE_WITH_KV_ID). -
Set secrets and deploy:
npx wrangler secret put DEEPSEEK_API_KEY npx wrangler secret put GITHUB_TOKEN # optional npx wrangler secret put CRON_SECRET # optional, for manual /api/cron?task=curate hits npm run deploy # builds with OpenNext + uploads -
Point the domain: in the Cloudflare dashboard, add a Worker route for
codewhale.net/*→ the deployed Worker (currently nameddeepseek-tui-webunless the Worker is renamed during deploy).
The first cron run happens within 6 hours; you can also kick it manually:
curl -H "x-cron-secret: $CRON_SECRET" "https://codewhale.net/api/cron?task=curate"
What's where
web/
├── app/
│ ├── layout.tsx root layout, font loading
│ ├── page.tsx home — hero, dispatch, stats, how-it-works, join
│ ├── globals.css design system: paper grain, hairlines, type, seal
│ ├── install/page.tsx per-OS install with auto-detection
│ ├── docs/page.tsx modes / tools / approval / config / mcp / providers
│ ├── feed/page.tsx live mirror of issues + PRs
│ ├── roadmap/page.tsx shipped / underway / considered / ruled out
│ ├── contribute/page.tsx how to PR + house rules + dev loop
│ └── api/
│ ├── cron/route.ts manual cron trigger: GitHub → DeepSeek → KV
│ └── github/feed/route.ts cached JSON endpoint
├── components/
│ ├── nav.tsx sticky header w/ date strip + CJK accents
│ ├── footer.tsx dense 5-column footer
│ ├── seal.tsx red Chinese-seal mark used as section anchor
│ ├── ticker.tsx animated live activity strip
│ ├── stat-grid.tsx tabular repo stats row
│ ├── feed-card.tsx one issue/PR card
│ └── install-tabs.tsx client component, OS auto-detect + copy
├── lib/
│ ├── types.ts shared types
│ ├── github.ts REST client + relative-time formatter
│ ├── deepseek.ts v4-flash chat client + curate() prompt
│ └── kv.ts Cloudflare KV access via OpenNext bindings
├── wrangler.jsonc CF Worker config + cron + KV binding
├── open-next.config.ts OpenNext adapter config
└── tailwind.config.ts design tokens
Aesthetic
"Yamen tech": Qing memorial document × WeChat news feed × Bloomberg terminal.
- Palette: cream paper
#FAF6EE, ink#0A2540, cinnabar red#C8102E, aged gold, jade green, cobalt blue. - Type: Fraunces (display), IBM Plex Sans (body), JetBrains Mono (UI/code), Noto Serif SC (decorative CJK anchors).
- Structure: hairline 1px dividers, multi-column grids, big tabular numbers, surgical use of red for "hot" markers, decorative Chinese-seal squares as section anchors.
If you want to retune the palette, edit :root in app/globals.css and the colors block in tailwind.config.ts.