# ghc-tunnel

GitHub Copilot API Proxy — exposes standard **OpenAI** and **Anthropic** compatible endpoints so tools can use GitHub Copilot models.

This TypeScript implementation references the Python project at https://github.com/sxwxs/ghc-api, adding more convenient functions and easier configuration for code agents such as Claude Code and Coded.

## Quick Start

```bash
# Run directly (Node.js 18+ required)
npx ghc-tunnel

# Or install globally
npm install -g ghc-tunnel
ghc-tunnel

# Interactive setup (configures models and local settings)
ghc-tunnel --setup

# Update Claude Code settings only
ghc-tunnel --setup --claudecode
```

On first run the proxy initiates GitHub Device Flow authentication if no `GITHUB_TOKEN` is set.

## Features

- **OpenAI-compatible** `/v1/chat/completions` and `/v1/responses` endpoints (with Codex adapters: `apply_patch` tool, `X-Initiator`, context compaction)
- **Anthropic-compatible** `/v1/messages` endpoint (direct or translated)
- **Codex config auto-repair** — fills missing keys in `~/.codex/config.toml` on startup
- Automatic **model name translation** via configurable mappings
- **Streaming** support (SSE) for all endpoints
- **Disk-backed request history** with paged metadata and on-demand body viewing
- **Retry with backoff** for upstream connection errors
- **Content filtering** (system prompt manipulation, tool result cleaning)
- **Token management** with automatic refresh

## CLI Options

```
ghc-tunnel [options]

  -s, --setup             Interactive setup wizard (configure models and local settings)
      --claudecode        Update Claude Code settings only (use with --setup)
  -d, --default           Use defaults for setup and Codex config prompts
  -p, --port <port>       Port to listen on (default: 8314)
  -a, --address <addr>    Address to listen on (default: 127.0.0.1)
  -c, --config            Generate default config file
  -v, --version           Show version
  -h, --help              Show help
```

## Configuration

Config file: `~/.ghc-tunnel/config.yaml` (generated on first run or with `--config`).

See [docs/configuration.md](docs/configuration.md) for full reference.

### Token Usage Logging

Every 5 minutes, ghc-tunnel writes token usage delta (if non-zero) to:

- `~/.ghc-tunnel/token_usage.jl`

Also flushes pending usage on shutdown (`Ctrl+C`/termination/normal exit).

Each JSONL line includes:

- `timestamp` (unix seconds)
- `models` list with:
  - `model`
  - `request_count`
  - `input_tokens`
  - `output_tokens`
  - `total_tokens`

### Request History Storage

ghc-tunnel always stores completed request metadata and request/response bodies
under `~/.ghc-tunnel/requests/`. The dashboard reads metadata in pages and
loads a body only when a request is opened, so history does not remain resident
in the Node.js heap.

- The newest 10,000 completed requests are retained across restarts.
- Each request or response body is capped at 50 MB and marked when truncated.
- Streaming responses are written to disk incrementally while being forwarded.
- The dashboard initially loads a 64 KB body preview; full stored content is
  fetched only when requested.
- Bodies are formatted as JSON in the dashboard; streaming responses show raw
  events and reconstructed response content separately.

There is no total disk-size cap. Remove `~/.ghc-tunnel/requests/` while the
service is stopped if you want to clear all stored history.

## API Endpoints

| Endpoint | Description |
|----------|-------------|
| `POST /v1/chat/completions` | OpenAI chat completions |
| `POST /v1/responses` | OpenAI responses API |
| `GET /v1/models` | List available models |
| `POST /v1/messages` | Anthropic messages API |
| `GET /` | Web dashboard |
| `GET /requests` | Request browser |

## Example Usage

### OpenAI SDK

```python
from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:8314/v1",
    api_key="not-needed"
)

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}]
)
```

### Anthropic SDK

```python
import anthropic

client = anthropic.Anthropic(
    base_url="http://127.0.0.1:8314",
    api_key="not-needed"
)

message = client.messages.create(
    model="claude-sonnet-4",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}]
)
```

### cURL

```bash
curl http://127.0.0.1:8314/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello!"}]}'
```

## Documentation

- [Architecture](docs/architecture.md) — system design and data flow
- [API Reference](docs/api.md) — all HTTP endpoints
- [Configuration](docs/configuration.md) — config file, env vars, CLI options

## License

MIT
