Skip to content
Merged
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
12 changes: 9 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
OC_GO_CC_API_KEY=sk-opencode-your-key-here
OC_GO_CC_HOST=0.0.0.0
OC_GO_CC_PORT=3456
# Primary environment variables (recommended)
ROUTATIC_PROXY_API_KEY=sk-opencode-your-key-here
ROUTATIC_PROXY_HOST=0.0.0.0
ROUTATIC_PROXY_PORT=3456

# Legacy fallback names (still supported for migration)
# OC_GO_CC_API_KEY=sk-opencode-your-key-here
# OC_GO_CC_HOST=0.0.0.0
# OC_GO_CC_PORT=3456
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
run: go test ./... -v -race

- name: Build
run: go build ./cmd/oc-go-cc
run: go build ./cmd/routatic-proxy

lint:
name: Lint
Expand Down
79 changes: 44 additions & 35 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
run: go test ./... -v -race

- name: Build (sanity check)
run: go build -o /dev/null ./cmd/oc-go-cc
run: go build -o /dev/null ./cmd/routatic-proxy

# ── Stage 2: Create Release ──────────────────────────────────────
release:
Expand Down Expand Up @@ -100,8 +100,8 @@ jobs:
echo "Building ${NAME}..."
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" \
go build -ldflags "$LDFLAGS -s -w" \
-o "dist/oc-go-cc_${NAME}${EXT}" \
./cmd/oc-go-cc
-o "dist/routatic-proxy_${NAME}${EXT}" \
./cmd/routatic-proxy
done

echo ""
Expand All @@ -111,7 +111,7 @@ jobs:
- name: Generate checksums
run: |
cd dist
sha256sum oc-go-cc_* > checksums.txt
sha256sum routatic-proxy_* > checksums.txt
cat checksums.txt

- name: Generate AI Changelog
Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:
COMMITS=$(git log "${PREVIOUS_TAG}..HEAD" --pretty=format:"%H%n%s%n%b%n---COMMIT_SEP---")
FILE_CHANGES=$(git diff --stat "${PREVIOUS_TAG}..HEAD")

PROMPT="You are a technical writer generating release notes for a Go proxy server project (oc-go-cc).
PROMPT="You are a technical writer generating release notes for a Go proxy server project (routatic-proxy).

Analyze the provided git commits and file changes, then generate a well-structured
changelog in Markdown format. Follow these guidelines:
Expand Down Expand Up @@ -213,7 +213,7 @@ jobs:
draft: false
prerelease: false
files: |
dist/oc-go-cc_*
dist/routatic-proxy_*
dist/checksums.txt

# ── Stage 3: Publish Docker Image ───────────────────────────────
Expand All @@ -223,7 +223,7 @@ jobs:
permissions:
contents: read
packages: write
if: github.repository == 'samueltuyizere/oc-go-cc'
if: github.repository == 'routatic/proxy'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -263,7 +263,7 @@ jobs:
homebrew:
name: Update Homebrew Tap
needs: release
if: github.repository == 'samueltuyizere/oc-go-cc'
if: github.repository == 'routatic/proxy'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -313,58 +313,59 @@ jobs:
git clone "https://x-access-token:${PAT}@github.com/${TAP_REPO}.git" tap-repo
cd tap-repo

mkdir -p Formula
mkdir -p Formula Aliases

cat > Formula/oc-go-cc.rb << RUBY
class OcGoCc < Formula
cat > Formula/routatic-proxy.rb << RUBY
class RoutaticProxy < Formula
desc "Proxy Claude Code requests to OpenCode Go API"
homepage "https://github.com/${REPO}"
version "${VERSION}"

on_macos do
if Hardware::CPU.arm?
url "${BASE_URL}/oc-go-cc_darwin-arm64"
url "${BASE_URL}/routatic-proxy_darwin-arm64"
sha256 "${DARWIN_ARM64}"
else
url "${BASE_URL}/oc-go-cc_darwin-amd64"
url "${BASE_URL}/routatic-proxy_darwin-amd64"
sha256 "${DARWIN_AMD64}"
end
end

on_linux do
if Hardware::CPU.intel?
url "${BASE_URL}/oc-go-cc_linux-amd64"
url "${BASE_URL}/routatic-proxy_linux-amd64"
sha256 "${LINUX_AMD64}"
else
url "${BASE_URL}/oc-go-cc_linux-arm64"
url "${BASE_URL}/routatic-proxy_linux-arm64"
sha256 "${LINUX_ARM64}"
end
end

