Skip to content

Commit 24ab785

Browse files
v0.7.0
# v0.7.0 — Craft CLI, Headless Server & Architecture Overhaul --- ## Craft CLI A new terminal client for running agent sessions from the command line — no GUI required. - **`craft-cli run`** — launch agent sessions directly from your terminal with prompt input and streaming output - **Multi-provider support** — select between multiple LLM connections and models from the CLI - **CI workspace support** — create and manage workspaces programmatically for automated workflows - **`--validate-server`** — verify server configuration with prettified diagnostic output - **Redesigned spinner** — stays visible during agent thinking for better terminal UX ### Why it's cool This is the first step toward full programmatic control of Craft Agents. Script your agent workflows, integrate into CI/CD pipelines, or run sessions headlessly on remote machines. Partially addresses [#343](#343). ## Headless Server Run Craft Agents as a standalone server without the Electron desktop app — deploy on any machine, connect from any client. - **Standalone Bun server** — headless entry point that runs without Electron, with sharp eliminated from the bundle - **TLS support** — secure remote connections with automatic certificate handling - **Multi-platform build script** — distribution packaging for Linux, macOS, and Windows - **Remote mode via env vars** — configure headless operation through environment variables - **Thin-client connection state** — WebSocket connection status and failure banners in the client ### Why it's cool Deploy Craft Agents on a remote server and connect from anywhere. Run it on your home lab, a cloud VM, or a CI runner. The thin-client mode means the browser UI stays lightweight while all the heavy lifting happens server-side. Partially addresses [#291](#291). ## Transport & Architecture The entire inter-process communication layer has been rebuilt from the ground up. - **WebSocket-only RPC** — replaced Electron IPC with WebSocket RPC for all communication, enabling remote operation - **Bidirectional RPC** — typed push events, server-owned OAuth token management - **Server-core extraction** — 14 core handlers, session management, services, and domain utilities extracted into `@craft-agent/server-core` for reuse across Electron and headless - **Protocol formalization** — channels, DTOs, and event maps extracted to `@craft-agent/shared/protocol` with wire-format stability tests - **Split-brain cleanup** — removed dead handler/session copies, added `IWindowManager` and channel advertisement ### Why it's cool This is the foundation that makes CLI, headless server, and future remote/mobile clients possible. The architecture is now cleanly split between reusable server-core logic and platform-specific shells (Electron, Bun server, CLI). ## Editor & UI - **Rich block interactions** — tiptap image support, slash menu for inserting content blocks, and theme fallback - **Model fallback chain** — automatic fallback between models when one is unavailable, with improved playground theming - **Browser tooling** — better lifecycle management, metadata badge components, and safe-mode boundaries ## Security - **Block unencrypted WebSocket** — remote connections now require `wss://` (TLS); plaintext `ws://` is rejected - **Certificate generation** — fixed curve name in cert generation script ## Bug Fixes - **Pi 3-tier model persistence** — fixed model save/hydration for Best/Balanced/Fast tiers. Fixes [#336](#336) - **Multi-panel input focus** — fixed focus scoping and race handling that caused keystrokes to jump between panels. Fixes [#339](#339) - **Pi Azure base URL** — propagate Azure base URL to Pi subprocess - **Deep-link routing** — corrected deep-link client targeting with regression tests - **Split-turn expansion** — fixed TurnCard expansion key collisions - **Explore mode false positive** — no longer flags node arrow syntax (`=>`) as a write operation - **Onboarding loop** — fixed onboarding always showing + silent connection save failures - **Copilot model listing** — use GitHub OAuth token for CopilotClient model listing - **Label badge overlap** — prevent premature label badge overlap in UI - **Transport hardening** — hardened WebSocket startup routing, codec, and session watcher isolation - **OAuth fixes** — open OAuth browser locally via `shell.openExternal`, accept `/oauth/callback` path - **Event payloads** — consistent `sources:changed` and `skills:changed` payload shapes - **Global config guard** — ensure `config.json` exists before handler registration
1 parent 9e0b8fb commit 24ab785

