Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.git
.github
node_modules
**/node_modules
.turbo
.cache
dist
apps/*/dist
packages/*/dist
apps/lander/public
*.log
*.sqlite
*.db*
tmp
.DS_Store
.env
.env.*
docs
31 changes: 31 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Repository Guidelines

## Project Structure & Module Organization
- `apps/` hosts runnable surfaces: `server` (REST + proxy), `tui` (interactive console), and `lander` (marketing site screenshot assets).
- `packages/` contains reusable modules grouped by concern: telemetry + core logic (`core`, `core-di`, `load-balancer`), IO layers (`http-api`, `proxy`, `database`), shared UI kits (`dashboard-web`, `ui-common`, `ui-constants`), and tooling (`agents`, `cli-commands`).
- Docs and product briefs live in `docs/`; configs such as `tsconfig.json` and `biome.json` sit at the root for repo-wide tooling.

## Build, Test & Development Commands
- `bun install` installs all workspace dependencies (Bun >= 1.2.8 required).
- `bun run dev:server` hot-reloads the API/proxy; `bun run dev:dashboard` serves the React dashboard with Bun’s HMR.
- `bun run tui` launches the interactive terminal UI; `bun run ccflare` builds then starts the TUI + server bundle.
- `bun run build` orchestrates `build:dashboard`, `build:tui`, and optional `build:lander` for release artifacts.
- `bun run typecheck`, `bun run lint`, and `bun run format` gate submissions (Biome auto-formats with tabs + double quotes).

## Coding Style & Naming Conventions
- Follow TypeScript strictness; prefer ES modules and workspace-relative imports (`@ccflare/<package>`).
- Biome enforces tab indentation, double-quoted strings, and organized imports—run `bun run format` before commits.
- Use descriptive PascalCase for React components/Providers, camelCase for functions/instances, SCREAMING_SNAKE_CASE for env vars.

## Testing Guidelines
- The project is migrating to Bun’s built-in test runner; place specs beside source files as `<name>.test.ts` and target observable behavior rather than mocks.
- Until coverage targets solidify, add tests for every bug fix plus high-risk flows (load balancing, account rotation, OAuth refresh). Use `bun test` (or `bun wip --watch` once available) before pushing.

## Commit & Pull Request Guidelines
- Match the existing Conventional Commit style (`feat:`, `fix:`, `chore:`). Scope optional but encouraged for packages (e.g., `fix(tui-core): guard null response`).
- Each PR should describe the change, include reproduction steps or screenshots for UI/TUI work, and link any GitHub issues.
- Ensure CI-critical commands (`typecheck`, `lint`, `build`) pass locally; note any skipped tests and justify in the PR description.

## Security & Configuration Tips
- Keep sensitive credentials in the local `.env`; never commit API keys. Prefer the config modules under `packages/config` for defaults.
- When debugging proxy flows, set `ANTHROPIC_BASE_URL` and related credentials via `bun run server` env vars instead of hardcoding values.
47 changes: 47 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# syntax=docker/dockerfile:1.7

ARG BUN_VERSION=1.2.8

FROM oven/bun:${BUN_VERSION} AS builder
WORKDIR /app

# Copy workspace manifests first for better layer caching
COPY package.json bun.lock tsconfig.json biome.json ./
COPY apps ./apps
COPY packages ./packages

RUN bun install --frozen-lockfile
RUN bun run build:dashboard

FROM oven/bun:${BUN_VERSION} AS runner
WORKDIR /app

ENV NODE_ENV=production \
XDG_CONFIG_HOME=/data \
ccflare_CONFIG_PATH=/data/config/ccflare.json \
ccflare_DB_PATH=/data/storage/ccflare.db \
PORT=8080

# System dependencies for health checks
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/bun.lock ./bun.lock
COPY --from=builder /app/tsconfig.json ./tsconfig.json
COPY --from=builder /app/biome.json ./biome.json
COPY --from=builder /app/apps ./apps
COPY --from=builder /app/packages ./packages

RUN bun install --frozen-lockfile --production

RUN mkdir -p /data/config /data/storage

EXPOSE 8080
VOLUME ["/data"]

HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -fsS "http://127.0.0.1:${PORT}/api/stats" >/dev/null || exit 1

ENTRYPOINT ["bun", "run", "server"]
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,51 @@ bun run ccflare
export ANTHROPIC_BASE_URL=http://localhost:8080
```

## Run with Docker

```bash
# Build the image (ships with dashboard assets prebuilt)
docker build -t ccflare .

# Start the server inside Docker
docker run -d \
--name ccflare \
-p 8080:8080 \
-v ccflare_data:/data \
-e PORT=8080 \
-e LB_STRATEGY=session \
ccflare
```

The container stores configuration and the SQLite database under `/data` (mapped to
`ccflare_CONFIG_PATH=/data/config/ccflare.json` and `ccflare_DB_PATH=/data/storage/ccflare.db`).
Mount a volume there (shown above) to persist settings between restarts. See
[`docs/deployment.md`](docs/deployment.md#docker-deployment) for compose examples
and advanced options (health checks, reverse proxies, custom networks, etc.).

## Agent Workspaces & Discovery

ccflare loads agents from Markdown files inside `.claude/agents/` folders. To keep
your container in sync with projects scattered across Linux, WSL, or Windows:

```bash
# One-time automation: scan, capture workspaces, restart container with minimal mounts
bun run agents:setup