def install
if OS.mac? && Hardware::CPU.arm?
bin.install "oc-go-cc_darwin-arm64" => "oc-go-cc"
bin.install "routatic-proxy_darwin-arm64" => "routatic-proxy"
elsif OS.mac?
bin.install "oc-go-cc_darwin-amd64" => "oc-go-cc"
bin.install "routatic-proxy_darwin-amd64" => "routatic-proxy"
elsif OS.linux? && Hardware::CPU.intel?
bin.install "oc-go-cc_linux-amd64" => "oc-go-cc"
bin.install "routatic-proxy_linux-amd64" => "routatic-proxy"
else
bin.install "oc-go-cc_linux-arm64" => "oc-go-cc"
bin.install "routatic-proxy_linux-arm64" => "routatic-proxy"
end
bin.install_symlink bin/"routatic-proxy" => "oc-go-cc"
end

def caveats
<<~EOS
To get started with oc-go-cc:
To get started with routatic-proxy:

1. Initialize configuration:
oc-go-cc init
routatic-proxy init

2. Set your OpenCode Go API key:
export OC_GO_CC_API_KEY=sk-opencode-your-key
export ROUTATIC_PROXY_API_KEY=sk-opencode-your-key

3. Start the proxy:
oc-go-cc serve
routatic-proxy serve

4. Configure Claude Code:
export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
Expand All @@ -376,22 +377,25 @@ jobs:
end

test do
system "#{bin}/routatic-proxy", "--version"
system "#{bin}/oc-go-cc", "--version"
end
end
RUBY

git add Formula/oc-go-cc.rb
ln -sf ../Formula/routatic-proxy.rb Aliases/oc-go-cc

git add Formula/routatic-proxy.rb Aliases/oc-go-cc
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -m "Update oc-go-cc to ${VERSION}" || echo "No changes to commit"
git commit -m "Update routatic-proxy to ${VERSION}" || echo "No changes to commit"
git push

# ── Stage 5: Update Scoop Bucket ──────────────────────────────────
scoop:
name: Update Scoop Bucket
needs: release
if: github.repository == 'samueltuyizere/oc-go-cc'
if: github.repository == 'routatic/proxy'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -437,42 +441,47 @@ jobs:

mkdir -p bucket

cat > bucket/oc-go-cc.json << JSON
cat > bucket/routatic-proxy.json << JSON
{
"version": "${VERSION}",
"description": "Proxy Claude Code requests to OpenCode Go API",
"homepage": "https://github.com/${REPO}",
"license": "AGPL-3.0-only",
"architecture": {
"64bit": {
"url": "${BASE_URL}/oc-go-cc_windows-amd64.exe",
"url": "${BASE_URL}/routatic-proxy_windows-amd64.exe",
"hash": "${WINDOWS_AMD64}"
},
"arm64": {
"url": "${BASE_URL}/oc-go-cc_windows-arm64.exe",
"url": "${BASE_URL}/routatic-proxy_windows-arm64.exe",
"hash": "${WINDOWS_ARM64}"
}
},
"bin": "oc-go-cc.exe",
"post_install": "Get-ChildItem \$dir\\\\oc-go-cc_windows-*.exe | Rename-Item -NewName oc-go-cc.exe",
"bin": [
["routatic-proxy.exe", "routatic-proxy"],
["routatic-proxy.exe", "oc-go-cc"]
],
"post_install": "Get-ChildItem \$dir\\\\routatic-proxy_windows-*.exe | Rename-Item -NewName routatic-proxy.exe",
"checkver": {
"github": "https://github.com/${REPO}"
},
"autoupdate": {
"architecture": {
"64bit": {
"url": "https://github.com/${REPO}/releases/download/v\$version/oc-go-cc_windows-amd64.exe"
"url": "https://github.com/${REPO}/releases/download/v\$version/routatic-proxy_windows-amd64.exe"
},
"arm64": {
"url": "https://github.com/${REPO}/releases/download/v\$version/oc-go-cc_windows-arm64.exe"
"url": "https://github.com/${REPO}/releases/download/v\$version/routatic-proxy_windows-arm64.exe"
}
}
}
}
JSON

git add bucket/oc-go-cc.json
cp bucket/routatic-proxy.json bucket/oc-go-cc.json

git add bucket/routatic-proxy.json bucket/oc-go-cc.json
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -m "Update oc-go-cc to ${VERSION}" || echo "No changes to commit"
git commit -m "Update routatic-proxy to ${VERSION}" || echo "No changes to commit"
git push
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Commands

