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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ tests/
*.md

.eslintcache

web/node_modules
web/dist
10 changes: 10 additions & 0 deletions .idea/.gitignore

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

59 changes: 59 additions & 0 deletions .idea/codeStyles/Project.xml

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

5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

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

8 changes: 8 additions & 0 deletions .idea/copilot-api.iml

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

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

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

8 changes: 8 additions & 0 deletions .idea/modules.xml

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

6 changes: 6 additions & 0 deletions .idea/vcs.xml

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

55 changes: 55 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

copilot-api is a reverse proxy that exposes GitHub Copilot as OpenAI and Anthropic-compatible API endpoints. Built with Bun, TypeScript, Hono, and citty (CLI framework).

## Commands

- **Build:** `bun run build` (tsdown bundler, outputs to `dist/`)
- **Dev:** `bun run dev` (watch mode via `bun run --watch`)
- **Start:** `bun run start` (production mode)
- **Lint:** `bun run lint` (ESLint with `@echristian/eslint-config`)
- **Typecheck:** `bun run typecheck` (tsc, no emit)
- **Test all:** `bun test`
- **Test single file:** `bun test tests/<filename>.test.ts`
- **Unused code detection:** `bun run knip`

## Architecture

### CLI Layer (`src/main.ts`)
Entry point uses citty to define subcommands: `start`, `auth`, `check-usage`, `debug`, `console`.

### Server (`src/server.ts`)
Hono app with routes mounted at both `/` and `/v1/` prefixes for compatibility:
- `POST /v1/chat/completions` — OpenAI-compatible chat completions
- `POST /v1/messages` — Anthropic-compatible messages API
- `POST /v1/messages/count_tokens` — Token counting
- `GET /v1/models` — Model listing
- `POST /v1/embeddings` — Embeddings
- `GET /usage`, `GET /token` — Monitoring endpoints

### Key Directories
- `src/routes/` — Route handlers, each in its own directory with `route.ts` + `handler.ts`
- `src/services/copilot/` — GitHub Copilot API calls (completions, embeddings, models)
- `src/services/github/` — GitHub API calls (auth, tokens, usage)
- `src/lib/` — Shared utilities (state, tokens, rate limiting, error handling, proxy)
- `src/console/` — Multi-account management mode with load balancing and web UI
- `web/` — React + Vite frontend for the console mode
- `tests/` — Bun test runner, files named `*.test.ts`

### Global State (`src/lib/state.ts`)
Mutable singleton `state` object holds runtime config: GitHub/Copilot tokens, account type, cached models, rate limit settings.

### Anthropic Translation Layer (`src/routes/messages/`)
Converts between Anthropic message format and Copilot's OpenAI-style API. Handles both streaming (`stream-translation.ts`) and non-streaming (`non-stream-translation.ts`) responses.

## Code Conventions

- ESM only, strict TypeScript — no `any`, no unused variables/imports
- Path alias: `~/*` maps to `src/*` (e.g., `import { state } from "~/lib/state"`)
- camelCase for variables/functions, PascalCase for types/classes
- Zod v4 for runtime validation
- Pre-commit hook runs lint-staged via simple-git-hooks
36 changes: 25 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
# Stage 1: Build frontend
FROM node:22-alpine AS web-builder
WORKDIR /app/web

COPY web/package.json ./
RUN npm install

COPY web/ ./
RUN npm run build

# Stage 2: Build backend
FROM oven/bun:1.2.19-alpine AS builder
WORKDIR /app

COPY ./package.json ./bun.lock ./
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

COPY . .
RUN bun run build

FROM oven/bun:1.2.19-alpine AS runner
# Stage 3: Runtime
FROM oven/bun:1.2.19-alpine
WORKDIR /app

COPY ./package.json ./bun.lock ./
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production --ignore-scripts --no-cache

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/src ./src
COPY --from=builder /app/tsconfig.json ./
COPY --from=web-builder /app/web/dist ./web/dist

EXPOSE 3000 4141

EXPOSE 4141
VOLUME /root/.local/share/copilot-api

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --spider -q http://localhost:4141/ || exit 1
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD wget --spider -q http://localhost:3000/api/config || exit 1

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
ENTRYPOINT ["bun", "run", "./src/main.ts", "console"]
CMD ["--web-port", "3000", "--proxy-port", "4141"]
39 changes: 39 additions & 0 deletions Dockerfile.console
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Stage 1: Build frontend
FROM node:22-alpine AS web-builder
WORKDIR /app/web

COPY web/package.json ./
RUN npm install

COPY web/ ./
RUN npm run build

# Stage 2: Build backend
FROM oven/bun:1.2.19-alpine AS builder
WORKDIR /app

COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

COPY . .

# Stage 3: Runtime
FROM oven/bun:1.2.19-alpine
WORKDIR /app

COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production --ignore-scripts --no-cache

COPY --from=builder /app/src ./src
COPY --from=builder /app/tsconfig.json ./
COPY --from=web-builder /app/web/dist ./web/dist

EXPOSE 3000 4141

VOLUME /root/.local/share/copilot-api

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD wget --spider -q http://localhost:3000/api/config || exit 1

ENTRYPOINT ["bun", "run", "./src/main.ts", "console"]
CMD ["--web-port", "3000", "--proxy-port", "4141"]
1 change: 1 addition & 0 deletions bun.lock

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

12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
services:
copilot-api:
image: copilot-api
ports:
- "3000:3000"
- "4141:4141"
volumes:
- copilot-api-data:/root/.local/share/copilot-api
restart: unless-stopped

volumes:
copilot-api-data:
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export default config({
prettier: {
plugins: ["prettier-plugin-packagejson"],
},
ignores: ["web/**"],
})
Loading