# Manual scan if you want to keep the container running
bun run agents:scan -- /host /mnt/c --max-depth 8
```

- The setup script stops `ccflare-dev`, launches a helper container with wide
mounts, runs the scanner, and restarts `ccflare-dev` with only the discovered
bind mounts plus `/data`, while sharing a `ccflare-workspaces` volume that
persists `/root/.ccflare/workspaces.json` between restarts.
- The dashboard now includes a **Register Workspace Paths** card (Agents tab) so
you can add absolute paths on the fly. Behind the scenes it calls
`POST /api/workspaces` (documented in [`docs/api-http.md`](docs/api-http.md#post-apiworkspaces)).
- For more examples (mount tables, environment variables, troubleshooting) see
[`docs/agent-workspaces.md`](docs/agent-workspaces.md).

## Features

### 🎯 Intelligent Load Balancing
Expand Down
43 changes: 23 additions & 20 deletions apps/tui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,26 +182,29 @@ Examples:
return;
}

if (parsed.setModel) {
const config = new Config();
// Validate the model
const modelMap: Record<string, string> = {
"opus-4": CLAUDE_MODEL_IDS.OPUS_4,
"sonnet-4": CLAUDE_MODEL_IDS.SONNET_4,
"opus-4.1": CLAUDE_MODEL_IDS.OPUS_4_1,
};

const fullModel = modelMap[parsed.setModel];
if (!fullModel) {
console.error(`❌ Invalid model: ${parsed.setModel}`);
console.error("Valid models: opus-4, sonnet-4");
process.exit(1);
}

config.setDefaultAgentModel(fullModel);
console.log(`✅ Default agent model set to: ${fullModel}`);
return;
}
if (parsed.setModel) {
const config = new Config();
// Validate the model
const modelMap: Record<string, string> = {
"sonnet-4.5": CLAUDE_MODEL_IDS.SONNET_4_5,
"opus-4.1": CLAUDE_MODEL_IDS.OPUS_4_1,
"haiku-4.5": CLAUDE_MODEL_IDS.HAIKU_4_5,
"opus-plan": CLAUDE_MODEL_IDS.OPUS_PLAN_MODE,
};

const fullModel = modelMap[parsed.setModel];
if (!fullModel) {
console.error(`❌ Invalid model: ${parsed.setModel}`);
console.error(
`Valid models: ${Object.keys(modelMap).join(", ")}`,
);
process.exit(1);
}

config.setDefaultAgentModel(fullModel);
console.log(`✅ Default agent model set to: ${fullModel}`);
return;
}

// Default: Launch interactive TUI with auto-started server
const config = new Config();
Expand Down
7 changes: 5 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 81 additions & 0 deletions docs/agent-workspaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Agent Workspace Discovery

ccflare automatically surfaces agents that live in Markdown files under `.claude/agents/` folders. This guide covers every way to point the proxy at new projects—across Linux, WSL, and Windows—and how to keep the container mounts in sync.

## Default Discovery Flow

1. **Scan for `.claude/agents` directories.** The `packages/agents` scanner crawls the filesystem and writes the results to `~/.ccflare/workspaces.json`.
2. **Persist the workspace list.** Each entry stores the absolute path, a short name, and `lastSeen` timestamp.
3. **Load agents.** The dashboard/API reads every Markdown file and merges metadata with any per-agent model preferences stored in the database.

You can trigger the scan manually (`bun run agents:scan`) or run the automation script described below.

## Manual Scan (`bun run agents:scan`)

```
bun run agents:scan -- /host /mnt/c --max-depth 8
```

- Without arguments the scanner walks the current directory, your home folder, and a platform-aware list of defaults (`/workspaces`, `/host`, `/mnt/<drive>`, `/host_mnt/<drive>`, etc.).
- Pass custom roots via CLI arguments or `AGENT_SCAN_ROOTS="/host,/mnt/c"`. Use `AGENT_SCAN_EXTRA_ROOTS` to append to the defaults.
- `AGENT_SCAN_MAX_DEPTH` controls recursion depth (default `8`). Set `AGENT_SCAN_INCLUDE_ROOT=true` if you truly want to traverse `/`.
- Windows paths such as `C:\Projects\Repo` are automatically normalized to `/mnt/c/Projects/Repo` when the scanner runs inside WSL/Linux.

The command logs every discovered `.claude/agents` directory plus any warnings (e.g., unsupported `model: inherit`).

## Automated Setup (`bun run agents:setup`)

The orchestration script handles the full workflow:

1. Stops the current `ccflare-dev` container and any previous helper containers.
2. Launches a temporary `ccflare-dev-scan` container with wide mounts (`/` → `/host`, `/mnt/*`).
3. Runs the scanner (`bun run agents:scan --max-depth 8 /host /mnt/c`).
4. Reads `~/.ccflare/workspaces.json` from the helper container.
5. Tears down the helper and restarts `ccflare-dev` with bind mounts only for the discovered directories plus `/data`.

```bash
bun run agents:setup
```

Environment variables:

| Variable | Description |
| --- | --- |
| `CCFLARE_CONTAINER` | Name for the runtime container (`ccflare-dev` by default). |
| `CCFLARE_IMAGE` | Image tag to run (`ccflare:latest`). |
| `CCFLARE_DATA_VOLUME` | Data volume name (`ccflare-data`). |
| `CCFLARE_WORKSPACES_VOLUME` | Named volume that persists `/root/.ccflare/workspaces.json` (`ccflare-workspaces`). |
| `AGENT_SCAN_ROOTS` | Extra roots to mount during the scan (comma/semicolon/newline separated). |
| `AGENT_SCAN_MAX_DEPTH` | Overrides traversal depth. |

If no workspaces are found the script falls back to the wide mounts so you can diagnose manually.

## Mounting Host Paths

The scanner (and later the server) only sees directories that are mounted into the container. Examples:

| Host | Sample `docker run` mounts |
| --- | --- |
| Native Linux | `-v /:/host` plus more targeted paths (`-v /srv/projects:/srv/projects`). |
| WSL2 | `-v /:/host -v /mnt/c:/mnt/c` so Windows drives are visible as `/mnt/c`. |
| Windows (Docker Desktop) | `-v C:\\Users\\me\\agents:/windows/agents` for each shared directory. |

After adjusting mounts, rerun `bun run agents:setup` so the helper container refreshes both the bind mounts and the persisted `/root/.ccflare/workspaces.json` living inside the `ccflare-workspaces` volume. If you prefer to keep the container running, call `bun run agents:scan` followed by `POST /api/workspaces` (see below).

## Registering Paths from the Dashboard/API

- **Dashboard:** In the **Agents → Register Workspace Paths** card, paste absolute paths (e.g., `/opt/projects/app`, `/mnt/c/Users/me/tooling`). Paths are normalized and sent to the API; success/errors are shown inline.
- **API:**
```bash
curl -X POST http://localhost:8080/api/workspaces \
-H "Content-Type: application/json" \
-d '{"paths":["/opt/projects/app","/mnt/c/Users/me/tooling"]}'
```
The handler checks each path, registers valid ones, and returns counts for `added`, `updated`, `skipped`, plus any `invalidPaths`.

## Common Warnings

- **“invalid model: inherit”** – The agent file specifies a shorthand model name that ccflare doesn’t recognize. The default agent model (configurable in the dashboard/API) is used instead.
- **Duplicate agents** – Multiple mounts often point to the same repo (e.g., `/mnt/c/...` and Docker Desktop’s bind-mount mirrors). ccflare logs the duplicates but keeps the first copy; no action is required unless you want to prune redundant mounts.

Refer to [`docs/deployment.md`](docs/deployment.md#host-mounts-for-agent-discovery-linux-windows-wsl) for more mount examples and [`docs/api-http.md`](docs/api-http.md#post-apiworkspaces) for the full workspace API reference.
61 changes: 60 additions & 1 deletion docs/api-http.md
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,65 @@ List all available workspaces with agent counts.
curl http://localhost:8080/api/workspaces
```

#### POST /api/workspaces

Register one or more workspace paths so ccflare can load agents from them.

**Request:**
```json
{
"paths": ["/opt/projects/my-app", "/mnt/c/Users/me/project"]
}
```

**Response:**
```json
{
"success": true,
"added": 2,
"updated": 0,
"skipped": 0,
"invalidPaths": [],
"workspaces": [
{
"name": "my-app",
"path": "/opt/projects/my-app",
"agentCount": 6
}
]
}
```

**Notes:**

- Paths must exist inside the running container. Use `bun run agents:setup` (or add `-v` mounts manually) before calling this endpoint.
- You can send a single string with `path` or an array with `paths`.
- Duplicate entries simply refresh the `lastSeen` timestamp; they do not re-import files.

#### One-Time Workspace Scan

If you need to bulk-register every `.claude/agents` directory that already exists on disk (e.g., host-mounted Windows drives), run the bundled scanner once:

```bash
bun run agents:scan -- /host /mnt/c
```

- With no arguments the scanner walks the current directory, your home directory, and a platform-aware list of defaults: on Linux/WSL it checks `/workspaces`, `/host`, `/data`, `/mnt/<drive>`, `/host_mnt/<drive>`, etc.; on Windows it automatically iterates every mounted drive letter (`C:\`, `D:\`, …) plus `%USERPROFILE%`.
- Pass custom roots explicitly (`bun run agents:scan -- /host /mnt/c /data/shared`) or set `AGENT_SCAN_ROOTS="/host,/mnt/c"` (comma/semicolon/newline separated). Use `AGENT_SCAN_EXTRA_ROOTS` to append to the defaults without replacing them.
- Windows-style paths such as `C:\Projects\Repo` are accepted everywhere; on Linux/WSL the scanner transparently normalizes them to `/mnt/c/Projects/Repo`.
- Control traversal fan-out via `--max-depth 10` or `AGENT_SCAN_MAX_DEPTH=10`. Set `AGENT_SCAN_INCLUDE_ROOT=true` if you truly want to walk `/`.
- Every discovered workspace is persisted to `~/.ccflare/workspaces.json`, so the dashboard immediately shows the agents after a single scan (no server restart required).

Example commands:

```powershell
# PowerShell on Windows – scan C: and D:
bun run agents:scan -- "C:\Users\me" "D:\labs" --max-depth 6

# WSL / Docker container with Windows drives mounted under /mnt
AGENT_SCAN_ROOTS="/host,/mnt/c" bun run agents:scan --max-depth 8
```

---

### Logs
Expand Down Expand Up @@ -867,4 +926,4 @@ The following strategy is available:

7. **Rate Limit Tracking**: Rate limit information is automatically extracted from responses and stored for each account, including reset times and remaining requests.

8. **Provider Filtering**: Accounts are automatically filtered by provider when selecting for requests, ensuring compatibility.
8. **Provider Filtering**: Accounts are automatically filtered by provider when selecting for requests, ensuring compatibility.
Loading