Initial CodeCraft: web-based coding harness for AI models

Rust backend (axum/tokio): agent loop with streaming, tools (fs, bash,
web_fetch, todo, ask_user, subagents, skills, craft snapshots/compare),
permissions with modes and rules, Anthropic + OpenAI-compatible providers,
MCP client (stdio/HTTP, elicitation), SQLite sessions, usage statistics,
compaction, commented TOML config editable via toml_edit.

React PWA: chat with live streaming, permission/question cards, modes
(build/plan/craft), settings for providers/models/prices/MCP/permissions,
raw config editor, statistics dashboard, i18n (en/de). Docker setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-08 14:39:24 +01:00
commit f3a2fe3816
62 files changed
+21835

No files matched your search

+208
View File
@@ -0,0 +1,208 @@
# =============================================================================
# CodeCraft – configuration
# =============================================================================
#
# This file is the single source of truth for the server. Everything here can
# also be edited in the web UI (Settings). The UI edits this file in place and
# keeps your comments and formatting intact.
#
# Location: $CODECRAFT_CONFIG, otherwise <data_dir>/config.toml (Docker: /data/config.toml)
#
# Secrets: any string value may reference environment variables with ${NAME},
# e.g. api_key = "${ANTHROPIC_API_KEY}". Prefer this over literal keys – keys
# entered in the UI are stored here in plain text.
# =============================================================================
[server]
# Address the HTTP server listens on.
bind = "0.0.0.0:8787"
# Root directory the agents may work in. Sessions pick a project folder below it.
# Docker: mount your projects here (see docker-compose.yml).
workspace = "/workspace"
# Where sessions (SQLite) and runtime state are stored.
data_dir = "/data"
# Access token for the web UI and API. Leave empty to disable auth (only do
# this on localhost!). Recommended: auth_token = "${CODECRAFT_TOKEN}"
auth_token = "${CODECRAFT_TOKEN}"
# Default model for new sessions, as "<provider>/<model>".
default_model = "anthropic/claude-sonnet-5-5"
# Cheap/fast model for titles and history compaction. Falls back to default_model.
small_model = "anthropic/claude-haiku-5-5"
# Compact the conversation automatically when the context is this full (0.0–1.0).
auto_compact_threshold = 0.85
# Currency label for the cost statistics (prices below are in this currency).
currency = "USD"
# -----------------------------------------------------------------------------
# Providers
# -----------------------------------------------------------------------------
# kind = "anthropic" → Anthropic Messages API (and compatible endpoints)
# kind = "openai" → OpenAI-compatible /chat/completions (Gemini, opencode Go,
# fal, OpenRouter, Ollama, vLLM, llama.cpp, LM Studio, ...)
#
# models: optional list. If empty, the model list is fetched from <base_url>/models.
# Per-model options: context (tokens), max_output (tokens), reasoning (bool),
# vision (bool, can read images), effort ("low" | "medium" | "high" | "xhigh" | "max").
#
# Prices (per 1M tokens, used for the statistics):
# input_price, output_price, cache_read_price, cache_write_price
# Cache prices default to input_price when omitted. Costs are stored per request,
# so changing a price only affects future requests.
#
# Authentication: by default "Authorization: Bearer <api_key>" (openai) or
# "x-api-key: <api_key>" (anthropic). Override with auth_header / auth_prefix.
# Extra static headers: headers = { "X-Foo" = "bar" }
# -----------------------------------------------------------------------------
[providers.anthropic]
kind = "anthropic"
base_url = "https://api.anthropic.com/v1"
api_key = "${ANTHROPIC_API_KEY}"
# Current Claude models think adaptively; "effort" controls depth and cost.
# Prices: Anthropic list prices in USD per 1M tokens (Nov 2026) – adjust if needed.
models = [
{ id = "claude-opus-5-5", context = 1000000, max_output = 64000, reasoning = true, vision = true, effort = "high",
input_price = 4.0, output_price = 20.0, cache_read_price = 0.20, cache_write_price = 5.0 },
{ id = "claude-sonnet-5-5", context = 1000000, max_output = 64000, reasoning = true, vision = true, effort = "medium",
input_price = 2.0, output_price = 10.0, cache_read_price = 0.20, cache_write_price = 2.5 },
{ id = "claude-haiku-5-5", context = 1000000, max_output = 32000, reasoning = true, vision = true, effort = "low",
input_price = 0.10, output_price = 0.50, cache_read_price = 0.01, cache_write_price = 0.125 },
]
# Google Gemini through its OpenAI-compatible endpoint.
[providers.gemini]
kind = "openai"
base_url = "https://generativelanguage.googleapis.com/v1beta/openai"
api_key = "${GEMINI_API_KEY}"
models = [] # discovered automatically
enabled = false # set to true once GEMINI_API_KEY is available
# opencode Go subscription (OpenAI-compatible endpoint for most models).
# Current model list: https://opencode.ai/inference/go/v1/models
[providers.opencode-go]
kind = "openai"
base_url = "https://opencode.ai/inference/go/openai/v1"
api_key = "${OPENCODE_API_KEY}"
models = []
enabled = false
# fal.ai – OpenRouter-backed LLM router. fal expects "Authorization: Key <FAL_KEY>".
# NOTE: verify base_url against the fal docs for your account.
[providers.fal]
kind = "openai"
base_url = "https://fal.run/openrouter/router/openai/v1"
api_key = "${FAL_KEY}"
auth_prefix = "Key "
models = [
{ id = "google/gemini-2.5-flash", context = 1000000, max_output = 32000, vision = true },
]
enabled = false
# Self-hosted (Ollama shown; vLLM/llama.cpp/LM Studio work the same way).
# From inside Docker, the host is reachable as host.docker.internal.
[providers.local]
kind = "openai"
base_url = "http://host.docker.internal:11434/v1"
api_key = ""
models = []
enabled = false
# -----------------------------------------------------------------------------
# MCP servers
# -----------------------------------------------------------------------------
# type = "stdio" → command + args (+ env), started as a child process
# type = "http" → Streamable HTTP endpoint (url + optional headers)
# Tools appear to the model as mcp__<server>__<tool>.
# MCP servers may ask you questions (elicitation); they show up in the UI.
# -----------------------------------------------------------------------------
# [mcp.trilium]
# type = "http"
# url = "https://notes.example.org/mcp"
# headers = { Authorization = "Bearer ${TRILIUM_TOKEN}" }
# [mcp.filesystem]
# type = "stdio"
# command = "npx"
# args = ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
# env = {}
# enabled = false
# -----------------------------------------------------------------------------
# Permissions
# -----------------------------------------------------------------------------
# For each tool: "allow" | "ask" | "deny". "ask" forwards the request to the UI.
# MCP tools can be addressed as "mcp__<server>__<tool>" or "mcp__<server>__*".
# Session permission modes in the UI override this:
# default → these rules
# accept_edits → file edits allowed without asking
# bypass → everything allowed (use with care)
# -----------------------------------------------------------------------------
[permissions]
read = "allow"
glob = "allow"
grep = "allow"
list = "allow"
todo_write = "allow"
ask_user = "allow"
task = "allow"
write = "ask"
edit = "ask"
bash = "ask"
web_fetch = "ask"
browser_snapshot = "allow"
image_compare = "allow"
"mcp__*" = "ask"
# Shell command patterns (glob, matched against the full command). First match wins.
[permissions.bash_rules]
"git status*" = "allow"
"git diff*" = "allow"
"git log*" = "allow"
"ls*" = "allow"
"pwd" = "allow"
"rm -rf /*" = "deny"
# -----------------------------------------------------------------------------
# Agents (modes)
# -----------------------------------------------------------------------------
# Built-in: build (full access), plan (read-only, proposes a plan),
# craft (study → build → check → ready), and subagents
# general + explore used by the "task" tool.
# Define your own or override built-ins here. mode = "primary" shows up in the
# mode switcher, mode = "subagent" is only available through the task tool.
# -----------------------------------------------------------------------------
# [agents.reviewer]
# mode = "subagent"
# description = "Reviews code changes for bugs and style issues."
# model = "anthropic/claude-opus-5-5" # optional, otherwise session model
# tools = ["read", "glob", "grep", "bash"] # optional, default: all tools
# prompt = """
# You are a meticulous code reviewer. ...
# """
# -----------------------------------------------------------------------------
# Craft mode
# -----------------------------------------------------------------------------
[craft]
# Headless Chromium binary used to study websites and check builds.
chromium = "chromium"
# Screen widths used for screenshots and comparisons.
widths = [390, 768, 1440]
# Clean room: only what a visitor sees (screenshots, visible text, layout).
# Set to false for "deep" mode (page source and scripts may be read).
clean_room = true