Commit ·
ae9d2aa
0
Parent(s):
feat: initial LLM gateway proxy for Claude Code
Browse files- .dockerignore +10 -0
- .env.example +18 -0
- .gitignore +6 -0
- Dockerfile +39 -0
- README.md +158 -0
- docker-compose.yml +14 -0
- package-lock.json +1273 -0
- package.json +26 -0
- src/auth.ts +28 -0
- src/config.ts +49 -0
- src/index.ts +138 -0
- src/proxy.ts +133 -0
- src/types.ts +55 -0
- tsconfig.json +24 -0
.dockerignore
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules
|
| 2 |
+
dist
|
| 3 |
+
.env
|
| 4 |
+
.env.*
|
| 5 |
+
!.env.example
|
| 6 |
+
.git
|
| 7 |
+
.gitignore
|
| 8 |
+
*.md
|
| 9 |
+
.vscode
|
| 10 |
+
.idea
|
.env.example
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# === Required ===
|
| 2 |
+
ANTHROPIC_API_KEY=sk-ant-... # Real Anthropic API key (stays on the server)
|
| 3 |
+
PROXY_AUTH_TOKEN=your-secure-shared-secret # Token that Claude Code uses as ANTHROPIC_AUTH_TOKEN
|
| 4 |
+
|
| 5 |
+
# === Optional ===
|
| 6 |
+
PORT=7860 # Default: 7860 (Hugging Face Spaces default)
|
| 7 |
+
HOST=0.0.0.0
|
| 8 |
+
LOG_LEVEL=info # trace | debug | info | warn | error
|
| 9 |
+
|
| 10 |
+
# Security
|
| 11 |
+
RATE_LIMIT_MAX=100 # Requests per time window per IP
|
| 12 |
+
RATE_LIMIT_WINDOW_MS=60000 # Time window in ms
|
| 13 |
+
BODY_LIMIT=5242880 # Max body size in bytes (default: 5 MB)
|
| 14 |
+
CORS_ORIGIN= # Empty = disabled
|
| 15 |
+
|
| 16 |
+
# Upstream
|
| 17 |
+
ANTHROPIC_BASE_URL=https://api.anthropic.com # Overridable for testing
|
| 18 |
+
UPSTREAM_TIMEOUT_MS=300000 # Upstream request timeout (default: 5 min)
|
.gitignore
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules/
|
| 2 |
+
dist/
|
| 3 |
+
.env
|
| 4 |
+
.env.*
|
| 5 |
+
!.env.example
|
| 6 |
+
*.tsbuildinfo
|
Dockerfile
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ── Stage 1: Build ────────────────────────────────────────────────────────────
|
| 2 |
+
FROM node:20-alpine AS builder
|
| 3 |
+
|
| 4 |
+
WORKDIR /app
|
| 5 |
+
|
| 6 |
+
COPY package.json package-lock.json* ./
|
| 7 |
+
RUN npm ci --ignore-scripts
|
| 8 |
+
|
| 9 |
+
COPY tsconfig.json ./
|
| 10 |
+
COPY src/ ./src/
|
| 11 |
+
|
| 12 |
+
RUN npm run build
|
| 13 |
+
|
| 14 |
+
# ── Stage 2: Run ──────────────────────────────────────────────────────────────
|
| 15 |
+
FROM node:20-alpine AS runner
|
| 16 |
+
|
| 17 |
+
WORKDIR /app
|
| 18 |
+
|
| 19 |
+
# Hugging Face Spaces runs containers as uid 1000
|
| 20 |
+
RUN addgroup -g 1000 appgroup && \
|
| 21 |
+
adduser -u 1000 -G appgroup -s /bin/sh -D appuser && \
|
| 22 |
+
mkdir -p /data && \
|
| 23 |
+
chown -R appuser:appgroup /data /app
|
| 24 |
+
|
| 25 |
+
COPY --from=builder --chown=appuser:appgroup /app/package.json /app/package-lock.json* ./
|
| 26 |
+
RUN npm ci --omit=dev --ignore-scripts
|
| 27 |
+
|
| 28 |
+
COPY --from=builder --chown=appuser:appgroup /app/dist/ ./dist/
|
| 29 |
+
|
| 30 |
+
USER appuser
|
| 31 |
+
|
| 32 |
+
# Hugging Face Spaces default port
|
| 33 |
+
ENV PORT=7860
|
| 34 |
+
ENV HOST=0.0.0.0
|
| 35 |
+
ENV NODE_ENV=production
|
| 36 |
+
|
| 37 |
+
EXPOSE 7860
|
| 38 |
+
|
| 39 |
+
CMD ["node", "dist/index.js"]
|
README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AI Proxy – LLM Gateway for Claude Code
|
| 2 |
+
|
| 3 |
+
A transparent relay proxy that forwards [Anthropic Messages API](https://docs.anthropic.com/en/api/messages) requests from **Claude Code CLI** through environments where `api.anthropic.com` is blocked (e.g. corporate firewalls). The proxy performs auth-swap, header hygiene, and supports SSE streaming.
|
| 4 |
+
|
| 5 |
+
```
|
| 6 |
+
Claude Code CLI → AI Proxy (this server) → api.anthropic.com
|
| 7 |
+
ANTHROPIC_BASE_URL Upstream
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
## Features
|
| 11 |
+
|
| 12 |
+
- **1:1 transparent relay** – no request/response body modification
|
| 13 |
+
- **SSE streaming** – chunk-by-chunk forwarding, zero buffering
|
| 14 |
+
- **Auth swap** – client authenticates with a shared token; server injects the real API key
|
| 15 |
+
- **Header hygiene** – strips hop-by-hop headers, authorization, and client-sent API keys
|
| 16 |
+
- **Rate limiting** – per-IP, configurable window and max
|
| 17 |
+
- **Defensive headers** – `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`
|
| 18 |
+
- **Graceful shutdown** – finishes in-flight streams before exiting
|
| 19 |
+
- **Hugging Face Spaces ready** – Docker configuration pre-set for HF Spaces deployment
|
| 20 |
+
|
| 21 |
+
## Quick Start (Local)
|
| 22 |
+
|
| 23 |
+
```bash
|
| 24 |
+
# 1. Clone & install
|
| 25 |
+
git clone <your-repo-url> && cd ai-proxy
|
| 26 |
+
npm install
|
| 27 |
+
|
| 28 |
+
# 2. Configure – copy and edit
|
| 29 |
+
cp .env.example .env
|
| 30 |
+
# Set ANTHROPIC_API_KEY and PROXY_AUTH_TOKEN in .env
|
| 31 |
+
|
| 32 |
+
# 3. Run
|
| 33 |
+
npm run dev
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
Health check: `curl http://localhost:7860/health`
|
| 37 |
+
|
| 38 |
+
## Environment Variables
|
| 39 |
+
|
| 40 |
+
| Variable | Required | Default | Description |
|
| 41 |
+
|---|---|---|---|
|
| 42 |
+
| `ANTHROPIC_API_KEY` | ✅ | – | Real Anthropic API key (stays on the server) |
|
| 43 |
+
| `PROXY_AUTH_TOKEN` | ✅ | – | Shared secret for client authentication |
|
| 44 |
+
| `PORT` | – | `7860` | Server port |
|
| 45 |
+
| `HOST` | – | `0.0.0.0` | Server bind address |
|
| 46 |
+
| `LOG_LEVEL` | – | `info` | `trace` \| `debug` \| `info` \| `warn` \| `error` |
|
| 47 |
+
| `RATE_LIMIT_MAX` | – | `100` | Requests per time window per IP |
|
| 48 |
+
| `RATE_LIMIT_WINDOW_MS` | – | `60000` | Rate limit window (ms) |
|
| 49 |
+
| `BODY_LIMIT` | – | `5242880` | Max request body size (bytes, 5 MB) |
|
| 50 |
+
| `CORS_ORIGIN` | – | *(disabled)* | CORS origin (e.g. `*` or `https://example.com`) |
|
| 51 |
+
| `ANTHROPIC_BASE_URL` | – | `https://api.anthropic.com` | Upstream URL (override for testing) |
|
| 52 |
+
| `UPSTREAM_TIMEOUT_MS` | – | `300000` | Upstream request timeout (5 min) |
|
| 53 |
+
|
| 54 |
+
## API Endpoints
|
| 55 |
+
|
| 56 |
+
| Method | Path | Auth | Description |
|
| 57 |
+
|---|---|---|---|
|
| 58 |
+
| `GET` | `/health` | No | Health check → `{"status":"ok"}` |
|
| 59 |
+
| `POST` | `/v1/messages` | Yes | Chat completions (relayed 1:1) |
|
| 60 |
+
| `POST` | `/v1/messages/count_tokens` | Yes | Token counting (relayed 1:1) |
|
| 61 |
+
|
| 62 |
+
All other routes return `404`. Non-POST methods on API routes return `405`.
|
| 63 |
+
|
| 64 |
+
## Docker
|
| 65 |
+
|
| 66 |
+
### Local (docker compose)
|
| 67 |
+
|
| 68 |
+
```bash
|
| 69 |
+
cp .env.example .env
|
| 70 |
+
# Edit .env with your keys
|
| 71 |
+
docker compose up --build
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
### Hugging Face Spaces
|
| 75 |
+
|
| 76 |
+
1. Create a new Space on [huggingface.co/new-space](https://huggingface.co/new-space):
|
| 77 |
+
- **SDK**: Docker
|
| 78 |
+
- **Visibility**: Private (recommended – this handles API keys)
|
| 79 |
+
|
| 80 |
+
2. Push this repository to the Space:
|
| 81 |
+
```bash
|
| 82 |
+
git remote add hf https://huggingface.co/spaces/<YOUR_USER>/<SPACE_NAME>
|
| 83 |
+
git push hf main
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
3. Configure **Secrets** in Space Settings → Repository secrets:
|
| 87 |
+
- `ANTHROPIC_API_KEY` = your real Anthropic key
|
| 88 |
+
- `PROXY_AUTH_TOKEN` = your chosen shared secret
|
| 89 |
+
|
| 90 |
+
4. The Space will build and deploy automatically. Your proxy URL will be:
|
| 91 |
+
```
|
| 92 |
+
https://<YOUR_USER>-<SPACE_NAME>.hf.space
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
> **Note:** HF Spaces secrets become environment variables at runtime. The Dockerfile already defaults to port 7860 and runs as uid 1000 as required by the platform.
|
| 96 |
+
|
| 97 |
+
## Claude Code Client Configuration
|
| 98 |
+
|
| 99 |
+
### Option 1: Environment Variables
|
| 100 |
+
|
| 101 |
+
```bash
|
| 102 |
+
export ANTHROPIC_BASE_URL=https://your-server.example.com
|
| 103 |
+
export ANTHROPIC_AUTH_TOKEN=your-proxy-auth-token
|
| 104 |
+
claude
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
### Option 2: Persistent (settings.json)
|
| 108 |
+
|
| 109 |
+
```json
|
| 110 |
+
// ~/.claude/settings.json
|
| 111 |
+
{
|
| 112 |
+
"env": {
|
| 113 |
+
"ANTHROPIC_BASE_URL": "https://your-server.example.com",
|
| 114 |
+
"ANTHROPIC_AUTH_TOKEN": "your-proxy-auth-token"
|
| 115 |
+
}
|
| 116 |
+
}
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
### Option 3: Managed Settings (Enterprise)
|
| 120 |
+
|
| 121 |
+
```json
|
| 122 |
+
// macOS: /Library/Application Support/ClaudeCode/managed-settings.json
|
| 123 |
+
// Linux: /etc/claude-code/managed-settings.json
|
| 124 |
+
{
|
| 125 |
+
"env": {
|
| 126 |
+
"ANTHROPIC_BASE_URL": "https://your-server.example.com"
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
+
### Test the Connection
|
| 132 |
+
|
| 133 |
+
```bash
|
| 134 |
+
# Health check
|
| 135 |
+
curl https://your-server.example.com/health
|
| 136 |
+
|
| 137 |
+
# Test message
|
| 138 |
+
curl -X POST https://your-server.example.com/v1/messages \
|
| 139 |
+
-H "Authorization: Bearer your-proxy-auth-token" \
|
| 140 |
+
-H "Content-Type: application/json" \
|
| 141 |
+
-H "anthropic-version: 2023-06-01" \
|
| 142 |
+
-d '{
|
| 143 |
+
"model": "claude-sonnet-4-20250514",
|
| 144 |
+
"max_tokens": 100,
|
| 145 |
+
"messages": [{"role": "user", "content": "Hi"}]
|
| 146 |
+
}'
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
## Tech Stack
|
| 150 |
+
|
| 151 |
+
- **Runtime:** Node.js ≥ 20
|
| 152 |
+
- **Framework:** [Fastify](https://fastify.dev/) 5
|
| 153 |
+
- **HTTP Client:** [undici](https://undici.nodejs.org/)
|
| 154 |
+
- **Language:** TypeScript (strict mode)
|
| 155 |
+
|
| 156 |
+
## License
|
| 157 |
+
|
| 158 |
+
MIT
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
services:
|
| 2 |
+
ai-proxy:
|
| 3 |
+
build: .
|
| 4 |
+
ports:
|
| 5 |
+
- "${PORT:-7860}:${PORT:-7860}"
|
| 6 |
+
env_file:
|
| 7 |
+
- .env
|
| 8 |
+
restart: unless-stopped
|
| 9 |
+
healthcheck:
|
| 10 |
+
test: [ "CMD", "wget", "--spider", "-q", "http://localhost:${PORT:-7860}/health" ]
|
| 11 |
+
interval: 30s
|
| 12 |
+
timeout: 5s
|
| 13 |
+
retries: 3
|
| 14 |
+
start_period: 10s
|
package-lock.json
ADDED
|
@@ -0,0 +1,1273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "ai-proxy",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "ai-proxy",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"@fastify/rate-limit": "^10.2.2",
|
| 12 |
+
"dotenv": "^16.5.0",
|
| 13 |
+
"fastify": "^5.3.3",
|
| 14 |
+
"undici": "^7.5.0"
|
| 15 |
+
},
|
| 16 |
+
"devDependencies": {
|
| 17 |
+
"@types/node": "^22.13.10",
|
| 18 |
+
"tsx": "^4.19.3",
|
| 19 |
+
"typescript": "^5.8.2"
|
| 20 |
+
},
|
| 21 |
+
"engines": {
|
| 22 |
+
"node": ">=20"
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
"node_modules/@esbuild/aix-ppc64": {
|
| 26 |
+
"version": "0.27.3",
|
| 27 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
|
| 28 |
+
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
|
| 29 |
+
"cpu": [
|
| 30 |
+
"ppc64"
|
| 31 |
+
],
|
| 32 |
+
"dev": true,
|
| 33 |
+
"license": "MIT",
|
| 34 |
+
"optional": true,
|
| 35 |
+
"os": [
|
| 36 |
+
"aix"
|
| 37 |
+
],
|
| 38 |
+
"engines": {
|
| 39 |
+
"node": ">=18"
|
| 40 |
+
}
|
| 41 |
+
},
|
| 42 |
+
"node_modules/@esbuild/android-arm": {
|
| 43 |
+
"version": "0.27.3",
|
| 44 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
|
| 45 |
+
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
|
| 46 |
+
"cpu": [
|
| 47 |
+
"arm"
|
| 48 |
+
],
|
| 49 |
+
"dev": true,
|
| 50 |
+
"license": "MIT",
|
| 51 |
+
"optional": true,
|
| 52 |
+
"os": [
|
| 53 |
+
"android"
|
| 54 |
+
],
|
| 55 |
+
"engines": {
|
| 56 |
+
"node": ">=18"
|
| 57 |
+
}
|
| 58 |
+
},
|
| 59 |
+
"node_modules/@esbuild/android-arm64": {
|
| 60 |
+
"version": "0.27.3",
|
| 61 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
|
| 62 |
+
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
|
| 63 |
+
"cpu": [
|
| 64 |
+
"arm64"
|
| 65 |
+
],
|
| 66 |
+
"dev": true,
|
| 67 |
+
"license": "MIT",
|
| 68 |
+
"optional": true,
|
| 69 |
+
"os": [
|
| 70 |
+
"android"
|
| 71 |
+
],
|
| 72 |
+
"engines": {
|
| 73 |
+
"node": ">=18"
|
| 74 |
+
}
|
| 75 |
+
},
|
| 76 |
+
"node_modules/@esbuild/android-x64": {
|
| 77 |
+
"version": "0.27.3",
|
| 78 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
|
| 79 |
+
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
|
| 80 |
+
"cpu": [
|
| 81 |
+
"x64"
|
| 82 |
+
],
|
| 83 |
+
"dev": true,
|
| 84 |
+
"license": "MIT",
|
| 85 |
+
"optional": true,
|
| 86 |
+
"os": [
|
| 87 |
+
"android"
|
| 88 |
+
],
|
| 89 |
+
"engines": {
|
| 90 |
+
"node": ">=18"
|
| 91 |
+
}
|
| 92 |
+
},
|
| 93 |
+
"node_modules/@esbuild/darwin-arm64": {
|
| 94 |
+
"version": "0.27.3",
|
| 95 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
|
| 96 |
+
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
|
| 97 |
+
"cpu": [
|
| 98 |
+
"arm64"
|
| 99 |
+
],
|
| 100 |
+
"dev": true,
|
| 101 |
+
"license": "MIT",
|
| 102 |
+
"optional": true,
|
| 103 |
+
"os": [
|
| 104 |
+
"darwin"
|
| 105 |
+
],
|
| 106 |
+
"engines": {
|
| 107 |
+
"node": ">=18"
|
| 108 |
+
}
|
| 109 |
+
},
|
| 110 |
+
"node_modules/@esbuild/darwin-x64": {
|
| 111 |
+
"version": "0.27.3",
|
| 112 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
|
| 113 |
+
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
|
| 114 |
+
"cpu": [
|
| 115 |
+
"x64"
|
| 116 |
+
],
|
| 117 |
+
"dev": true,
|
| 118 |
+
"license": "MIT",
|
| 119 |
+
"optional": true,
|
| 120 |
+
"os": [
|
| 121 |
+
"darwin"
|
| 122 |
+
],
|
| 123 |
+
"engines": {
|
| 124 |
+
"node": ">=18"
|
| 125 |
+
}
|
| 126 |
+
},
|
| 127 |
+
"node_modules/@esbuild/freebsd-arm64": {
|
| 128 |
+
"version": "0.27.3",
|
| 129 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
|
| 130 |
+
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
|
| 131 |
+
"cpu": [
|
| 132 |
+
"arm64"
|
| 133 |
+
],
|
| 134 |
+
"dev": true,
|
| 135 |
+
"license": "MIT",
|
| 136 |
+
"optional": true,
|
| 137 |
+
"os": [
|
| 138 |
+
"freebsd"
|
| 139 |
+
],
|
| 140 |
+
"engines": {
|
| 141 |
+
"node": ">=18"
|
| 142 |
+
}
|
| 143 |
+
},
|
| 144 |
+
"node_modules/@esbuild/freebsd-x64": {
|
| 145 |
+
"version": "0.27.3",
|
| 146 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
|
| 147 |
+
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
|
| 148 |
+
"cpu": [
|
| 149 |
+
"x64"
|
| 150 |
+
],
|
| 151 |
+
"dev": true,
|
| 152 |
+
"license": "MIT",
|
| 153 |
+
"optional": true,
|
| 154 |
+
"os": [
|
| 155 |
+
"freebsd"
|
| 156 |
+
],
|
| 157 |
+
"engines": {
|
| 158 |
+
"node": ">=18"
|
| 159 |
+
}
|
| 160 |
+
},
|
| 161 |
+
"node_modules/@esbuild/linux-arm": {
|
| 162 |
+
"version": "0.27.3",
|
| 163 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
|
| 164 |
+
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
|
| 165 |
+
"cpu": [
|
| 166 |
+
"arm"
|
| 167 |
+
],
|
| 168 |
+
"dev": true,
|
| 169 |
+
"license": "MIT",
|
| 170 |
+
"optional": true,
|
| 171 |
+
"os": [
|
| 172 |
+
"linux"
|
| 173 |
+
],
|
| 174 |
+
"engines": {
|
| 175 |
+
"node": ">=18"
|
| 176 |
+
}
|
| 177 |
+
},
|
| 178 |
+
"node_modules/@esbuild/linux-arm64": {
|
| 179 |
+
"version": "0.27.3",
|
| 180 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
|
| 181 |
+
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
|
| 182 |
+
"cpu": [
|
| 183 |
+
"arm64"
|
| 184 |
+
],
|
| 185 |
+
"dev": true,
|
| 186 |
+
"license": "MIT",
|
| 187 |
+
"optional": true,
|
| 188 |
+
"os": [
|
| 189 |
+
"linux"
|
| 190 |
+
],
|
| 191 |
+
"engines": {
|
| 192 |
+
"node": ">=18"
|
| 193 |
+
}
|
| 194 |
+
},
|
| 195 |
+
"node_modules/@esbuild/linux-ia32": {
|
| 196 |
+
"version": "0.27.3",
|
| 197 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
|
| 198 |
+
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
|
| 199 |
+
"cpu": [
|
| 200 |
+
"ia32"
|
| 201 |
+
],
|
| 202 |
+
"dev": true,
|
| 203 |
+
"license": "MIT",
|
| 204 |
+
"optional": true,
|
| 205 |
+
"os": [
|
| 206 |
+
"linux"
|
| 207 |
+
],
|
| 208 |
+
"engines": {
|
| 209 |
+
"node": ">=18"
|
| 210 |
+
}
|
| 211 |
+
},
|
| 212 |
+
"node_modules/@esbuild/linux-loong64": {
|
| 213 |
+
"version": "0.27.3",
|
| 214 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
|
| 215 |
+
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
|
| 216 |
+
"cpu": [
|
| 217 |
+
"loong64"
|
| 218 |
+
],
|
| 219 |
+
"dev": true,
|
| 220 |
+
"license": "MIT",
|
| 221 |
+
"optional": true,
|
| 222 |
+
"os": [
|
| 223 |
+
"linux"
|
| 224 |
+
],
|
| 225 |
+
"engines": {
|
| 226 |
+
"node": ">=18"
|
| 227 |
+
}
|
| 228 |
+
},
|
| 229 |
+
"node_modules/@esbuild/linux-mips64el": {
|
| 230 |
+
"version": "0.27.3",
|
| 231 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
|
| 232 |
+
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
|
| 233 |
+
"cpu": [
|
| 234 |
+
"mips64el"
|
| 235 |
+
],
|
| 236 |
+
"dev": true,
|
| 237 |
+
"license": "MIT",
|
| 238 |
+
"optional": true,
|
| 239 |
+
"os": [
|
| 240 |
+
"linux"
|
| 241 |
+
],
|
| 242 |
+
"engines": {
|
| 243 |
+
"node": ">=18"
|
| 244 |
+
}
|
| 245 |
+
},
|
| 246 |
+
"node_modules/@esbuild/linux-ppc64": {
|
| 247 |
+
"version": "0.27.3",
|
| 248 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
|
| 249 |
+
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
|
| 250 |
+
"cpu": [
|
| 251 |
+
"ppc64"
|
| 252 |
+
],
|
| 253 |
+
"dev": true,
|
| 254 |
+
"license": "MIT",
|
| 255 |
+
"optional": true,
|
| 256 |
+
"os": [
|
| 257 |
+
"linux"
|
| 258 |
+
],
|
| 259 |
+
"engines": {
|
| 260 |
+
"node": ">=18"
|
| 261 |
+
}
|
| 262 |
+
},
|
| 263 |
+
"node_modules/@esbuild/linux-riscv64": {
|
| 264 |
+
"version": "0.27.3",
|
| 265 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
|
| 266 |
+
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
|
| 267 |
+
"cpu": [
|
| 268 |
+
"riscv64"
|
| 269 |
+
],
|
| 270 |
+
"dev": true,
|
| 271 |
+
"license": "MIT",
|
| 272 |
+
"optional": true,
|
| 273 |
+
"os": [
|
| 274 |
+
"linux"
|
| 275 |
+
],
|
| 276 |
+
"engines": {
|
| 277 |
+
"node": ">=18"
|
| 278 |
+
}
|
| 279 |
+
},
|
| 280 |
+
"node_modules/@esbuild/linux-s390x": {
|
| 281 |
+
"version": "0.27.3",
|
| 282 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
|
| 283 |
+
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
|
| 284 |
+
"cpu": [
|
| 285 |
+
"s390x"
|
| 286 |
+
],
|
| 287 |
+
"dev": true,
|
| 288 |
+
"license": "MIT",
|
| 289 |
+
"optional": true,
|
| 290 |
+
"os": [
|
| 291 |
+
"linux"
|
| 292 |
+
],
|
| 293 |
+
"engines": {
|
| 294 |
+
"node": ">=18"
|
| 295 |
+
}
|
| 296 |
+
},
|
| 297 |
+
"node_modules/@esbuild/linux-x64": {
|
| 298 |
+
"version": "0.27.3",
|
| 299 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
|
| 300 |
+
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
|
| 301 |
+
"cpu": [
|
| 302 |
+
"x64"
|
| 303 |
+
],
|
| 304 |
+
"dev": true,
|
| 305 |
+
"license": "MIT",
|
| 306 |
+
"optional": true,
|
| 307 |
+
"os": [
|
| 308 |
+
"linux"
|
| 309 |
+
],
|
| 310 |
+
"engines": {
|
| 311 |
+
"node": ">=18"
|
| 312 |
+
}
|
| 313 |
+
},
|
| 314 |
+
"node_modules/@esbuild/netbsd-arm64": {
|
| 315 |
+
"version": "0.27.3",
|
| 316 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
|
| 317 |
+
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
|
| 318 |
+
"cpu": [
|
| 319 |
+
"arm64"
|
| 320 |
+
],
|
| 321 |
+
"dev": true,
|
| 322 |
+
"license": "MIT",
|
| 323 |
+
"optional": true,
|
| 324 |
+
"os": [
|
| 325 |
+
"netbsd"
|
| 326 |
+
],
|
| 327 |
+
"engines": {
|
| 328 |
+
"node": ">=18"
|
| 329 |
+
}
|
| 330 |
+
},
|
| 331 |
+
"node_modules/@esbuild/netbsd-x64": {
|
| 332 |
+
"version": "0.27.3",
|
| 333 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
|
| 334 |
+
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
|
| 335 |
+
"cpu": [
|
| 336 |
+
"x64"
|
| 337 |
+
],
|
| 338 |
+
"dev": true,
|
| 339 |
+
"license": "MIT",
|
| 340 |
+
"optional": true,
|
| 341 |
+
"os": [
|
| 342 |
+
"netbsd"
|
| 343 |
+
],
|
| 344 |
+
"engines": {
|
| 345 |
+
"node": ">=18"
|
| 346 |
+
}
|
| 347 |
+
},
|
| 348 |
+
"node_modules/@esbuild/openbsd-arm64": {
|
| 349 |
+
"version": "0.27.3",
|
| 350 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
|
| 351 |
+
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
|
| 352 |
+
"cpu": [
|
| 353 |
+
"arm64"
|
| 354 |
+
],
|
| 355 |
+
"dev": true,
|
| 356 |
+
"license": "MIT",
|
| 357 |
+
"optional": true,
|
| 358 |
+
"os": [
|
| 359 |
+
"openbsd"
|
| 360 |
+
],
|
| 361 |
+
"engines": {
|
| 362 |
+
"node": ">=18"
|
| 363 |
+
}
|
| 364 |
+
},
|
| 365 |
+
"node_modules/@esbuild/openbsd-x64": {
|
| 366 |
+
"version": "0.27.3",
|
| 367 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
|
| 368 |
+
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
|
| 369 |
+
"cpu": [
|
| 370 |
+
"x64"
|
| 371 |
+
],
|
| 372 |
+
"dev": true,
|
| 373 |
+
"license": "MIT",
|
| 374 |
+
"optional": true,
|
| 375 |
+
"os": [
|
| 376 |
+
"openbsd"
|
| 377 |
+
],
|
| 378 |
+
"engines": {
|
| 379 |
+
"node": ">=18"
|
| 380 |
+
}
|
| 381 |
+
},
|
| 382 |
+
"node_modules/@esbuild/openharmony-arm64": {
|
| 383 |
+
"version": "0.27.3",
|
| 384 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
|
| 385 |
+
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
|
| 386 |
+
"cpu": [
|
| 387 |
+
"arm64"
|
| 388 |
+
],
|
| 389 |
+
"dev": true,
|
| 390 |
+
"license": "MIT",
|
| 391 |
+
"optional": true,
|
| 392 |
+
"os": [
|
| 393 |
+
"openharmony"
|
| 394 |
+
],
|
| 395 |
+
"engines": {
|
| 396 |
+
"node": ">=18"
|
| 397 |
+
}
|
| 398 |
+
},
|
| 399 |
+
"node_modules/@esbuild/sunos-x64": {
|
| 400 |
+
"version": "0.27.3",
|
| 401 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
|
| 402 |
+
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
|
| 403 |
+
"cpu": [
|
| 404 |
+
"x64"
|
| 405 |
+
],
|
| 406 |
+
"dev": true,
|
| 407 |
+
"license": "MIT",
|
| 408 |
+
"optional": true,
|
| 409 |
+
"os": [
|
| 410 |
+
"sunos"
|
| 411 |
+
],
|
| 412 |
+
"engines": {
|
| 413 |
+
"node": ">=18"
|
| 414 |
+
}
|
| 415 |
+
},
|
| 416 |
+
"node_modules/@esbuild/win32-arm64": {
|
| 417 |
+
"version": "0.27.3",
|
| 418 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
|
| 419 |
+
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
|
| 420 |
+
"cpu": [
|
| 421 |
+
"arm64"
|
| 422 |
+
],
|
| 423 |
+
"dev": true,
|
| 424 |
+
"license": "MIT",
|
| 425 |
+
"optional": true,
|
| 426 |
+
"os": [
|
| 427 |
+
"win32"
|
| 428 |
+
],
|
| 429 |
+
"engines": {
|
| 430 |
+
"node": ">=18"
|
| 431 |
+
}
|
| 432 |
+
},
|
| 433 |
+
"node_modules/@esbuild/win32-ia32": {
|
| 434 |
+
"version": "0.27.3",
|
| 435 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
|
| 436 |
+
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
|
| 437 |
+
"cpu": [
|
| 438 |
+
"ia32"
|
| 439 |
+
],
|
| 440 |
+
"dev": true,
|
| 441 |
+
"license": "MIT",
|
| 442 |
+
"optional": true,
|
| 443 |
+
"os": [
|
| 444 |
+
"win32"
|
| 445 |
+
],
|
| 446 |
+
"engines": {
|
| 447 |
+
"node": ">=18"
|
| 448 |
+
}
|
| 449 |
+
},
|
| 450 |
+
"node_modules/@esbuild/win32-x64": {
|
| 451 |
+
"version": "0.27.3",
|
| 452 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
|
| 453 |
+
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
|
| 454 |
+
"cpu": [
|
| 455 |
+
"x64"
|
| 456 |
+
],
|
| 457 |
+
"dev": true,
|
| 458 |
+
"license": "MIT",
|
| 459 |
+
"optional": true,
|
| 460 |
+
"os": [
|
| 461 |
+
"win32"
|
| 462 |
+
],
|
| 463 |
+
"engines": {
|
| 464 |
+
"node": ">=18"
|
| 465 |
+
}
|
| 466 |
+
},
|
| 467 |
+
"node_modules/@fastify/ajv-compiler": {
|
| 468 |
+
"version": "4.0.5",
|
| 469 |
+
"resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz",
|
| 470 |
+
"integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==",
|
| 471 |
+
"funding": [
|
| 472 |
+
{
|
| 473 |
+
"type": "github",
|
| 474 |
+
"url": "https://github.com/sponsors/fastify"
|
| 475 |
+
},
|
| 476 |
+
{
|
| 477 |
+
"type": "opencollective",
|
| 478 |
+
"url": "https://opencollective.com/fastify"
|
| 479 |
+
}
|
| 480 |
+
],
|
| 481 |
+
"license": "MIT",
|
| 482 |
+
"dependencies": {
|
| 483 |
+
"ajv": "^8.12.0",
|
| 484 |
+
"ajv-formats": "^3.0.1",
|
| 485 |
+
"fast-uri": "^3.0.0"
|
| 486 |
+
}
|
| 487 |
+
},
|
| 488 |
+
"node_modules/@fastify/error": {
|
| 489 |
+
"version": "4.2.0",
|
| 490 |
+
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz",
|
| 491 |
+
"integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==",
|
| 492 |
+
"funding": [
|
| 493 |
+
{
|
| 494 |
+
"type": "github",
|
| 495 |
+
"url": "https://github.com/sponsors/fastify"
|
| 496 |
+
},
|
| 497 |
+
{
|
| 498 |
+
"type": "opencollective",
|
| 499 |
+
"url": "https://opencollective.com/fastify"
|
| 500 |
+
}
|
| 501 |
+
],
|
| 502 |
+
"license": "MIT"
|
| 503 |
+
},
|
| 504 |
+
"node_modules/@fastify/fast-json-stringify-compiler": {
|
| 505 |
+
"version": "5.0.3",
|
| 506 |
+
"resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz",
|
| 507 |
+
"integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==",
|
| 508 |
+
"funding": [
|
| 509 |
+
{
|
| 510 |
+
"type": "github",
|
| 511 |
+
"url": "https://github.com/sponsors/fastify"
|
| 512 |
+
},
|
| 513 |
+
{
|
| 514 |
+
"type": "opencollective",
|
| 515 |
+
"url": "https://opencollective.com/fastify"
|
| 516 |
+
}
|
| 517 |
+
],
|
| 518 |
+
"license": "MIT",
|
| 519 |
+
"dependencies": {
|
| 520 |
+
"fast-json-stringify": "^6.0.0"
|
| 521 |
+
}
|
| 522 |
+
},
|
| 523 |
+
"node_modules/@fastify/forwarded": {
|
| 524 |
+
"version": "3.0.1",
|
| 525 |
+
"resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz",
|
| 526 |
+
"integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==",
|
| 527 |
+
"funding": [
|
| 528 |
+
{
|
| 529 |
+
"type": "github",
|
| 530 |
+
"url": "https://github.com/sponsors/fastify"
|
| 531 |
+
},
|
| 532 |
+
{
|
| 533 |
+
"type": "opencollective",
|
| 534 |
+
"url": "https://opencollective.com/fastify"
|
| 535 |
+
}
|
| 536 |
+
],
|
| 537 |
+
"license": "MIT"
|
| 538 |
+
},
|
| 539 |
+
"node_modules/@fastify/merge-json-schemas": {
|
| 540 |
+
"version": "0.2.1",
|
| 541 |
+
"resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz",
|
| 542 |
+
"integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==",
|
| 543 |
+
"funding": [
|
| 544 |
+
{
|
| 545 |
+
"type": "github",
|
| 546 |
+
"url": "https://github.com/sponsors/fastify"
|
| 547 |
+
},
|
| 548 |
+
{
|
| 549 |
+
"type": "opencollective",
|
| 550 |
+
"url": "https://opencollective.com/fastify"
|
| 551 |
+
}
|
| 552 |
+
],
|
| 553 |
+
"license": "MIT",
|
| 554 |
+
"dependencies": {
|
| 555 |
+
"dequal": "^2.0.3"
|
| 556 |
+
}
|
| 557 |
+
},
|
| 558 |
+
"node_modules/@fastify/proxy-addr": {
|
| 559 |
+
"version": "5.1.0",
|
| 560 |
+
"resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz",
|
| 561 |
+
"integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==",
|
| 562 |
+
"funding": [
|
| 563 |
+
{
|
| 564 |
+
"type": "github",
|
| 565 |
+
"url": "https://github.com/sponsors/fastify"
|
| 566 |
+
},
|
| 567 |
+
{
|
| 568 |
+
"type": "opencollective",
|
| 569 |
+
"url": "https://opencollective.com/fastify"
|
| 570 |
+
}
|
| 571 |
+
],
|
| 572 |
+
"license": "MIT",
|
| 573 |
+
"dependencies": {
|
| 574 |
+
"@fastify/forwarded": "^3.0.0",
|
| 575 |
+
"ipaddr.js": "^2.1.0"
|
| 576 |
+
}
|
| 577 |
+
},
|
| 578 |
+
"node_modules/@fastify/rate-limit": {
|
| 579 |
+
"version": "10.3.0",
|
| 580 |
+
"resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-10.3.0.tgz",
|
| 581 |
+
"integrity": "sha512-eIGkG9XKQs0nyynatApA3EVrojHOuq4l6fhB4eeCk4PIOeadvOJz9/4w3vGI44Go17uaXOWEcPkaD8kuKm7g6Q==",
|
| 582 |
+
"funding": [
|
| 583 |
+
{
|
| 584 |
+
"type": "github",
|
| 585 |
+
"url": "https://github.com/sponsors/fastify"
|
| 586 |
+
},
|
| 587 |
+
{
|
| 588 |
+
"type": "opencollective",
|
| 589 |
+
"url": "https://opencollective.com/fastify"
|
| 590 |
+
}
|
| 591 |
+
],
|
| 592 |
+
"license": "MIT",
|
| 593 |
+
"dependencies": {
|
| 594 |
+
"@lukeed/ms": "^2.0.2",
|
| 595 |
+
"fastify-plugin": "^5.0.0",
|
| 596 |
+
"toad-cache": "^3.7.0"
|
| 597 |
+
}
|
| 598 |
+
},
|
| 599 |
+
"node_modules/@lukeed/ms": {
|
| 600 |
+
"version": "2.0.2",
|
| 601 |
+
"resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
|
| 602 |
+
"integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
|
| 603 |
+
"license": "MIT",
|
| 604 |
+
"engines": {
|
| 605 |
+
"node": ">=8"
|
| 606 |
+
}
|
| 607 |
+
},
|
| 608 |
+
"node_modules/@pinojs/redact": {
|
| 609 |
+
"version": "0.4.0",
|
| 610 |
+
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
|
| 611 |
+
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
|
| 612 |
+
"license": "MIT"
|
| 613 |
+
},
|
| 614 |
+
"node_modules/@types/node": {
|
| 615 |
+
"version": "22.19.15",
|
| 616 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz",
|
| 617 |
+
"integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==",
|
| 618 |
+
"dev": true,
|
| 619 |
+
"license": "MIT",
|
| 620 |
+
"dependencies": {
|
| 621 |
+
"undici-types": "~6.21.0"
|
| 622 |
+
}
|
| 623 |
+
},
|
| 624 |
+
"node_modules/abstract-logging": {
|
| 625 |
+
"version": "2.0.1",
|
| 626 |
+
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
| 627 |
+
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
|
| 628 |
+
"license": "MIT"
|
| 629 |
+
},
|
| 630 |
+
"node_modules/ajv": {
|
| 631 |
+
"version": "8.18.0",
|
| 632 |
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
| 633 |
+
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
| 634 |
+
"license": "MIT",
|
| 635 |
+
"dependencies": {
|
| 636 |
+
"fast-deep-equal": "^3.1.3",
|
| 637 |
+
"fast-uri": "^3.0.1",
|
| 638 |
+
"json-schema-traverse": "^1.0.0",
|
| 639 |
+
"require-from-string": "^2.0.2"
|
| 640 |
+
},
|
| 641 |
+
"funding": {
|
| 642 |
+
"type": "github",
|
| 643 |
+
"url": "https://github.com/sponsors/epoberezkin"
|
| 644 |
+
}
|
| 645 |
+
},
|
| 646 |
+
"node_modules/ajv-formats": {
|
| 647 |
+
"version": "3.0.1",
|
| 648 |
+
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
|
| 649 |
+
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
|
| 650 |
+
"license": "MIT",
|
| 651 |
+
"dependencies": {
|
| 652 |
+
"ajv": "^8.0.0"
|
| 653 |
+
},
|
| 654 |
+
"peerDependencies": {
|
| 655 |
+
"ajv": "^8.0.0"
|
| 656 |
+
},
|
| 657 |
+
"peerDependenciesMeta": {
|
| 658 |
+
"ajv": {
|
| 659 |
+
"optional": true
|
| 660 |
+
}
|
| 661 |
+
}
|
| 662 |
+
},
|
| 663 |
+
"node_modules/atomic-sleep": {
|
| 664 |
+
"version": "1.0.0",
|
| 665 |
+
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
| 666 |
+
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
|
| 667 |
+
"license": "MIT",
|
| 668 |
+
"engines": {
|
| 669 |
+
"node": ">=8.0.0"
|
| 670 |
+
}
|
| 671 |
+
},
|
| 672 |
+
"node_modules/avvio": {
|
| 673 |
+
"version": "9.2.0",
|
| 674 |
+
"resolved": "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz",
|
| 675 |
+
"integrity": "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==",
|
| 676 |
+
"funding": [
|
| 677 |
+
{
|
| 678 |
+
"type": "github",
|
| 679 |
+
"url": "https://github.com/sponsors/fastify"
|
| 680 |
+
},
|
| 681 |
+
{
|
| 682 |
+
"type": "opencollective",
|
| 683 |
+
"url": "https://opencollective.com/fastify"
|
| 684 |
+
}
|
| 685 |
+
],
|
| 686 |
+
"license": "MIT",
|
| 687 |
+
"dependencies": {
|
| 688 |
+
"@fastify/error": "^4.0.0",
|
| 689 |
+
"fastq": "^1.17.1"
|
| 690 |
+
}
|
| 691 |
+
},
|
| 692 |
+
"node_modules/cookie": {
|
| 693 |
+
"version": "1.1.1",
|
| 694 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
|
| 695 |
+
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
|
| 696 |
+
"license": "MIT",
|
| 697 |
+
"engines": {
|
| 698 |
+
"node": ">=18"
|
| 699 |
+
},
|
| 700 |
+
"funding": {
|
| 701 |
+
"type": "opencollective",
|
| 702 |
+
"url": "https://opencollective.com/express"
|
| 703 |
+
}
|
| 704 |
+
},
|
| 705 |
+
"node_modules/dequal": {
|
| 706 |
+
"version": "2.0.3",
|
| 707 |
+
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
| 708 |
+
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
| 709 |
+
"license": "MIT",
|
| 710 |
+
"engines": {
|
| 711 |
+
"node": ">=6"
|
| 712 |
+
}
|
| 713 |
+
},
|
| 714 |
+
"node_modules/dotenv": {
|
| 715 |
+
"version": "16.6.1",
|
| 716 |
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
| 717 |
+
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
| 718 |
+
"license": "BSD-2-Clause",
|
| 719 |
+
"engines": {
|
| 720 |
+
"node": ">=12"
|
| 721 |
+
},
|
| 722 |
+
"funding": {
|
| 723 |
+
"url": "https://dotenvx.com"
|
| 724 |
+
}
|
| 725 |
+
},
|
| 726 |
+
"node_modules/esbuild": {
|
| 727 |
+
"version": "0.27.3",
|
| 728 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
|
| 729 |
+
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
| 730 |
+
"dev": true,
|
| 731 |
+
"hasInstallScript": true,
|
| 732 |
+
"license": "MIT",
|
| 733 |
+
"bin": {
|
| 734 |
+
"esbuild": "bin/esbuild"
|
| 735 |
+
},
|
| 736 |
+
"engines": {
|
| 737 |
+
"node": ">=18"
|
| 738 |
+
},
|
| 739 |
+
"optionalDependencies": {
|
| 740 |
+
"@esbuild/aix-ppc64": "0.27.3",
|
| 741 |
+
"@esbuild/android-arm": "0.27.3",
|
| 742 |
+
"@esbuild/android-arm64": "0.27.3",
|
| 743 |
+
"@esbuild/android-x64": "0.27.3",
|
| 744 |
+
"@esbuild/darwin-arm64": "0.27.3",
|
| 745 |
+
"@esbuild/darwin-x64": "0.27.3",
|
| 746 |
+
"@esbuild/freebsd-arm64": "0.27.3",
|
| 747 |
+
"@esbuild/freebsd-x64": "0.27.3",
|
| 748 |
+
"@esbuild/linux-arm": "0.27.3",
|
| 749 |
+
"@esbuild/linux-arm64": "0.27.3",
|
| 750 |
+
"@esbuild/linux-ia32": "0.27.3",
|
| 751 |
+
"@esbuild/linux-loong64": "0.27.3",
|
| 752 |
+
"@esbuild/linux-mips64el": "0.27.3",
|
| 753 |
+
"@esbuild/linux-ppc64": "0.27.3",
|
| 754 |
+
"@esbuild/linux-riscv64": "0.27.3",
|
| 755 |
+
"@esbuild/linux-s390x": "0.27.3",
|
| 756 |
+
"@esbuild/linux-x64": "0.27.3",
|
| 757 |
+
"@esbuild/netbsd-arm64": "0.27.3",
|
| 758 |
+
"@esbuild/netbsd-x64": "0.27.3",
|
| 759 |
+
"@esbuild/openbsd-arm64": "0.27.3",
|
| 760 |
+
"@esbuild/openbsd-x64": "0.27.3",
|
| 761 |
+
"@esbuild/openharmony-arm64": "0.27.3",
|
| 762 |
+
"@esbuild/sunos-x64": "0.27.3",
|
| 763 |
+
"@esbuild/win32-arm64": "0.27.3",
|
| 764 |
+
"@esbuild/win32-ia32": "0.27.3",
|
| 765 |
+
"@esbuild/win32-x64": "0.27.3"
|
| 766 |
+
}
|
| 767 |
+
},
|
| 768 |
+
"node_modules/fast-decode-uri-component": {
|
| 769 |
+
"version": "1.0.1",
|
| 770 |
+
"resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
|
| 771 |
+
"integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==",
|
| 772 |
+
"license": "MIT"
|
| 773 |
+
},
|
| 774 |
+
"node_modules/fast-deep-equal": {
|
| 775 |
+
"version": "3.1.3",
|
| 776 |
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
| 777 |
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
| 778 |
+
"license": "MIT"
|
| 779 |
+
},
|
| 780 |
+
"node_modules/fast-json-stringify": {
|
| 781 |
+
"version": "6.3.0",
|
| 782 |
+
"resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.3.0.tgz",
|
| 783 |
+
"integrity": "sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==",
|
| 784 |
+
"funding": [
|
| 785 |
+
{
|
| 786 |
+
"type": "github",
|
| 787 |
+
"url": "https://github.com/sponsors/fastify"
|
| 788 |
+
},
|
| 789 |
+
{
|
| 790 |
+
"type": "opencollective",
|
| 791 |
+
"url": "https://opencollective.com/fastify"
|
| 792 |
+
}
|
| 793 |
+
],
|
| 794 |
+
"license": "MIT",
|
| 795 |
+
"dependencies": {
|
| 796 |
+
"@fastify/merge-json-schemas": "^0.2.0",
|
| 797 |
+
"ajv": "^8.12.0",
|
| 798 |
+
"ajv-formats": "^3.0.1",
|
| 799 |
+
"fast-uri": "^3.0.0",
|
| 800 |
+
"json-schema-ref-resolver": "^3.0.0",
|
| 801 |
+
"rfdc": "^1.2.0"
|
| 802 |
+
}
|
| 803 |
+
},
|
| 804 |
+
"node_modules/fast-querystring": {
|
| 805 |
+
"version": "1.1.2",
|
| 806 |
+
"resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz",
|
| 807 |
+
"integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
|
| 808 |
+
"license": "MIT",
|
| 809 |
+
"dependencies": {
|
| 810 |
+
"fast-decode-uri-component": "^1.0.1"
|
| 811 |
+
}
|
| 812 |
+
},
|
| 813 |
+
"node_modules/fast-uri": {
|
| 814 |
+
"version": "3.1.0",
|
| 815 |
+
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
| 816 |
+
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
| 817 |
+
"funding": [
|
| 818 |
+
{
|
| 819 |
+
"type": "github",
|
| 820 |
+
"url": "https://github.com/sponsors/fastify"
|
| 821 |
+
},
|
| 822 |
+
{
|
| 823 |
+
"type": "opencollective",
|
| 824 |
+
"url": "https://opencollective.com/fastify"
|
| 825 |
+
}
|
| 826 |
+
],
|
| 827 |
+
"license": "BSD-3-Clause"
|
| 828 |
+
},
|
| 829 |
+
"node_modules/fastify": {
|
| 830 |
+
"version": "5.8.1",
|
| 831 |
+
"resolved": "https://registry.npmjs.org/fastify/-/fastify-5.8.1.tgz",
|
| 832 |
+
"integrity": "sha512-y0kicFvvn7CYWoPOVLOcvn4YyKQz03DIY7UxmyOy21/J8eXm09R+tmb+tVDBW5h+pja30cHI5dqUcSlvY86V2A==",
|
| 833 |
+
"funding": [
|
| 834 |
+
{
|
| 835 |
+
"type": "github",
|
| 836 |
+
"url": "https://github.com/sponsors/fastify"
|
| 837 |
+
},
|
| 838 |
+
{
|
| 839 |
+
"type": "opencollective",
|
| 840 |
+
"url": "https://opencollective.com/fastify"
|
| 841 |
+
}
|
| 842 |
+
],
|
| 843 |
+
"license": "MIT",
|
| 844 |
+
"dependencies": {
|
| 845 |
+
"@fastify/ajv-compiler": "^4.0.5",
|
| 846 |
+
"@fastify/error": "^4.0.0",
|
| 847 |
+
"@fastify/fast-json-stringify-compiler": "^5.0.0",
|
| 848 |
+
"@fastify/proxy-addr": "^5.0.0",
|
| 849 |
+
"abstract-logging": "^2.0.1",
|
| 850 |
+
"avvio": "^9.0.0",
|
| 851 |
+
"fast-json-stringify": "^6.0.0",
|
| 852 |
+
"find-my-way": "^9.0.0",
|
| 853 |
+
"light-my-request": "^6.0.0",
|
| 854 |
+
"pino": "^9.14.0 || ^10.1.0",
|
| 855 |
+
"process-warning": "^5.0.0",
|
| 856 |
+
"rfdc": "^1.3.1",
|
| 857 |
+
"secure-json-parse": "^4.0.0",
|
| 858 |
+
"semver": "^7.6.0",
|
| 859 |
+
"toad-cache": "^3.7.0"
|
| 860 |
+
}
|
| 861 |
+
},
|
| 862 |
+
"node_modules/fastify-plugin": {
|
| 863 |
+
"version": "5.1.0",
|
| 864 |
+
"resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz",
|
| 865 |
+
"integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==",
|
| 866 |
+
"funding": [
|
| 867 |
+
{
|
| 868 |
+
"type": "github",
|
| 869 |
+
"url": "https://github.com/sponsors/fastify"
|
| 870 |
+
},
|
| 871 |
+
{
|
| 872 |
+
"type": "opencollective",
|
| 873 |
+
"url": "https://opencollective.com/fastify"
|
| 874 |
+
}
|
| 875 |
+
],
|
| 876 |
+
"license": "MIT"
|
| 877 |
+
},
|
| 878 |
+
"node_modules/fastq": {
|
| 879 |
+
"version": "1.20.1",
|
| 880 |
+
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
|
| 881 |
+
"integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
|
| 882 |
+
"license": "ISC",
|
| 883 |
+
"dependencies": {
|
| 884 |
+
"reusify": "^1.0.4"
|
| 885 |
+
}
|
| 886 |
+
},
|
| 887 |
+
"node_modules/find-my-way": {
|
| 888 |
+
"version": "9.5.0",
|
| 889 |
+
"resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.5.0.tgz",
|
| 890 |
+
"integrity": "sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==",
|
| 891 |
+
"license": "MIT",
|
| 892 |
+
"dependencies": {
|
| 893 |
+
"fast-deep-equal": "^3.1.3",
|
| 894 |
+
"fast-querystring": "^1.0.0",
|
| 895 |
+
"safe-regex2": "^5.0.0"
|
| 896 |
+
},
|
| 897 |
+
"engines": {
|
| 898 |
+
"node": ">=20"
|
| 899 |
+
}
|
| 900 |
+
},
|
| 901 |
+
"node_modules/fsevents": {
|
| 902 |
+
"version": "2.3.3",
|
| 903 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 904 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 905 |
+
"dev": true,
|
| 906 |
+
"hasInstallScript": true,
|
| 907 |
+
"license": "MIT",
|
| 908 |
+
"optional": true,
|
| 909 |
+
"os": [
|
| 910 |
+
"darwin"
|
| 911 |
+
],
|
| 912 |
+
"engines": {
|
| 913 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 914 |
+
}
|
| 915 |
+
},
|
| 916 |
+
"node_modules/get-tsconfig": {
|
| 917 |
+
"version": "4.13.6",
|
| 918 |
+
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
|
| 919 |
+
"integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
|
| 920 |
+
"dev": true,
|
| 921 |
+
"license": "MIT",
|
| 922 |
+
"dependencies": {
|
| 923 |
+
"resolve-pkg-maps": "^1.0.0"
|
| 924 |
+
},
|
| 925 |
+
"funding": {
|
| 926 |
+
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
| 927 |
+
}
|
| 928 |
+
},
|
| 929 |
+
"node_modules/ipaddr.js": {
|
| 930 |
+
"version": "2.3.0",
|
| 931 |
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz",
|
| 932 |
+
"integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==",
|
| 933 |
+
"license": "MIT",
|
| 934 |
+
"engines": {
|
| 935 |
+
"node": ">= 10"
|
| 936 |
+
}
|
| 937 |
+
},
|
| 938 |
+
"node_modules/json-schema-ref-resolver": {
|
| 939 |
+
"version": "3.0.0",
|
| 940 |
+
"resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz",
|
| 941 |
+
"integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==",
|
| 942 |
+
"funding": [
|
| 943 |
+
{
|
| 944 |
+
"type": "github",
|
| 945 |
+
"url": "https://github.com/sponsors/fastify"
|
| 946 |
+
},
|
| 947 |
+
{
|
| 948 |
+
"type": "opencollective",
|
| 949 |
+
"url": "https://opencollective.com/fastify"
|
| 950 |
+
}
|
| 951 |
+
],
|
| 952 |
+
"license": "MIT",
|
| 953 |
+
"dependencies": {
|
| 954 |
+
"dequal": "^2.0.3"
|
| 955 |
+
}
|
| 956 |
+
},
|
| 957 |
+
"node_modules/json-schema-traverse": {
|
| 958 |
+
"version": "1.0.0",
|
| 959 |
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
| 960 |
+
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
| 961 |
+
"license": "MIT"
|
| 962 |
+
},
|
| 963 |
+
"node_modules/light-my-request": {
|
| 964 |
+
"version": "6.6.0",
|
| 965 |
+
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz",
|
| 966 |
+
"integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==",
|
| 967 |
+
"funding": [
|
| 968 |
+
{
|
| 969 |
+
"type": "github",
|
| 970 |
+
"url": "https://github.com/sponsors/fastify"
|
| 971 |
+
},
|
| 972 |
+
{
|
| 973 |
+
"type": "opencollective",
|
| 974 |
+
"url": "https://opencollective.com/fastify"
|
| 975 |
+
}
|
| 976 |
+
],
|
| 977 |
+
"license": "BSD-3-Clause",
|
| 978 |
+
"dependencies": {
|
| 979 |
+
"cookie": "^1.0.1",
|
| 980 |
+
"process-warning": "^4.0.0",
|
| 981 |
+
"set-cookie-parser": "^2.6.0"
|
| 982 |
+
}
|
| 983 |
+
},
|
| 984 |
+
"node_modules/light-my-request/node_modules/process-warning": {
|
| 985 |
+
"version": "4.0.1",
|
| 986 |
+
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz",
|
| 987 |
+
"integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==",
|
| 988 |
+
"funding": [
|
| 989 |
+
{
|
| 990 |
+
"type": "github",
|
| 991 |
+
"url": "https://github.com/sponsors/fastify"
|
| 992 |
+
},
|
| 993 |
+
{
|
| 994 |
+
"type": "opencollective",
|
| 995 |
+
"url": "https://opencollective.com/fastify"
|
| 996 |
+
}
|
| 997 |
+
],
|
| 998 |
+
"license": "MIT"
|
| 999 |
+
},
|
| 1000 |
+
"node_modules/on-exit-leak-free": {
|
| 1001 |
+
"version": "2.1.2",
|
| 1002 |
+
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
| 1003 |
+
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
|
| 1004 |
+
"license": "MIT",
|
| 1005 |
+
"engines": {
|
| 1006 |
+
"node": ">=14.0.0"
|
| 1007 |
+
}
|
| 1008 |
+
},
|
| 1009 |
+
"node_modules/pino": {
|
| 1010 |
+
"version": "10.3.1",
|
| 1011 |
+
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
|
| 1012 |
+
"integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
|
| 1013 |
+
"license": "MIT",
|
| 1014 |
+
"dependencies": {
|
| 1015 |
+
"@pinojs/redact": "^0.4.0",
|
| 1016 |
+
"atomic-sleep": "^1.0.0",
|
| 1017 |
+
"on-exit-leak-free": "^2.1.0",
|
| 1018 |
+
"pino-abstract-transport": "^3.0.0",
|
| 1019 |
+
"pino-std-serializers": "^7.0.0",
|
| 1020 |
+
"process-warning": "^5.0.0",
|
| 1021 |
+
"quick-format-unescaped": "^4.0.3",
|
| 1022 |
+
"real-require": "^0.2.0",
|
| 1023 |
+
"safe-stable-stringify": "^2.3.1",
|
| 1024 |
+
"sonic-boom": "^4.0.1",
|
| 1025 |
+
"thread-stream": "^4.0.0"
|
| 1026 |
+
},
|
| 1027 |
+
"bin": {
|
| 1028 |
+
"pino": "bin.js"
|
| 1029 |
+
}
|
| 1030 |
+
},
|
| 1031 |
+
"node_modules/pino-abstract-transport": {
|
| 1032 |
+
"version": "3.0.0",
|
| 1033 |
+
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz",
|
| 1034 |
+
"integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==",
|
| 1035 |
+
"license": "MIT",
|
| 1036 |
+
"dependencies": {
|
| 1037 |
+
"split2": "^4.0.0"
|
| 1038 |
+
}
|
| 1039 |
+
},
|
| 1040 |
+
"node_modules/pino-std-serializers": {
|
| 1041 |
+
"version": "7.1.0",
|
| 1042 |
+
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
|
| 1043 |
+
"integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
|
| 1044 |
+
"license": "MIT"
|
| 1045 |
+
},
|
| 1046 |
+
"node_modules/process-warning": {
|
| 1047 |
+
"version": "5.0.0",
|
| 1048 |
+
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
| 1049 |
+
"integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
|
| 1050 |
+
"funding": [
|
| 1051 |
+
{
|
| 1052 |
+
"type": "github",
|
| 1053 |
+
"url": "https://github.com/sponsors/fastify"
|
| 1054 |
+
},
|
| 1055 |
+
{
|
| 1056 |
+
"type": "opencollective",
|
| 1057 |
+
"url": "https://opencollective.com/fastify"
|
| 1058 |
+
}
|
| 1059 |
+
],
|
| 1060 |
+
"license": "MIT"
|
| 1061 |
+
},
|
| 1062 |
+
"node_modules/quick-format-unescaped": {
|
| 1063 |
+
"version": "4.0.4",
|
| 1064 |
+
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
| 1065 |
+
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
| 1066 |
+
"license": "MIT"
|
| 1067 |
+
},
|
| 1068 |
+
"node_modules/real-require": {
|
| 1069 |
+
"version": "0.2.0",
|
| 1070 |
+
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
| 1071 |
+
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
|
| 1072 |
+
"license": "MIT",
|
| 1073 |
+
"engines": {
|
| 1074 |
+
"node": ">= 12.13.0"
|
| 1075 |
+
}
|
| 1076 |
+
},
|
| 1077 |
+
"node_modules/require-from-string": {
|
| 1078 |
+
"version": "2.0.2",
|
| 1079 |
+
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
| 1080 |
+
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
| 1081 |
+
"license": "MIT",
|
| 1082 |
+
"engines": {
|
| 1083 |
+
"node": ">=0.10.0"
|
| 1084 |
+
}
|
| 1085 |
+
},
|
| 1086 |
+
"node_modules/resolve-pkg-maps": {
|
| 1087 |
+
"version": "1.0.0",
|
| 1088 |
+
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
| 1089 |
+
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
| 1090 |
+
"dev": true,
|
| 1091 |
+
"license": "MIT",
|
| 1092 |
+
"funding": {
|
| 1093 |
+
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
| 1094 |
+
}
|
| 1095 |
+
},
|
| 1096 |
+
"node_modules/ret": {
|
| 1097 |
+
"version": "0.5.0",
|
| 1098 |
+
"resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz",
|
| 1099 |
+
"integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==",
|
| 1100 |
+
"license": "MIT",
|
| 1101 |
+
"engines": {
|
| 1102 |
+
"node": ">=10"
|
| 1103 |
+
}
|
| 1104 |
+
},
|
| 1105 |
+
"node_modules/reusify": {
|
| 1106 |
+
"version": "1.1.0",
|
| 1107 |
+
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
| 1108 |
+
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
| 1109 |
+
"license": "MIT",
|
| 1110 |
+
"engines": {
|
| 1111 |
+
"iojs": ">=1.0.0",
|
| 1112 |
+
"node": ">=0.10.0"
|
| 1113 |
+
}
|
| 1114 |
+
},
|
| 1115 |
+
"node_modules/rfdc": {
|
| 1116 |
+
"version": "1.4.1",
|
| 1117 |
+
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
| 1118 |
+
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
| 1119 |
+
"license": "MIT"
|
| 1120 |
+
},
|
| 1121 |
+
"node_modules/safe-regex2": {
|
| 1122 |
+
"version": "5.0.0",
|
| 1123 |
+
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz",
|
| 1124 |
+
"integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==",
|
| 1125 |
+
"funding": [
|
| 1126 |
+
{
|
| 1127 |
+
"type": "github",
|
| 1128 |
+
"url": "https://github.com/sponsors/fastify"
|
| 1129 |
+
},
|
| 1130 |
+
{
|
| 1131 |
+
"type": "opencollective",
|
| 1132 |
+
"url": "https://opencollective.com/fastify"
|
| 1133 |
+
}
|
| 1134 |
+
],
|
| 1135 |
+
"license": "MIT",
|
| 1136 |
+
"dependencies": {
|
| 1137 |
+
"ret": "~0.5.0"
|
| 1138 |
+
}
|
| 1139 |
+
},
|
| 1140 |
+
"node_modules/safe-stable-stringify": {
|
| 1141 |
+
"version": "2.5.0",
|
| 1142 |
+
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
| 1143 |
+
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
| 1144 |
+
"license": "MIT",
|
| 1145 |
+
"engines": {
|
| 1146 |
+
"node": ">=10"
|
| 1147 |
+
}
|
| 1148 |
+
},
|
| 1149 |
+
"node_modules/secure-json-parse": {
|
| 1150 |
+
"version": "4.1.0",
|
| 1151 |
+
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
|
| 1152 |
+
"integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
|
| 1153 |
+
"funding": [
|
| 1154 |
+
{
|
| 1155 |
+
"type": "github",
|
| 1156 |
+
"url": "https://github.com/sponsors/fastify"
|
| 1157 |
+
},
|
| 1158 |
+
{
|
| 1159 |
+
"type": "opencollective",
|
| 1160 |
+
"url": "https://opencollective.com/fastify"
|
| 1161 |
+
}
|
| 1162 |
+
],
|
| 1163 |
+
"license": "BSD-3-Clause"
|
| 1164 |
+
},
|
| 1165 |
+
"node_modules/semver": {
|
| 1166 |
+
"version": "7.7.4",
|
| 1167 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
| 1168 |
+
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
| 1169 |
+
"license": "ISC",
|
| 1170 |
+
"bin": {
|
| 1171 |
+
"semver": "bin/semver.js"
|
| 1172 |
+
},
|
| 1173 |
+
"engines": {
|
| 1174 |
+
"node": ">=10"
|
| 1175 |
+
}
|
| 1176 |
+
},
|
| 1177 |
+
"node_modules/set-cookie-parser": {
|
| 1178 |
+
"version": "2.7.2",
|
| 1179 |
+
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
|
| 1180 |
+
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
| 1181 |
+
"license": "MIT"
|
| 1182 |
+
},
|
| 1183 |
+
"node_modules/sonic-boom": {
|
| 1184 |
+
"version": "4.2.1",
|
| 1185 |
+
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
|
| 1186 |
+
"integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
|
| 1187 |
+
"license": "MIT",
|
| 1188 |
+
"dependencies": {
|
| 1189 |
+
"atomic-sleep": "^1.0.0"
|
| 1190 |
+
}
|
| 1191 |
+
},
|
| 1192 |
+
"node_modules/split2": {
|
| 1193 |
+
"version": "4.2.0",
|
| 1194 |
+
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
| 1195 |
+
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
| 1196 |
+
"license": "ISC",
|
| 1197 |
+
"engines": {
|
| 1198 |
+
"node": ">= 10.x"
|
| 1199 |
+
}
|
| 1200 |
+
},
|
| 1201 |
+
"node_modules/thread-stream": {
|
| 1202 |
+
"version": "4.0.0",
|
| 1203 |
+
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz",
|
| 1204 |
+
"integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==",
|
| 1205 |
+
"license": "MIT",
|
| 1206 |
+
"dependencies": {
|
| 1207 |
+
"real-require": "^0.2.0"
|
| 1208 |
+
},
|
| 1209 |
+
"engines": {
|
| 1210 |
+
"node": ">=20"
|
| 1211 |
+
}
|
| 1212 |
+
},
|
| 1213 |
+
"node_modules/toad-cache": {
|
| 1214 |
+
"version": "3.7.0",
|
| 1215 |
+
"resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
|
| 1216 |
+
"integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==",
|
| 1217 |
+
"license": "MIT",
|
| 1218 |
+
"engines": {
|
| 1219 |
+
"node": ">=12"
|
| 1220 |
+
}
|
| 1221 |
+
},
|
| 1222 |
+
"node_modules/tsx": {
|
| 1223 |
+
"version": "4.21.0",
|
| 1224 |
+
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
|
| 1225 |
+
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
| 1226 |
+
"dev": true,
|
| 1227 |
+
"license": "MIT",
|
| 1228 |
+
"dependencies": {
|
| 1229 |
+
"esbuild": "~0.27.0",
|
| 1230 |
+
"get-tsconfig": "^4.7.5"
|
| 1231 |
+
},
|
| 1232 |
+
"bin": {
|
| 1233 |
+
"tsx": "dist/cli.mjs"
|
| 1234 |
+
},
|
| 1235 |
+
"engines": {
|
| 1236 |
+
"node": ">=18.0.0"
|
| 1237 |
+
},
|
| 1238 |
+
"optionalDependencies": {
|
| 1239 |
+
"fsevents": "~2.3.3"
|
| 1240 |
+
}
|
| 1241 |
+
},
|
| 1242 |
+
"node_modules/typescript": {
|
| 1243 |
+
"version": "5.9.3",
|
| 1244 |
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
| 1245 |
+
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
| 1246 |
+
"dev": true,
|
| 1247 |
+
"license": "Apache-2.0",
|
| 1248 |
+
"bin": {
|
| 1249 |
+
"tsc": "bin/tsc",
|
| 1250 |
+
"tsserver": "bin/tsserver"
|
| 1251 |
+
},
|
| 1252 |
+
"engines": {
|
| 1253 |
+
"node": ">=14.17"
|
| 1254 |
+
}
|
| 1255 |
+
},
|
| 1256 |
+
"node_modules/undici": {
|
| 1257 |
+
"version": "7.22.0",
|
| 1258 |
+
"resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",
|
| 1259 |
+
"integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==",
|
| 1260 |
+
"license": "MIT",
|
| 1261 |
+
"engines": {
|
| 1262 |
+
"node": ">=20.18.1"
|
| 1263 |
+
}
|
| 1264 |
+
},
|
| 1265 |
+
"node_modules/undici-types": {
|
| 1266 |
+
"version": "6.21.0",
|
| 1267 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
| 1268 |
+
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
| 1269 |
+
"dev": true,
|
| 1270 |
+
"license": "MIT"
|
| 1271 |
+
}
|
| 1272 |
+
}
|
| 1273 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "ai-proxy",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "LLM Gateway Proxy for Claude Code – transparent Anthropic API relay with auth-swap",
|
| 5 |
+
"type": "module",
|
| 6 |
+
"main": "dist/index.js",
|
| 7 |
+
"scripts": {
|
| 8 |
+
"dev": "tsx --watch src/index.ts",
|
| 9 |
+
"build": "tsc",
|
| 10 |
+
"start": "node dist/index.js"
|
| 11 |
+
},
|
| 12 |
+
"engines": {
|
| 13 |
+
"node": ">=20"
|
| 14 |
+
},
|
| 15 |
+
"dependencies": {
|
| 16 |
+
"fastify": "^5.3.3",
|
| 17 |
+
"@fastify/rate-limit": "^10.2.2",
|
| 18 |
+
"dotenv": "^16.5.0",
|
| 19 |
+
"undici": "^7.5.0"
|
| 20 |
+
},
|
| 21 |
+
"devDependencies": {
|
| 22 |
+
"typescript": "^5.8.2",
|
| 23 |
+
"@types/node": "^22.13.10",
|
| 24 |
+
"tsx": "^4.19.3"
|
| 25 |
+
}
|
| 26 |
+
}
|
src/auth.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { FastifyRequest, FastifyReply } from 'fastify';
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Creates a Fastify onRequest hook that validates the Authorization header
|
| 5 |
+
* against the configured proxy auth token.
|
| 6 |
+
*
|
| 7 |
+
* Rejects with 401 if the header is missing or the token doesn't match.
|
| 8 |
+
*/
|
| 9 |
+
export function createAuthHook(proxyAuthToken: string): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
|
| 10 |
+
return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
|
| 11 |
+
const authHeader = request.headers['authorization'];
|
| 12 |
+
|
| 13 |
+
if (!authHeader) {
|
| 14 |
+
reply.code(401).send({ error: 'Missing Authorization header' });
|
| 15 |
+
return;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
// Support "Bearer <token>" format
|
| 19 |
+
const token = authHeader.startsWith('Bearer ')
|
| 20 |
+
? authHeader.slice(7)
|
| 21 |
+
: authHeader;
|
| 22 |
+
|
| 23 |
+
if (token !== proxyAuthToken) {
|
| 24 |
+
reply.code(401).send({ error: 'Invalid authorization token' });
|
| 25 |
+
return;
|
| 26 |
+
}
|
| 27 |
+
};
|
| 28 |
+
}
|
src/config.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import 'dotenv/config';
|
| 2 |
+
import type { ProxyConfig } from './types.js';
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Reads a required environment variable or aborts the process.
|
| 6 |
+
*/
|
| 7 |
+
function requireEnv(name: string): string {
|
| 8 |
+
const value = process.env[name]?.trim();
|
| 9 |
+
if (!value) {
|
| 10 |
+
console.error(`[FATAL] Missing required environment variable: ${name}`);
|
| 11 |
+
process.exit(1);
|
| 12 |
+
}
|
| 13 |
+
return value;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Reads an optional environment variable with a fallback default.
|
| 18 |
+
*/
|
| 19 |
+
function optionalEnv(name: string, fallback: string): string {
|
| 20 |
+
return process.env[name]?.trim() || fallback;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* Parses and validates all environment variables into a typed, frozen config object.
|
| 25 |
+
* Aborts the process immediately if required variables are missing.
|
| 26 |
+
*/
|
| 27 |
+
export function loadConfig(): ProxyConfig {
|
| 28 |
+
const config: ProxyConfig = {
|
| 29 |
+
anthropicApiKey: requireEnv('ANTHROPIC_API_KEY'),
|
| 30 |
+
proxyAuthToken: requireEnv('PROXY_AUTH_TOKEN'),
|
| 31 |
+
port: parseInt(optionalEnv('PORT', '7860'), 10),
|
| 32 |
+
host: optionalEnv('HOST', '0.0.0.0'),
|
| 33 |
+
logLevel: optionalEnv('LOG_LEVEL', 'info'),
|
| 34 |
+
rateLimitMax: parseInt(optionalEnv('RATE_LIMIT_MAX', '100'), 10),
|
| 35 |
+
rateLimitWindowMs: parseInt(optionalEnv('RATE_LIMIT_WINDOW_MS', '60000'), 10),
|
| 36 |
+
bodyLimit: parseInt(optionalEnv('BODY_LIMIT', '5242880'), 10),
|
| 37 |
+
corsOrigin: optionalEnv('CORS_ORIGIN', ''),
|
| 38 |
+
anthropicBaseUrl: optionalEnv('ANTHROPIC_BASE_URL', 'https://api.anthropic.com'),
|
| 39 |
+
upstreamTimeoutMs: parseInt(optionalEnv('UPSTREAM_TIMEOUT_MS', '300000'), 10),
|
| 40 |
+
};
|
| 41 |
+
|
| 42 |
+
// Validate numeric values
|
| 43 |
+
if (isNaN(config.port) || config.port < 1 || config.port > 65535) {
|
| 44 |
+
console.error(`[FATAL] Invalid PORT value: ${process.env['PORT']}`);
|
| 45 |
+
process.exit(1);
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
return Object.freeze(config);
|
| 49 |
+
}
|
src/index.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Fastify, { type FastifyError } from 'fastify';
|
| 2 |
+
import rateLimit from '@fastify/rate-limit';
|
| 3 |
+
import { loadConfig } from './config.js';
|
| 4 |
+
import { createAuthHook } from './auth.js';
|
| 5 |
+
import { forwardRequest } from './proxy.js';
|
| 6 |
+
import { PROXY_ROUTES } from './types.js';
|
| 7 |
+
|
| 8 |
+
const config = loadConfig();
|
| 9 |
+
|
| 10 |
+
const app = Fastify({
|
| 11 |
+
logger: {
|
| 12 |
+
level: config.logLevel,
|
| 13 |
+
// Redact sensitive fields from logs
|
| 14 |
+
redact: ['req.headers.authorization', 'req.headers["x-api-key"]'],
|
| 15 |
+
},
|
| 16 |
+
bodyLimit: config.bodyLimit,
|
| 17 |
+
trustProxy: true,
|
| 18 |
+
});
|
| 19 |
+
|
| 20 |
+
/** Register rate limiting. */
|
| 21 |
+
await app.register(rateLimit, {
|
| 22 |
+
max: config.rateLimitMax,
|
| 23 |
+
timeWindow: config.rateLimitWindowMs,
|
| 24 |
+
addHeadersOnExceeding: { 'x-ratelimit-limit': true, 'x-ratelimit-remaining': true, 'x-ratelimit-reset': true },
|
| 25 |
+
addHeaders: { 'x-ratelimit-limit': true, 'x-ratelimit-remaining': true, 'x-ratelimit-reset': true, 'retry-after': true },
|
| 26 |
+
});
|
| 27 |
+
|
| 28 |
+
/** Optional CORS support – @fastify/cors must be installed separately. */
|
| 29 |
+
if (config.corsOrigin) {
|
| 30 |
+
try {
|
| 31 |
+
// Dynamic import: @fastify/cors is an optional dependency
|
| 32 |
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
| 33 |
+
const corsPlugin = await import(/* webpackIgnore: true */ '@fastify/cors' + '');
|
| 34 |
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
| 35 |
+
await app.register(corsPlugin.default ?? corsPlugin, { origin: config.corsOrigin });
|
| 36 |
+
} catch {
|
| 37 |
+
app.log.warn('CORS_ORIGIN is set but @fastify/cors is not installed. Run: npm install @fastify/cors');
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
/** Add defensive response headers to every response. */
|
| 42 |
+
app.addHook('onSend', async (_request, reply) => {
|
| 43 |
+
reply.header('X-Content-Type-Options', 'nosniff');
|
| 44 |
+
reply.header('X-Frame-Options', 'DENY');
|
| 45 |
+
});
|
| 46 |
+
|
| 47 |
+
/** Auth hook instance. */
|
| 48 |
+
const authHook = createAuthHook(config.proxyAuthToken);
|
| 49 |
+
|
| 50 |
+
/**
|
| 51 |
+
* Content-Type validation hook for API routes.
|
| 52 |
+
* Rejects requests that don't send application/json.
|
| 53 |
+
*/
|
| 54 |
+
async function validateContentType(
|
| 55 |
+
request: Parameters<typeof authHook>[0],
|
| 56 |
+
reply: Parameters<typeof authHook>[1],
|
| 57 |
+
): Promise<void> {
|
| 58 |
+
const ct = request.headers['content-type'];
|
| 59 |
+
if (!ct || !ct.includes('application/json')) {
|
| 60 |
+
reply.code(415).send({ error: 'Unsupported Media Type. Expected application/json.' });
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
// ── Routes ──────────────────────────────────────────────────────────────────
|
| 65 |
+
|
| 66 |
+
/** Health check – no auth required. */
|
| 67 |
+
app.get('/health', async (_request, reply) => {
|
| 68 |
+
reply.send({ status: 'ok' });
|
| 69 |
+
});
|
| 70 |
+
|
| 71 |
+
/** Register proxy routes. */
|
| 72 |
+
for (const route of PROXY_ROUTES) {
|
| 73 |
+
app.post(route, {
|
| 74 |
+
onRequest: [authHook, validateContentType],
|
| 75 |
+
}, async (request, reply) => {
|
| 76 |
+
await forwardRequest(request, reply, route, config, app.log);
|
| 77 |
+
});
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/** Catch-all 404 for unregistered routes. */
|
| 81 |
+
app.setNotFoundHandler((_request, reply) => {
|
| 82 |
+
reply.code(404).send({ error: 'Not found' });
|
| 83 |
+
});
|
| 84 |
+
|
| 85 |
+
/** Method not allowed – not needed since Fastify handles it,
|
| 86 |
+
* but we customize the response format. */
|
| 87 |
+
app.setErrorHandler((error: FastifyError, _request, reply) => {
|
| 88 |
+
const statusCode = error.statusCode ?? 500;
|
| 89 |
+
|
| 90 |
+
if (statusCode === 405) {
|
| 91 |
+
reply.code(405).send({ error: 'Method not allowed' });
|
| 92 |
+
return;
|
| 93 |
+
}
|
| 94 |
+
if (statusCode === 429) {
|
| 95 |
+
// Rate limit exceeded – forward Fastify's rate-limit response
|
| 96 |
+
reply.code(429).send({ error: 'Too many requests. Please retry later.' });
|
| 97 |
+
return;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
app.log.error({ err: error }, 'Unhandled error');
|
| 101 |
+
reply.code(statusCode).send({ error: 'Internal server error' });
|
| 102 |
+
});
|
| 103 |
+
|
| 104 |
+
// ── Server Start ────────────────────────────────────────────────────────────
|
| 105 |
+
|
| 106 |
+
const start = async (): Promise<void> => {
|
| 107 |
+
try {
|
| 108 |
+
await app.listen({ port: config.port, host: config.host });
|
| 109 |
+
app.log.info(`Proxy listening on ${config.host}:${config.port}`);
|
| 110 |
+
} catch (err) {
|
| 111 |
+
app.log.fatal({ err }, 'Failed to start server');
|
| 112 |
+
process.exit(1);
|
| 113 |
+
}
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
+
// ── Graceful Shutdown ───────────────────────────────────────────────────────
|
| 117 |
+
|
| 118 |
+
const shutdown = async (signal: string): Promise<void> => {
|
| 119 |
+
app.log.info(`Received ${signal}, shutting down gracefully…`);
|
| 120 |
+
try {
|
| 121 |
+
await app.close();
|
| 122 |
+
app.log.info('Server closed.');
|
| 123 |
+
process.exit(0);
|
| 124 |
+
} catch (err) {
|
| 125 |
+
app.log.error({ err }, 'Error during shutdown');
|
| 126 |
+
process.exit(1);
|
| 127 |
+
}
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
process.on('SIGTERM', () => void shutdown('SIGTERM'));
|
| 131 |
+
process.on('SIGINT', () => void shutdown('SIGINT'));
|
| 132 |
+
|
| 133 |
+
// Catch unhandled rejections to keep the server stable
|
| 134 |
+
process.on('unhandledRejection', (reason) => {
|
| 135 |
+
app.log.error({ reason }, 'Unhandled promise rejection');
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
await start();
|
src/proxy.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { request as undiciRequest } from 'undici';
|
| 2 |
+
import type { FastifyRequest, FastifyReply, FastifyBaseLogger } from 'fastify';
|
| 3 |
+
import type { ProxyConfig } from './types.js';
|
| 4 |
+
import { FORWARDED_HEADERS, HOP_BY_HOP_HEADERS } from './types.js';
|
| 5 |
+
|
| 6 |
+
/**
|
| 7 |
+
* Builds the sanitized header set for the upstream request.
|
| 8 |
+
*
|
| 9 |
+
* - Forwards only explicitly allowed headers from the client
|
| 10 |
+
* - Strips authorization and x-api-key (client must not set the real key)
|
| 11 |
+
* - Strips hop-by-hop headers
|
| 12 |
+
* - Injects the real Anthropic API key as x-api-key
|
| 13 |
+
*/
|
| 14 |
+
function buildUpstreamHeaders(
|
| 15 |
+
incomingHeaders: Record<string, string | string[] | undefined>,
|
| 16 |
+
anthropicApiKey: string,
|
| 17 |
+
): Record<string, string> {
|
| 18 |
+
const headers: Record<string, string> = {};
|
| 19 |
+
|
| 20 |
+
for (const name of FORWARDED_HEADERS) {
|
| 21 |
+
const value = incomingHeaders[name];
|
| 22 |
+
if (value !== undefined) {
|
| 23 |
+
headers[name] = Array.isArray(value) ? value.join(', ') : value;
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
// Inject the real API key
|
| 28 |
+
headers['x-api-key'] = anthropicApiKey;
|
| 29 |
+
|
| 30 |
+
return headers;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* Checks whether a response header should be forwarded back to the client.
|
| 35 |
+
* Strips hop-by-hop and internal headers.
|
| 36 |
+
*/
|
| 37 |
+
function shouldForwardResponseHeader(name: string): boolean {
|
| 38 |
+
const lower = name.toLowerCase();
|
| 39 |
+
if (HOP_BY_HOP_HEADERS.includes(lower)) return false;
|
| 40 |
+
if (lower === 'x-api-key') return false;
|
| 41 |
+
return true;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/**
|
| 45 |
+
* Forwards an incoming request to the upstream Anthropic API and streams
|
| 46 |
+
* or relays the response back to the client.
|
| 47 |
+
*
|
| 48 |
+
* - Streaming: if upstream responds with `text/event-stream`, the body is
|
| 49 |
+
* piped directly to the client without buffering.
|
| 50 |
+
* - Non-streaming: the full response body is read and sent back.
|
| 51 |
+
* - Errors from upstream are forwarded with their original status code and body.
|
| 52 |
+
*/
|
| 53 |
+
export async function forwardRequest(
|
| 54 |
+
request: FastifyRequest,
|
| 55 |
+
reply: FastifyReply,
|
| 56 |
+
targetPath: string,
|
| 57 |
+
config: ProxyConfig,
|
| 58 |
+
logger: FastifyBaseLogger,
|
| 59 |
+
): Promise<void> {
|
| 60 |
+
const upstreamUrl = `${config.anthropicBaseUrl}${targetPath}`;
|
| 61 |
+
|
| 62 |
+
const upstreamHeaders = buildUpstreamHeaders(
|
| 63 |
+
request.headers as Record<string, string | string[] | undefined>,
|
| 64 |
+
config.anthropicApiKey,
|
| 65 |
+
);
|
| 66 |
+
|
| 67 |
+
logger.debug({ upstreamUrl, headers: Object.keys(upstreamHeaders) }, 'Forwarding request upstream');
|
| 68 |
+
|
| 69 |
+
let upstreamResponse: Awaited<ReturnType<typeof undiciRequest>>;
|
| 70 |
+
try {
|
| 71 |
+
upstreamResponse = await undiciRequest(upstreamUrl, {
|
| 72 |
+
method: 'POST',
|
| 73 |
+
headers: upstreamHeaders,
|
| 74 |
+
body: JSON.stringify(request.body),
|
| 75 |
+
headersTimeout: config.upstreamTimeoutMs,
|
| 76 |
+
bodyTimeout: config.upstreamTimeoutMs,
|
| 77 |
+
});
|
| 78 |
+
} catch (err: unknown) {
|
| 79 |
+
const message = err instanceof Error ? err.message : 'Unknown upstream error';
|
| 80 |
+
logger.error({ err }, 'Upstream request failed');
|
| 81 |
+
|
| 82 |
+
// Distinguish timeout from other errors
|
| 83 |
+
if (message.includes('timeout') || message.includes('Timeout')) {
|
| 84 |
+
reply.code(504).send({ error: 'Upstream request timed out' });
|
| 85 |
+
} else {
|
| 86 |
+
reply.code(502).send({ error: 'Failed to connect to upstream API' });
|
| 87 |
+
}
|
| 88 |
+
return;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
const { statusCode, headers: responseHeaders, body: responseBody } = upstreamResponse;
|
| 92 |
+
|
| 93 |
+
// Forward response headers (filtered)
|
| 94 |
+
const forwardedHeaders: Record<string, string | string[]> = {};
|
| 95 |
+
for (const [name, value] of Object.entries(responseHeaders)) {
|
| 96 |
+
if (value !== undefined && shouldForwardResponseHeader(name)) {
|
| 97 |
+
forwardedHeaders[name] = value;
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
const contentType = responseHeaders['content-type'];
|
| 102 |
+
const isStreaming =
|
| 103 |
+
typeof contentType === 'string' && contentType.includes('text/event-stream');
|
| 104 |
+
|
| 105 |
+
if (isStreaming) {
|
| 106 |
+
// Streaming: pipe upstream SSE body directly to the client without buffering
|
| 107 |
+
logger.debug('Streaming SSE response to client');
|
| 108 |
+
|
| 109 |
+
reply.raw.writeHead(statusCode, forwardedHeaders as Record<string, string>);
|
| 110 |
+
|
| 111 |
+
for await (const chunk of responseBody) {
|
| 112 |
+
if (!reply.raw.write(chunk)) {
|
| 113 |
+
// Back-pressure: wait for drain
|
| 114 |
+
await new Promise<void>((resolve) => reply.raw.once('drain', resolve));
|
| 115 |
+
}
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
reply.raw.end();
|
| 119 |
+
// Mark the reply as sent so Fastify doesn't try to send again
|
| 120 |
+
reply.hijack();
|
| 121 |
+
} else {
|
| 122 |
+
// Non-streaming: read full body and forward
|
| 123 |
+
const bodyChunks: Buffer[] = [];
|
| 124 |
+
for await (const chunk of responseBody) {
|
| 125 |
+
bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
| 126 |
+
}
|
| 127 |
+
const fullBody = Buffer.concat(bodyChunks);
|
| 128 |
+
|
| 129 |
+
logger.debug({ statusCode, bodyLength: fullBody.length }, 'Forwarding non-streaming response');
|
| 130 |
+
|
| 131 |
+
reply.code(statusCode).headers(forwardedHeaders).send(fullBody);
|
| 132 |
+
}
|
| 133 |
+
}
|
src/types.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/** Shared TypeScript types for the proxy server. */
|
| 2 |
+
|
| 3 |
+
/** Typed server configuration derived from environment variables. */
|
| 4 |
+
export interface ProxyConfig {
|
| 5 |
+
/** Anthropic API key used for upstream requests. */
|
| 6 |
+
readonly anthropicApiKey: string;
|
| 7 |
+
/** Shared secret token for client authentication. */
|
| 8 |
+
readonly proxyAuthToken: string;
|
| 9 |
+
/** Server listen port. */
|
| 10 |
+
readonly port: number;
|
| 11 |
+
/** Server listen host. */
|
| 12 |
+
readonly host: string;
|
| 13 |
+
/** Pino log level. */
|
| 14 |
+
readonly logLevel: string;
|
| 15 |
+
/** Max requests per rate-limit window per IP. */
|
| 16 |
+
readonly rateLimitMax: number;
|
| 17 |
+
/** Rate-limit window duration in ms. */
|
| 18 |
+
readonly rateLimitWindowMs: number;
|
| 19 |
+
/** Max request body size in bytes. */
|
| 20 |
+
readonly bodyLimit: number;
|
| 21 |
+
/** CORS origin (empty string = disabled). */
|
| 22 |
+
readonly corsOrigin: string;
|
| 23 |
+
/** Upstream Anthropic base URL. */
|
| 24 |
+
readonly anthropicBaseUrl: string;
|
| 25 |
+
/** Upstream request timeout in ms. */
|
| 26 |
+
readonly upstreamTimeoutMs: number;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
/** Headers that must be forwarded to the upstream API. */
|
| 30 |
+
export const FORWARDED_HEADERS: ReadonlyArray<string> = [
|
| 31 |
+
'anthropic-beta',
|
| 32 |
+
'anthropic-version',
|
| 33 |
+
'content-type',
|
| 34 |
+
'accept',
|
| 35 |
+
'x-request-id',
|
| 36 |
+
] as const;
|
| 37 |
+
|
| 38 |
+
/** Hop-by-hop headers that must be stripped before forwarding. */
|
| 39 |
+
export const HOP_BY_HOP_HEADERS: ReadonlyArray<string> = [
|
| 40 |
+
'connection', // Controls if the network connection stays open after the current transaction.
|
| 41 |
+
'keep-alive', // Settings for persistent connections.
|
| 42 |
+
'transfer-encoding', // Specifies the form of encoding used to safely transfer the payload.
|
| 43 |
+
'upgrade', // Mechanism for switching to a different protocol (e.g., WebSocket).
|
| 44 |
+
'proxy-authorization', // Credentials for authenticating the client with a proxy.
|
| 45 |
+
'te', // Specifies the transfer encodings the user agent is willing to accept.
|
| 46 |
+
'trailer', // Indicates that specific headers will be present in the trailer of a chunked message.
|
| 47 |
+
] as const;
|
| 48 |
+
|
| 49 |
+
/** Allowed proxy route paths. */
|
| 50 |
+
export const PROXY_ROUTES = [
|
| 51 |
+
'/v1/messages',
|
| 52 |
+
'/v1/messages/count_tokens',
|
| 53 |
+
] as const;
|
| 54 |
+
|
| 55 |
+
export type ProxyRoute = (typeof PROXY_ROUTES)[number];
|
tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2022",
|
| 4 |
+
"module": "NodeNext",
|
| 5 |
+
"moduleResolution": "NodeNext",
|
| 6 |
+
"outDir": "dist",
|
| 7 |
+
"rootDir": "src",
|
| 8 |
+
"strict": true,
|
| 9 |
+
"esModuleInterop": true,
|
| 10 |
+
"skipLibCheck": true,
|
| 11 |
+
"forceConsistentCasingInFileNames": true,
|
| 12 |
+
"resolveJsonModule": true,
|
| 13 |
+
"declaration": true,
|
| 14 |
+
"declarationMap": true,
|
| 15 |
+
"sourceMap": true
|
| 16 |
+
},
|
| 17 |
+
"include": [
|
| 18 |
+
"src/**/*"
|
| 19 |
+
],
|
| 20 |
+
"exclude": [
|
| 21 |
+
"node_modules",
|
| 22 |
+
"dist"
|
| 23 |
+
]
|
| 24 |
+
}
|