File tree

392 files changed

+34414
-8384
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

392 files changed

+34414
-8384
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Validate Server (Integration)
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
validate-server:
8+
runs-on: ubuntu-latest
9+
timeout-minutes: 15
10+
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Setup Bun
16+
uses: oven-sh/setup-bun@v2
17+
with:
18+
bun-version: latest
19+
20+
- name: Install dependencies
21+
run: bun install --frozen-lockfile
22+
23+
- name: Run --validate-server
24+
env:
25+
ANTHROPIC_API_KEY: ${{ secrets.CRAFT_ANTHROPIC_API_KEY }}
26+
run: bun run apps/cli/src/index.ts --validate-server --no-spinner

.github/workflows/validate.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Validate
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: validate-${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
validate:
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Setup Bun
23+
uses: oven-sh/setup-bun@v2
24+
with:
25+
bun-version: latest
26+
27+
- name: Install uv
28+
uses: astral-sh/setup-uv@v5
29+
30+
- name: Install dependencies
31+
run: bun install --frozen-lockfile
32+
33+
- name: Run validation suite
34+
run: bun run validate:ci

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ id_ed25519*
6262
# Credentials files
6363
credentials.enc
6464
*.credentials
65+
66+
# act (local GitHub Actions runner)
67+
.secrets
68+
.actrc

README.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,205 @@ Use **SHIFT+TAB** to cycle through modes in the chat interface.
147147
| `Enter` | Send message |
148148
| `Shift+Enter` | New line |
149149

150+
## Remote Server (Headless)
151+
152+
Craft Agents can run as a headless server on a remote machine (e.g., a Linux VPS), with the desktop app connecting as a thin client. This lets you keep long-running sessions alive, access them from multiple machines, and run compute-heavy tasks on a powerful server.
153+
154+
### Quick Start
155+
156+
From the monorepo root:
157+
158+
```bash
159+
# Generate a token and start the server
160+
CRAFT_SERVER_TOKEN=$(openssl rand -hex 32) bun run packages/server/src/index.ts
161+
```
162+
163+
The server prints the connection details on startup:
164+
165+
```
166+
CRAFT_SERVER_URL=ws://203.0.113.5:9100
167+
CRAFT_SERVER_TOKEN=<generated-token>
168+
```
169+
170+
Copy these values and use them to connect the desktop app.
171+
172+
### Connecting the Desktop App
173+
174+
Launch the Electron app in thin-client mode by passing the server URL and token:
175+
176+
```bash
177+
CRAFT_SERVER_URL=wss://203.0.113.5:9100 CRAFT_SERVER_TOKEN=<token> bun run electron:start
178+
```
179+
180+
In thin-client mode, the desktop app renders the UI but all session logic, tool execution, and LLM calls run on the remote server.
181+
182+
### Environment Variables
183+
184+
| Variable | Required | Default | Description |
185+
|----------|----------|---------|-------------|
186+
| `CRAFT_SERVER_TOKEN` | Yes || Bearer token for client authentication |
187+
| `CRAFT_RPC_HOST` | No | `127.0.0.1` | Bind address (`0.0.0.0` for remote access) |
188+
| `CRAFT_RPC_PORT` | No | `9100` | Bind port |
189+
| `CRAFT_RPC_TLS_CERT` | No || Path to PEM certificate file (enables `wss://`) |
190+
| `CRAFT_RPC_TLS_KEY` | No || Path to PEM private key file (required with cert) |
191+
| `CRAFT_RPC_TLS_CA` | No || Path to PEM CA chain file (optional, for client cert verification) |
192+
| `CRAFT_DEBUG` | No | `false` | Enable debug logging |
193+
194+
### TLS (Recommended for Remote Access)
195+
196+
When exposing the server over the network, TLS encrypts the WebSocket connection (`wss://` instead of `ws://`).
197+
198+
**Generate a self-signed certificate (development/testing):**
199+
200+
```bash
201+
./scripts/generate-dev-cert.sh
202+
# Creates certs/cert.pem and certs/key.pem (valid 365 days)
203+
```
204+
205+
**Start the server with TLS:**
206+
207+
```bash
208+
CRAFT_SERVER_TOKEN=<token> \
209+
CRAFT_RPC_HOST=0.0.0.0 \
210+
CRAFT_RPC_TLS_CERT=certs/cert.pem \
211+
CRAFT_RPC_TLS_KEY=certs/key.pem \
212+
bun run packages/server/src/index.ts
213+
```
214+
215+
The server will print `CRAFT_SERVER_URL=wss://<your-public-ip>:9100`.
216+
217+
**For production**, use certificates from a trusted CA (e.g., Let's Encrypt) or place the server behind a reverse proxy (nginx, Caddy) that terminates TLS.
218+
219+
### Docker
220+
221+
```bash
222+
docker run -d \
223+
-p 9100:9100 \
224+
-e CRAFT_SERVER_TOKEN=<token> \
225+
-e CRAFT_RPC_HOST=0.0.0.0 \
226+
-v craft-data:/root/.craft-agent \
227+
craft-agents-server
228+
```
229+
230+
To enable TLS in Docker, mount your certificates and set the env vars:
231+
232+
```bash
233+
docker run -d \
234+
-p 9100:9100 \
235+
-e CRAFT_SERVER_TOKEN=<token> \
236+
-e CRAFT_RPC_HOST=0.0.0.0 \
237+
-e CRAFT_RPC_TLS_CERT=/certs/cert.pem \
238+
-e CRAFT_RPC_TLS_KEY=/certs/key.pem \
239+
-v ./certs:/certs:ro \
240+
-v craft-data:/root/.craft-agent \
241+
craft-agents-server
242+
```
243+
244+
## CLI Client
245+
246+
A terminal client that connects to a running Craft Agent server over WebSocket (`ws://` or `wss://`). Use it for scripting, CI/CD pipelines, server validation, or when you prefer the command line.
247+
248+
### Installation
249+
250+
```bash
251+
# From the monorepo (requires Bun)
252+
bun run apps/cli/src/index.ts --help
253+
254+
# Or add to your PATH
255+
alias craft-cli="bun run $(pwd)/apps/cli/src/index.ts"
256+
```
257+
258+
### Connection
259+
260+
The CLI reads connection details from flags or environment variables:
261+
262+
```bash
263+
# Via environment (set once)
264+
export CRAFT_SERVER_URL=ws://127.0.0.1:9100
265+
export CRAFT_SERVER_TOKEN=<your-token>
266+
267+
# Or via flags
268+
craft-cli --url ws://127.0.0.1:9100 --token <token> ping
269+
```
270+
271+
For TLS connections (`wss://`), use `--tls-ca <path>` for self-signed certificates.
272+
273+
### Commands
274+
275+
| Command | Description |
276+
|---------|-------------|
277+
| `ping` | Verify connectivity (clientId + latency) |
278+
| `health` | Check credential store health |
279+
| `versions` | Show server runtime versions |
280+
| `workspaces` | List workspaces |
281+
| `sessions` | List sessions in workspace |
282+
| `connections` | List LLM connections |
283+
| `sources` | List configured sources |
284+
| `session create` | Create a session (`--name`, `--mode`) |
285+
| `session messages <id>` | Print session message history |
286+
| `session delete <id>` | Delete a session |
287+
| `send <id> <message>` | Send message and stream AI response |
288+
| `cancel <id>` | Cancel in-progress processing |
289+
| `invoke <channel> [args]` | Raw RPC call with JSON args |
290+
| `listen <channel>` | Subscribe to push events (Ctrl+C to stop) |
291+
| `run <prompt>` | Self-contained: spawn server, run prompt, stream response, exit |
292+
| `--validate-server` | 21-step integration test (auto-spawns server if no `--url`) |
293+
294+
#### Run Command Flags
295+
296+
| Flag | Default | Description |
297+
|------|---------|-------------|
298+
| `--workspace-dir <path>` || Register a workspace directory before running |
299+
| `--source <slug>` || Enable a source (repeatable) |
300+
| `--output-format <fmt>` | `text` | Output format: `text` or `stream-json` |
301+
| `--mode <mode>` | `allow-all` | Permission mode for the session |
302+
| `--no-cleanup` | `false` | Skip session deletion on exit |
303+
| `--server-entry <path>` || Custom server entry point |
304+
| `--provider <name>` | `anthropic` | LLM provider (`anthropic`, `openai`, `google`, `openrouter`, `groq`, `mistral`, `xai`, etc.) |
305+
| `--model <id>` | (provider default) | Model ID (e.g., `claude-sonnet-4-5-20250929`, `gpt-4o`, `gemini-2.0-flash`) |
306+
| `--api-key <key>` || API key (or `$LLM_API_KEY`, or provider-specific env var) |
307+
| `--base-url <url>` || Custom API endpoint for proxies or self-hosted models |
308+
309+
The `run` command is fully self-contained — it spawns a headless server, creates a session, sends the prompt, streams the response, and exits. No separate server setup needed. An API key is resolved from `--api-key`, `$LLM_API_KEY`, or a provider-specific env var (e.g., `$ANTHROPIC_API_KEY`, `$OPENAI_API_KEY`).
310+
311+
### Examples
312+
313+
```bash
314+
# Quick connectivity check
315+
craft-cli ping
316+
317+
# List sessions (human-readable)
318+
craft-cli sessions
319+
320+
# Send a message and stream the AI response
321+
craft-cli send abc-123 "What files are in the current directory?"
322+
323+
# Pipe input
324+
echo "Summarize this" | craft-cli send abc-123
325+
326+
# JSON output for scripting
327+
craft-cli --json workspaces | jq '.[].name'
328+
329+
# Self-contained run (spawns its own server)
330+
craft-cli run "Summarize the README"
331+
craft-cli run --workspace-dir ./my-project --source github "List open PRs"
332+
333+
# Multi-provider support
334+
craft-cli run --provider openai --model gpt-4o "Summarize this repo"
335+
GOOGLE_API_KEY=... craft-cli run --provider google --model gemini-2.0-flash "Hello"
336+
craft-cli run --provider anthropic --base-url https://openrouter.ai/api/v1 --api-key $OR_KEY "Hello"
337+
338+
# Validate the server (auto-spawns if no --url)
339+
craft-cli --validate-server
340+
craft-cli --validate-server --url ws://127.0.0.1:9100 --token <token>
341+
```
342+
150343
## Architecture
151344

152345
```
153346
craft-agent/
154347
├── apps/
348+
│ ├── cli/ # Terminal client (CLI)
155349
│ └── electron/ # Desktop GUI (primary)
156350
│ └── src/
157351
│ ├── main/ # Electron main process

apps/cli/package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@craft-agent/cli",
3+
"version": "0.7.0",
4+
"description": "Terminal client for Craft Agent server",
5+
"type": "module",
6+
"main": "src/index.ts",
7+
"bin": {
8+
"craft-cli": "src/index.ts"
9+
},
10+
"scripts": {
11+
"start": "bun run src/index.ts",
12+
"typecheck": "tsc --noEmit",
13+
"test": "bun test src/"
14+
},
15+
"dependencies": {
16+
"@craft-agent/shared": "workspace:*",
17+
"@craft-agent/server-core": "workspace:*"
18+
},
19+
"devDependencies": {
20+
"typescript": "^5.8.2",
21+
"@types/node": "^22.0.0",
22+
"@types/bun": "latest"
23+
}
24+
}

0 commit comments

Comments
 (0)