```bash
make build # Build binary to bin/oc-go-cc
make build # Build binary to bin/routatic-proxy
make run # Run without building
make test # Run tests with race detector
make lint # go vet + test
Expand All @@ -18,9 +18,9 @@ Run a single test: `go test ./internal/router/ -v`

## Architecture

**Purpose:** oc-go-cc is a proxy server that sits between Claude Code and OpenCode Go. It intercepts Anthropic API requests, transforms them to OpenAI Chat Completions format, forwards them to OpenCode Go, and transforms responses back to Anthropic SSE.
**Purpose:** routatic-proxy is a proxy server that sits between Claude Code and OpenCode Go. It intercepts Anthropic API requests, transforms them to OpenAI Chat Completions format, forwards them to OpenCode Go, and transforms responses back to Anthropic SSE.

**Model routing is config-driven, not code-driven.** All models are defined in `~/.config/oc-go-cc/config.json` — adding a new model requires no code changes. Go provider models are transformed to OpenAI Chat Completions format automatically. Zen models use endpoint classification via `ClassifyEndpoint()`. The router in `internal/router/` selects models by matching request content against scenario patterns defined in `scenarios.go`.
**Model routing is config-driven, not code-driven.** All models are defined in `~/.config/routatic-proxy/config.json` — adding a new model requires no code changes. Go provider models are transformed to OpenAI Chat Completions format automatically. Zen models use endpoint classification via `ClassifyEndpoint()`. The router in `internal/router/` selects models by matching request content against scenario patterns defined in `scenarios.go`.

If a model's upstream doesn't support Anthropic tool format (`type: "custom"` server-tool shorthands), set `"anthropic_tools_disabled": true` in the model config to force it through the Chat Completions transform path instead of the raw Anthropic endpoint.

Expand All @@ -47,7 +47,7 @@ For streaming, the router downgrades to fast models (Qwen3.6 Plus) for better TT

## Key Files

- `cmd/oc-go-cc/main.go` — CLI entry point (cobra). Default config template is generated here.
- `cmd/routatic-proxy/main.go` — CLI entry point (cobra). Default config template is generated here.
- `internal/config/` — Config types and JSON loader with `${VAR}` env interpolation.
- `internal/transformer/` — Request/response format conversion (Anthropic ↔ OpenAI).
- `internal/router/fallback.go` — Circuit breaker per model (3 failures = 30s skip).
Expand Down
28 changes: 16 additions & 12 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

## Config File

Location: `~/.config/oc-go-cc/config.json`
Location: `~/.config/routatic-proxy/config.json`

Override with `OC_GO_CC_CONFIG` environment variable.
Override with `ROUTATIC_PROXY_CONFIG` environment variable.

For migration, `~/.config/oc-go-cc/config.json` is loaded when the new config file does not exist, and every `OC_GO_CC_*` environment variable is still accepted as a fallback for its `ROUTATIC_PROXY_*` replacement.

## Full Config Reference

```json
{
"api_key": "${OC_GO_CC_API_KEY}",
"api_key": "${ROUTATIC_PROXY_API_KEY}",
"host": "127.0.0.1",
"port": 3456,
"hot_reload": false,
Expand Down Expand Up @@ -115,7 +117,7 @@ Override with `OC_GO_CC_CONFIG` environment variable.

## Providers

oc-go-cc supports two providers for upstream API calls:
routatic-proxy supports two providers for upstream API calls:

### OpenCode Go (`opencode-go`)

Expand All @@ -139,13 +141,15 @@ Environment variables override config file values. Config values also support `$

| Variable | Description | Default |
| ----------------------- | ------------------------------------------- | ------------------------------------------------ |
| `OC_GO_CC_API_KEY` | OpenCode Go API key (**required**) | — |
| `OC_GO_CC_CONFIG` | Custom config file path | `~/.config/oc-go-cc/config.json` |
| `OC_GO_CC_HOST` | Proxy listen host | `127.0.0.1` |
| `OC_GO_CC_PORT` | Proxy listen port | `3456` |
| `OC_GO_CC_OPENCODE_URL` | OpenCode Go API endpoint | `https://opencode.ai/zen/go/v1/chat/completions` |
| `OC_GO_CC_OPENCODE_ZEN_URL` | OpenCode Zen API endpoint | `https://opencode.ai/zen/v1/chat/completions` |
| `OC_GO_CC_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error` | `info` |
| `ROUTATIC_PROXY_API_KEY` | OpenCode Go API key (**required**) | — |
| `ROUTATIC_PROXY_CONFIG` | Custom config file path | `~/.config/routatic-proxy/config.json` |
| `ROUTATIC_PROXY_HOST` | Proxy listen host | `127.0.0.1` |
| `ROUTATIC_PROXY_PORT` | Proxy listen port | `3456` |
| `ROUTATIC_PROXY_OPENCODE_URL` | OpenCode Go API endpoint | `https://opencode.ai/zen/go/v1/chat/completions` |
| `ROUTATIC_PROXY_OPENCODE_ZEN_URL` | OpenCode Zen API endpoint | `https://opencode.ai/zen/v1/chat/completions` |
| `ROUTATIC_PROXY_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error` | `info` |

Legacy equivalents such as `OC_GO_CC_API_KEY`, `OC_GO_CC_CONFIG`, and `OC_GO_CC_PORT` continue to work. When both names are set, the `ROUTATIC_PROXY_*` value wins.

## Hot Reload

Expand Down Expand Up @@ -232,7 +236,7 @@ When a request arrives, the proxy checks `model_overrides[<model>]` **first**. I

Each entry accepts the same fields as a `ModelConfig` (`provider`, `model_id`, `temperature`, `max_tokens`, `reasoning_effort`, `thinking`, etc.). `model_id` is required; `provider` must be `"opencode-go"` or `"opencode-zen"` (or omitted to inherit the default).

See `oc-go-cc models` for the complete list of available Zen models across all endpoint types (Claude, GPT, Gemini, and free-tier).
See `routatic-proxy models` for the complete list of available Zen models across all endpoint types (Claude, GPT, Gemini, and free-tier).

### Routing precedence

Expand Down
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ Run a single test: `go test ./internal/router/ -v`

```
┌─────────────┐ Anthropic API ┌─────────────┐ OpenAI/Gemini/Responses ┌─────────────┐
│ Claude Code ├──────────────────────►│ oc-go-cc ├─────────────────────────────►│ OpenCode │
│ Claude Code ├──────────────────────►│ routatic-proxy ├─────────────────────────────►│ OpenCode │
│ (CLI) │ POST /v1/messages │ (Proxy) │ Multiple endpoint formats │ (Upstream) │
│ │◄──────────────────────┤ │◄─────────────────────────────┤ │
└─────────────┘ Anthropic SSE └─────────────┘ Format-appropriate SSE └─────────────┘
```

1. Claude Code sends a request in [Anthropic Messages API](https://docs.anthropic.com/en/api/messages) format
2. oc-go-cc parses the request, counts tokens, and selects a model via routing rules
2. routatic-proxy parses the request, counts tokens, and selects a model via routing rules
3. Based on the model's provider and endpoint type, the request is transformed to the appropriate format:
- **OpenAI Chat Completions** — for most OpenCode Go and Zen models
- **Anthropic Messages** — for MiniMax models (sent directly without transformation)
Expand Down Expand Up @@ -79,7 +79,7 @@ For Claude Code and other agentic coding workflows, configure DeepSeek V4 models
}
```

`oc-go-cc` forwards these fields to OpenCode Go as OpenAI Chat Completions parameters:
`routatic-proxy` forwards these fields to OpenCode Go as OpenAI Chat Completions parameters:

- `reasoning_effort`: controls DeepSeek V4 thinking effort (`high` or `max`)
- `thinking`: enables or disables DeepSeek V4 thinking mode
Expand All @@ -89,7 +89,7 @@ DeepSeek V4 thinking responses are returned as OpenAI `reasoning_content` and tr
## Architecture

```
cmd/oc-go-cc/main.go CLI entry point (cobra commands)
cmd/routatic-proxy/main.go CLI entry point (cobra commands)
internal/
├── config/
│ ├── config.go Config types (OpenCodeGoConfig, OpenCodeZenConfig)
Expand Down Expand Up @@ -130,7 +130,7 @@ configs/
- **Polymorphic field handling**: Anthropic's `system` and `content` fields accept both strings and arrays. We use `json.RawMessage` with accessor methods (`SystemText()`, `ContentBlocks()`) to handle both formats correctly.
- **Real-time stream proxying**: SSE events are transformed in-flight, not buffered. This means Claude Code sees responses as they arrive from upstream.
- **Circuit breaker per model**: Each model gets its own circuit breaker. After 3 consecutive failures, the model is skipped for 30 seconds, then tested again.
- **Environment variable interpolation**: Config values like `"${OC_GO_CC_API_KEY}"` are resolved at load time, so you never need to put secrets in the config file.
- **Environment variable interpolation**: Config values like `"${ROUTATIC_PROXY_API_KEY}"` are resolved at load time, so you never need to put secrets in the config file.
- **Provider-aware routing**: The `provider` field in model config determines which upstream service to use (Go or Zen). Zen models are further classified by endpoint type (Chat Completions, Anthropic, Responses, Gemini).

## API Endpoints
Expand Down
Loading
Loading