From a7b38ff8c1af1f7f6cc5743b0fd217212b0c09bb Mon Sep 17 00:00:00 2001 From: "Jhonattan L. Jimenez" Date: Sun, 13 Sep 2026 16:19:08 +0000 Subject: [PATCH] docs: elevate ChatForge with v4.0 identity (Cloudflare Workers AI) - package.json: public package (chatforge), files, publishConfig - .npmignore: excludes wrangler.toml, node_modules, env files - release.yml: GitHub Release on tag push with auto notes - docker-publish.yml: GHCR Docker publish on release - README: v4.0 visual identity (violet/cyan), install-from-source, package badges, architecture diagram --- .github/workflows/docker-publish.yml | 32 +++++ .github/workflows/release.yml | 19 +++ .npmignore | 13 ++ README.md | 192 ++++++++++++++------------- package.json | 83 ++++++------ 5 files changed, 212 insertions(+), 127 deletions(-) create mode 100644 .github/workflows/docker-publish.yml create mode 100644 .github/workflows/release.yml create mode 100644 .npmignore diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..b7d44ef --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,32 @@ +name: Docker Publish (GHCR) +on: + release: + types: [published] +permissions: + contents: read + packages: write +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/metadata-action@v5 + id: meta + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + - uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..991beeb --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,19 @@ +name: Release +on: + push: + tags: + - 'v*' +permissions: + contents: write +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + fail_on_unmatched_files: false diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..853c121 --- /dev/null +++ b/.npmignore @@ -0,0 +1,13 @@ +# Cloudflare +wrangler.toml +.env* + +# Dependencies +node_modules/ + +# Build artifacts +dist/ + +# Misc +.DS_Store +*.log diff --git a/README.md b/README.md index b4ae095..38bb553 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,30 @@
-![ChatForge banner](docs/assets/banner.svg) +![ChatForge](docs/assets/banner.svg) # ChatForge -**Edge-native AI chat interface** — a single Cloudflare Worker serving a streaming chat UI powered by Workers AI, with no origin server. +**Edge-native AI chat interface — single Cloudflare Worker, Workers AI, streaming SSE, no origin server** -Stars -Last commit -License -TypeScript -Cloudflare Workers -SSE streaming +[![GitHub release](https://img.shields.io/github/v/release/OneByJorah/ChatForge?color=3178C6&label=release&logo=github)](https://github.com/OneByJorah/ChatForge/releases) +[![Cloudflare Workers](https://img.shields.io/badge/Cloudflare%20Workers-F38020?style=flat-square&logo=cloudflare&logoColor=white)](https://developers.cloudflare.com/workers/) +[![Workers AI](https://img.shields.io/badge/Workers%20AI-8B8CFF?style=flat-square&logo=cloudflare&logoColor=white)](https://developers.cloudflare.com/workers-ai/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?color=FFB300&logo=open-source-initiative&logoColor=FFB300)](https://opensource.org/licenses/MIT) +[![Docker](https://img.shields.io/badge/Docker-2496ED?style=flat-square&logo=docker&logoColor=white)](https://www.docker.com/) +[![SSE Streaming](https://img.shields.io/badge/SSE-6366f1?style=flat-square&logo=socket.io&logoColor=white)](https://html.spec.whatwg.org/multipage/networking.html#server-sent-events) + +
![ChatForge screenshot](docs/assets/screenshot.png) - +## What This Is + +ChatForge is a reference implementation of an edge-hosted chat application: a static frontend plus a streaming `/api/chat` endpoint, both running inside a single Cloudflare Worker and calling Workers AI through a binding. There is no backend server, no API key to manage, and no cold-start regional infrastructure. + +Built for developers who want a clean starting point to fork and extend for their own product. + +> [!NOTE] +> ChatForge is intentionally minimal: **no authentication, no persistence, and no rate limiting**. Chat history lives in browser memory and resets on refresh. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the production upgrade path. ## Quick Start @@ -29,121 +38,126 @@ npm run dev Open **http://localhost:8787**. Run `npm run deploy` to publish to Cloudflare Workers. -## What This Is +## Install -ChatForge is a reference implementation of an edge-hosted chat app: a static frontend plus a streaming `/api/chat` endpoint, both running inside one Cloudflare Worker and calling Workers AI through a binding. There is no backend server, no API key to manage, and no cold-start regional infrastructure. It exists as a clean starting point you can fork and extend for your own product. +### From npm -> [!NOTE] -> ChatForge is intentionally minimal: **no authentication, no persistence, and no rate limiting**. Chat history lives in browser memory and resets on refresh. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the production upgrade path. +```bash +npm install chatforge +``` -## Features +### From Source -- **Workers AI inference** — runs `@cf/meta/llama-3.1-8b-instruct-fp8` at the edge with no API keys. -- **Real-time streaming** — Server-Sent Events (SSE) deliver tokens as they are generated. -- **Single-Worker deploy** — frontend and API ship together with one `wrangler deploy`. -- **Framework-free frontend** — vanilla HTML/CSS/JS keeps the whole UI small. -- **Security headers** — CSP, HSTS, `X-Frame-Options`, `nosniff`, referrer, and permissions policies on every response. -- **Input validation** — server-side limits on message shape, count (max 100), and length (max 32,000 chars). -- **Swappable model** — change `MODEL_ID` in `src/index.ts` to any Workers AI text-generation model. -- **Docker preview** — serve the static UI locally with `docker compose up` for a quick look. +```bash +git clone https://github.com/OneByJorah/ChatForge.git +cd ChatForge +npm install +npm run dev +``` -## Architecture +### Docker (local preview) +```bash +git clone https://github.com/OneByJorah/ChatForge.git +cd ChatForge +docker compose up ``` -Browser ──SSE──▶ Cloudflare Worker (src/index.ts) - │ - ├── GET /* ──▶ env.ASSETS.fetch() ──▶ public/ (static files) - │ - └── POST /api/chat ──▶ env.AI.run() ──▶ Workers AI (Llama 3.1 8B) - │ - └── text/event-stream response + +### Cloudflare Workers (production) + +```bash +git clone https://github.com/OneByJorah/ChatForge.git +cd ChatForge +npm install +npx wrangler login +npm run deploy ``` -Chat history is kept in browser memory only. The Worker is stateless. +## Features -## API +- **Workers AI inference** — `@cf/meta/llama-3.1-8b-instruct-fp8` at the edge, no API keys needed +- **Real-time SSE streaming** — tokens delivered as they are generated +- **Single-Worker deploy** — frontend + API with one `wrangler deploy` +- **Framework-free frontend** — vanilla HTML/CSS/JS, ~5.65 KiB gzipped +- **Security headers** — CSP, HSTS, `X-Frame-Options`, `nosniff`, referrer, permissions policy +- **Input validation** — message shape, count (max 100), length (max 32,000 chars) +- **Swappable model** — change `MODEL_ID` in `src/index.ts` to any Workers AI text-generation model +- **Docker preview** — serve static UI locally with `docker compose up` -| Endpoint | Method | Description | -|----------|--------|-------------| -| `/api/chat` | `POST` | Send `{ "messages": [...] }`, receive an SSE-streamed reply | -| `/*` (non-API) | `GET` | Static frontend from `public/` | +## Tech Stack -Errors: `400` invalid JSON/messages, `405` wrong method, `404` unknown API route, `500` AI failure. Full details in [docs/API.md](docs/API.md). +- **Runtime** — Cloudflare Workers +- **AI** — Workers AI (`@cf/meta/llama-3.1-8b-instruct-fp8`) +- **Frontend** — Vanilla HTML/CSS/JavaScript, SSE +- **Deployment** — `wrangler deploy`, Docker Compose for local preview +- **Config** — `wrangler.jsonc` (compatibility date 2025-10-08, nodejs_compat, global_fetch_strictly_public) -## Configuration +## Package Badges -No runtime secrets are needed — the AI binding is declared in [`wrangler.jsonc`](wrangler.jsonc). Deployment credentials are read from the environment (never committed): +[![GitHub release](https://img.shields.io/github/v/release/OneByJorah/ChatForge?color=3178C6&label=release&logo=github)](https://github.com/OneByJorah/ChatForge/releases) +[![Cloudflare Workers](https://img.shields.io/badge/Cloudflare%20Workers-F38020?style=flat-square&logo=cloudflare&logoColor=white)](https://developers.cloudflare.com/workers/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?color=FFB300&logo=open-source-initiative&logoColor=FFB300)](https://opensource.org/licenses/MIT) -| Variable | Required | Description | -|----------|----------|-------------| -| `CLOUDFLARE_API_TOKEN` | For deploy | Cloudflare API token used by Wrangler | -| `CLOUDFLARE_ACCOUNT_ID` | For deploy | Cloudflare account ID | +## Configuration -See [.env.example](.env.example). +Edit `wrangler.jsonc` to customize: -## Supported Models +| Setting | Description | +|---------|-------------| +| `name` | Worker name (default: `chatforge`) | +| `main` | Entry point (default: `src/index.ts`) | +| `compatibility_date` | Workers compatibility date | +| `assets.directory` | Static frontend directory (default: `./public`) | +| `ai.binding` | Workers AI binding (default: `AI`) | -| Provider | Model | -|----------|-------| -| **Cloudflare Workers AI** | `@cf/meta/llama-3.1-8b-instruct-fp8` (default, swappable via `MODEL_ID` in [src/index.ts](src/index.ts)) | +Environment variables (set via `wrangler secret` or `.dev.vars`): -Browse the [Workers AI model catalog](https://developers.cloudflare.com/workers-ai/models/) — any text-generation model ID can be dropped in. +| Variable | Description | +|----------|-------------| +| `MODEL_ID` | Workers AI model ID (default: `@cf/meta/llama-3.1-8b-instruct-fp8`) | -## Docker Preview +## Architecture -```bash -docker compose up -d # http://localhost:8787 +``` +Browser ──HTTPS──> Cloudflare Worker (src/index.ts) + │ + ├── GET /* ──▶ env.ASSETS.fetch() ──▶ public/ (static files) + │ + └── POST /api/chat ──> env.AI.run() ──> Workers AI (Llama 3.1 8B) + │ + └── stream ──> text/event-stream response ``` -> [!WARNING] -> The Docker image previews only the static frontend. The `/api/chat` endpoint requires `wrangler dev` or a deployed Worker because it depends on the `env.AI` binding. +See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for full architecture details and upgrade path. -## Use Cases +## API Reference -1. **Starter template** — fork it as the base for a custom edge chat product. -2. **Learning Workers AI** — see streaming, bindings, and security headers in ~one file. -3. **Internal tools** — deploy a no-backend assistant for quick Q&A. -4. **Prototyping** — test prompts against Llama 3.1 at the edge with zero infrastructure. +See [docs/API.md](docs/API.md) for the full API reference. -## Tech Stack +### `POST /api/chat` -Cloudflare Workers, Workers AI, TypeScript, vanilla HTML/CSS/JS, Server-Sent Events, Wrangler, Vitest, Docker (nginx:alpine preview). +Send a chat message and receive a streaming AI response. -## Screenshots +**Request:** +```json +{ + "messages": [ + { "role": "system", "content": "You are a helpful assistant." }, + { "role": "user", "content": "Hello!" } + ] +} +``` -| View | Preview | -|------|---------| -| Chat UI | ![ChatForge UI](docs/screenshots/chatforge-ui.png) | -| Conversation | ![ChatForge conversation](docs/screenshots/chat-conversation.png) | -| Mobile | ![ChatForge mobile view](docs/screenshots/main.mobile.png) | -| Main viewport | ![ChatForge main view](docs/screenshots/main.viewport.png) | +**Response:** `text/event-stream` — SSE stream with `data:` lines, ends with `data: [DONE]` -## Project Structure +### `GET /` or any non-API path -``` -ChatForge/ -├── src/ # Worker source -│ ├── index.ts # Routing + chat API -│ ├── types.ts # Type definitions -│ └── __tests__/ # Vitest suite -├── public/ # index.html + chat.js -├── wrangler.jsonc # Cloudflare config -├── Dockerfile # Static UI preview image -├── docker-compose.yml # Local static preview -├── docs/ # API.md, ARCHITECTURE.md, assets -└── package.json -``` +Serves static frontend assets from the `public/` directory. ## Contributing -Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), then [open an issue](https://github.com/OneByJorah/ChatForge/issues) or a pull request. +Contributions are welcome. [Open an issue](https://github.com/OneByJorah/ChatForge/issues) to report a bug or request a feature. ## License MIT — see [LICENSE](LICENSE). - -## Connect - -- [jorahone.com](https://jorahone.com) -- [GitHub Org](https://github.com/OneByJorah) -- [info@jorahone.com](mailto:info@jorahone.com) diff --git a/package.json b/package.json index 4757e99..42d4d33 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,46 @@ { - "name": "ChatForge", - "description": "A simple chat application powered by Cloudflare Workers AI", - "cloudflare": { - "label": "LLM Chat App", - "products": [ - "Workers", - "Workers AI", - "AI Gateway" - ], - "categories": [ - "ai" - ], - "docs_url": "https://developers.cloudflare.com/workers-ai/", - "preview_icon_url": "https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/a974bd55-20c6-4656-fa29-1551d43ddc00/public", - "preview_image_url": "https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/65dd8441-89d3-491d-c5a1-0a000d070700/preview", - "publish": true - }, - "devDependencies": { - "@cloudflare/vitest-pool-workers": "^0.22.0", - "@cloudflare/workers-types": "^5.20260822.1", - "@types/node": "^26.2.0", - "typescript": "^7.0.2", - "vitest": "^4.1.11", - "wrangler": "^4.125.0" - }, - "private": true, - "scripts": { - "cf-typegen": "wrangler types", - "check": "tsc --noEmit && wrangler deploy --dry-run", - "deploy": "wrangler deploy", - "dev": "wrangler dev", - "start": "wrangler dev", - "test": "vitest", - "typecheck": "tsc --noEmit", - "lint": "tsc --noEmit", - "format": "tsc --noEmit" - } -} + "name": "chatforge", + "description": "A simple chat application powered by Cloudflare Workers AI", + "cloudflare": { + "label": "LLM Chat App", + "products": [ + "Workers", + "Workers AI", + "AI Gateway" + ], + "categories": [ + "ai" + ], + "docs_url": "https://developers.cloudflare.com/workers-ai/", + "preview_icon_url": "https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/a974bd55-20c6-4656-fa29-1551d43ddc00/public", + "preview_image_url": "https://imagedelivery.net/wSMYJvS3Xw-n339CbDyDIA/65dd8441-89d3-491d-c5a1-0a000d070700/preview", + "publish": true + }, + "devDependencies": { + "@cloudflare/vitest-pool-workers": "^0.22.0", + "@cloudflare/workers-types": "^5.20260822.1", + "@types/node": "^26.2.0", + "typescript": "^7.0.2", + "vitest": "^4.1.11", + "wrangler": "^4.125.0" + }, + "scripts": { + "cf-typegen": "wrangler types", + "check": "tsc --noEmit && wrangler deploy --dry-run", + "deploy": "wrangler deploy", + "dev": "wrangler dev", + "start": "wrangler dev", + "test": "vitest", + "typecheck": "tsc --noEmit", + "lint": "tsc --noEmit", + "format": "tsc --noEmit" + }, + "version": "1.0.0", + "files": [ + "public/", + "src/" + ], + "publishConfig": { + "access": "public" + } +} \ No newline at end of file