From d2d6b0dcca7f029d596e2402126127a0c65ef0f5 Mon Sep 17 00:00:00 2001 From: okxlin Date: Fri, 6 Mar 2026 11:36:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0cli-proxy-api=E5=88=B0?= =?UTF-8?q?=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cli-proxy-api/6.8.41/.env.sample | 8 + apps/cli-proxy-api/6.8.41/data.yml | 108 ++++++ apps/cli-proxy-api/6.8.41/data/config.yaml | 330 +++++++++++++++++++ apps/cli-proxy-api/6.8.41/docker-compose.yml | 26 ++ apps/cli-proxy-api/README.md | 25 ++ apps/cli-proxy-api/data.yml | 32 ++ apps/cli-proxy-api/latest/.env.sample | 8 + apps/cli-proxy-api/latest/data.yml | 108 ++++++ apps/cli-proxy-api/latest/data/config.yaml | 330 +++++++++++++++++++ apps/cli-proxy-api/latest/docker-compose.yml | 26 ++ apps/cli-proxy-api/logo.png | Bin 0 -> 8721 bytes 11 files changed, 1001 insertions(+) create mode 100644 apps/cli-proxy-api/6.8.41/.env.sample create mode 100644 apps/cli-proxy-api/6.8.41/data.yml create mode 100644 apps/cli-proxy-api/6.8.41/data/config.yaml create mode 100644 apps/cli-proxy-api/6.8.41/docker-compose.yml create mode 100644 apps/cli-proxy-api/README.md create mode 100644 apps/cli-proxy-api/data.yml create mode 100644 apps/cli-proxy-api/latest/.env.sample create mode 100644 apps/cli-proxy-api/latest/data.yml create mode 100644 apps/cli-proxy-api/latest/data/config.yaml create mode 100644 apps/cli-proxy-api/latest/docker-compose.yml create mode 100644 apps/cli-proxy-api/logo.png diff --git a/apps/cli-proxy-api/6.8.41/.env.sample b/apps/cli-proxy-api/6.8.41/.env.sample new file mode 100644 index 00000000..94e7d3dd --- /dev/null +++ b/apps/cli-proxy-api/6.8.41/.env.sample @@ -0,0 +1,8 @@ +CLI_PROXY_PORT_11451=11451 +CLI_PROXY_PORT_1455=1455 +CLI_PROXY_PORT_51121=51121 +CLI_PROXY_PORT_54545=54545 +CLI_PROXY_PORT_8085=8085 +CONTAINER_NAME="cliproxyapi" +DEPLOY="" +PANEL_APP_PORT_HTTP=8317 diff --git a/apps/cli-proxy-api/6.8.41/data.yml b/apps/cli-proxy-api/6.8.41/data.yml new file mode 100644 index 00000000..ddf03670 --- /dev/null +++ b/apps/cli-proxy-api/6.8.41/data.yml @@ -0,0 +1,108 @@ +additionalProperties: + formFields: + - default: 8317 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port (8317) + labelZh: Web 访问端口 (8317) + label: + en: Web Port (8317) + ja: Webポート (8317) + ms: Web Port (8317) + pt-br: Porta Web (8317) + ru: Веб-порт (8317) + ko: 웹 포트 (8317) + zh-Hant: Web 訪問埠 (8317) + zh: Web 访问端口 (8317) + required: true + rule: paramPort + type: number + - default: 8085 + edit: true + envKey: CLI_PROXY_PORT_8085 + label: + en: Port 8085 + ja: ポート 8085 + ms: Port 8085 + pt-br: Porta 8085 + ru: Порт 8085 + ko: 포트 8085 + zh-Hant: 埠 8085 + zh: 端口 8085 + required: true + rule: paramPort + type: number + - default: 1455 + edit: true + envKey: CLI_PROXY_PORT_1455 + label: + en: Port 1455 + ja: ポート 1455 + ms: Port 1455 + pt-br: Porta 1455 + ru: Порт 1455 + ko: 포트 1455 + zh-Hant: 埠 1455 + zh: 端口 1455 + required: true + rule: paramPort + type: number + - default: 54545 + edit: true + envKey: CLI_PROXY_PORT_54545 + label: + en: Port 54545 + ja: ポート 54545 + ms: Port 54545 + pt-br: Porta 54545 + ru: Порт 54545 + ko: 포트 54545 + zh-Hant: 埠 54545 + zh: 端口 54545 + required: true + rule: paramPort + type: number + - default: 51121 + edit: true + envKey: CLI_PROXY_PORT_51121 + label: + en: Port 51121 + ja: ポート 51121 + ms: Port 51121 + pt-br: Porta 51121 + ru: Порт 51121 + ko: 포트 51121 + zh-Hant: 埠 51121 + zh: 端口 51121 + required: true + rule: paramPort + type: number + - default: 11451 + edit: true + envKey: CLI_PROXY_PORT_11451 + label: + en: Port 11451 + ja: ポート 11451 + ms: Port 11451 + pt-br: Porta 11451 + ru: Порт 11451 + ko: 포트 11451 + zh-Hant: 埠 11451 + zh: 端口 11451 + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: DEPLOY + label: + en: Deploy Env Variable + ja: デプロイ環境変数 + ms: Variabel Persekitaran Deploy + pt-br: Variável de Ambiente de Deploy + ru: Переменная среды развертывания + ko: 배포 환경 변수 + zh-Hant: 部署環境變數 + zh: 部署环境变量 + required: false + type: text diff --git a/apps/cli-proxy-api/6.8.41/data/config.yaml b/apps/cli-proxy-api/6.8.41/data/config.yaml new file mode 100644 index 00000000..fbabac8f --- /dev/null +++ b/apps/cli-proxy-api/6.8.41/data/config.yaml @@ -0,0 +1,330 @@ +# Server host/interface to bind to. Default is empty ("") to bind all interfaces (IPv4 + IPv6). +# Use "127.0.0.1" or "localhost" to restrict access to local machine only. +host: "" + +# Server port +port: 8317 + +# TLS settings for HTTPS. When enabled, the server listens with the provided certificate and key. +tls: + enable: false + cert: "" + key: "" + +# Management API settings +remote-management: + # Whether to allow remote (non-localhost) management access. + # When false, only localhost can access management endpoints (a key is still required). + allow-remote: false + + # Management key. If a plaintext value is provided here, it will be hashed on startup. + # All management requests (even from localhost) require this key. + # Leave empty to disable the Management API entirely (404 for all /v0/management routes). + secret-key: "" + + # Disable the bundled management control panel asset download and HTTP route when true. + disable-control-panel: false + + # GitHub repository for the management control panel. Accepts a repository URL or releases API URL. + panel-github-repository: "https://github.com/router-for-me/Cli-Proxy-API-Management-Center" + +# Authentication directory (supports ~ for home directory) +auth-dir: "~/.cli-proxy-api" + +# API keys for authentication +api-keys: + - "your-api-key-1" + - "your-api-key-2" + - "your-api-key-3" + +# Enable debug logging +debug: false + +# Enable pprof HTTP debug server (host:port). Keep it bound to localhost for safety. +pprof: + enable: false + addr: "127.0.0.1:8316" + +# When true, disable high-overhead HTTP middleware features to reduce per-request memory usage under high concurrency. +commercial-mode: false + +# When true, write application logs to rotating files instead of stdout +logging-to-file: false + +# Maximum total size (MB) of log files under the logs directory. When exceeded, the oldest log +# files are deleted until within the limit. Set to 0 to disable. +logs-max-total-size-mb: 0 + +# Maximum number of error log files retained when request logging is disabled. +# When exceeded, the oldest error log files are deleted. Default is 10. Set to 0 to disable cleanup. +error-logs-max-files: 10 + +# When false, disable in-memory usage statistics aggregation +usage-statistics-enabled: false + +# Proxy URL. Supports socks5/http/https protocols. Example: socks5://user:pass@192.168.1.1:1080/ +proxy-url: "" + +# When true, unprefixed model requests only use credentials without a prefix (except when prefix == model name). +force-model-prefix: false + +# When true, forward filtered upstream response headers to downstream clients. +# Default is false (disabled). +passthrough-headers: false + +# Number of times to retry a request. Retries will occur if the HTTP response code is 403, 408, 500, 502, 503, or 504. +request-retry: 3 + +# Maximum number of different credentials to try for one failed request. +# Set to 0 to keep legacy behavior (try all available credentials). +max-retry-credentials: 0 + +# Maximum wait time in seconds for a cooled-down credential before triggering a retry. +max-retry-interval: 30 + +# Quota exceeded behavior +quota-exceeded: + switch-project: true # Whether to automatically switch to another project when a quota is exceeded + switch-preview-model: true # Whether to automatically switch to a preview model when a quota is exceeded + +# Routing strategy for selecting credentials when multiple match. +routing: + strategy: "round-robin" # round-robin (default), fill-first + +# When true, enable authentication for the WebSocket API (/v1/ws). +ws-auth: false + +# When > 0, emit blank lines every N seconds for non-streaming responses to prevent idle timeouts. +nonstream-keepalive-interval: 0 + +# Streaming behavior (SSE keep-alives + safe bootstrap retries). +# streaming: +# keepalive-seconds: 15 # Default: 0 (disabled). <= 0 disables keep-alives. +# bootstrap-retries: 1 # Default: 0 (disabled). Retries before first byte is sent. + +# Gemini API keys +# gemini-api-key: +# - api-key: "AIzaSy...01" +# prefix: "test" # optional: require calls like "test/gemini-3-pro-preview" to target this credential +# base-url: "https://generativelanguage.googleapis.com" +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" +# models: +# - name: "gemini-2.5-flash" # upstream model name +# alias: "gemini-flash" # client alias mapped to the upstream model +# excluded-models: +# - "gemini-2.5-pro" # exclude specific models from this provider (exact match) +# - "gemini-2.5-*" # wildcard matching prefix (e.g. gemini-2.5-flash, gemini-2.5-pro) +# - "*-preview" # wildcard matching suffix (e.g. gemini-3-pro-preview) +# - "*flash*" # wildcard matching substring (e.g. gemini-2.5-flash-lite) +# - api-key: "AIzaSy...02" + +# Codex API keys +# codex-api-key: +# - api-key: "sk-atSM..." +# prefix: "test" # optional: require calls like "test/gpt-5-codex" to target this credential +# base-url: "https://www.example.com" # use the custom codex API endpoint +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override +# models: +# - name: "gpt-5-codex" # upstream model name +# alias: "codex-latest" # client alias mapped to the upstream model +# excluded-models: +# - "gpt-5.1" # exclude specific models (exact match) +# - "gpt-5-*" # wildcard matching prefix (e.g. gpt-5-medium, gpt-5-codex) +# - "*-mini" # wildcard matching suffix (e.g. gpt-5-codex-mini) +# - "*codex*" # wildcard matching substring (e.g. gpt-5-codex-low) + +# Claude API keys +# claude-api-key: +# - api-key: "sk-atSM..." # use the official claude API key, no need to set the base url +# - api-key: "sk-atSM..." +# prefix: "test" # optional: require calls like "test/claude-sonnet-latest" to target this credential +# base-url: "https://www.example.com" # use the custom claude API endpoint +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override +# models: +# - name: "claude-3-5-sonnet-20241022" # upstream model name +# alias: "claude-sonnet-latest" # client alias mapped to the upstream model +# excluded-models: +# - "claude-opus-4-5-20251101" # exclude specific models (exact match) +# - "claude-3-*" # wildcard matching prefix (e.g. claude-3-7-sonnet-20250219) +# - "*-thinking" # wildcard matching suffix (e.g. claude-opus-4-5-thinking) +# - "*haiku*" # wildcard matching substring (e.g. claude-3-5-haiku-20241022) +# cloak: # optional: request cloaking for non-Claude-Code clients +# mode: "auto" # "auto" (default): cloak only when client is not Claude Code +# # "always": always apply cloaking +# # "never": never apply cloaking +# strict-mode: false # false (default): prepend Claude Code prompt to user system messages +# # true: strip all user system messages, keep only Claude Code prompt +# sensitive-words: # optional: words to obfuscate with zero-width characters +# - "API" +# - "proxy" +# cache-user-id: true # optional: default is false; set true to reuse cached user_id per API key instead of generating a random one each request + +# Default headers for Claude API requests. Update when Claude Code releases new versions. +# These are used as fallbacks when the client does not send its own headers. +# claude-header-defaults: +# user-agent: "claude-cli/2.1.44 (external, sdk-cli)" +# package-version: "0.74.0" +# runtime-version: "v24.3.0" +# timeout: "600" + +# OpenAI compatibility providers +# openai-compatibility: +# - name: "openrouter" # The name of the provider; it will be used in the user agent and other places. +# prefix: "test" # optional: require calls like "test/kimi-k2" to target this provider's credentials +# base-url: "https://openrouter.ai/api/v1" # The base URL of the provider. +# headers: +# X-Custom-Header: "custom-value" +# api-key-entries: +# - api-key: "sk-or-v1-...b780" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override +# - api-key: "sk-or-v1-...b781" # without proxy-url +# models: # The models supported by the provider. +# - name: "moonshotai/kimi-k2:free" # The actual model name. +# alias: "kimi-k2" # The alias used in the API. + +# Vertex API keys (Vertex-compatible endpoints, use API key + base URL) +# vertex-api-key: +# - api-key: "vk-123..." # x-goog-api-key header +# prefix: "test" # optional: require calls like "test/vertex-pro" to target this credential +# base-url: "https://example.com/api" # e.g. https://zenmux.ai/api +# proxy-url: "socks5://proxy.example.com:1080" # optional per-key proxy override +# headers: +# X-Custom-Header: "custom-value" +# models: # optional: map aliases to upstream model names +# - name: "gemini-2.5-flash" # upstream model name +# alias: "vertex-flash" # client-visible alias +# - name: "gemini-2.5-pro" +# alias: "vertex-pro" + +# Amp Integration +# ampcode: +# # Configure upstream URL for Amp CLI OAuth and management features +# upstream-url: "https://ampcode.com" +# # Optional: Override API key for Amp upstream (otherwise uses env or file) +# upstream-api-key: "" +# # Per-client upstream API key mapping +# # Maps client API keys (from top-level api-keys) to different Amp upstream API keys. +# # Useful when different clients need to use different Amp accounts/quotas. +# # If a client key isn't mapped, falls back to upstream-api-key (default behavior). +# upstream-api-keys: +# - upstream-api-key: "amp_key_for_team_a" # Upstream key to use for these clients +# api-keys: # Client keys that use this upstream key +# - "your-api-key-1" +# - "your-api-key-2" +# - upstream-api-key: "amp_key_for_team_b" +# api-keys: +# - "your-api-key-3" +# # Restrict Amp management routes (/api/auth, /api/user, etc.) to localhost only (default: false) +# restrict-management-to-localhost: false +# # Force model mappings to run before checking local API keys (default: false) +# force-model-mappings: false +# # Amp Model Mappings +# # Route unavailable Amp models to alternative models available in your local proxy. +# # Useful when Amp CLI requests models you don't have access to (e.g., Claude Opus 4.5) +# # but you have a similar model available (e.g., Claude Sonnet 4). +# model-mappings: +# - from: "claude-opus-4-5-20251101" # Model requested by Amp CLI +# to: "gemini-claude-opus-4-5-thinking" # Route to this available model instead +# - from: "claude-sonnet-4-5-20250929" +# to: "gemini-claude-sonnet-4-5-thinking" +# - from: "claude-haiku-4-5-20251001" +# to: "gemini-2.5-flash" + +# Global OAuth model name aliases (per channel) +# These aliases rename model IDs for both model listing and request routing. +# Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow, kimi. +# NOTE: Aliases do not apply to gemini-api-key, codex-api-key, claude-api-key, openai-compatibility, vertex-api-key, or ampcode. +# You can repeat the same name with different aliases to expose multiple client model names. +# oauth-model-alias: +# gemini-cli: +# - name: "gemini-2.5-pro" # original model name under this channel +# alias: "g2.5p" # client-visible alias +# fork: true # when true, keep original and also add the alias as an extra model (default: false) +# vertex: +# - name: "gemini-2.5-pro" +# alias: "g2.5p" +# aistudio: +# - name: "gemini-2.5-pro" +# alias: "g2.5p" +# antigravity: +# - name: "gemini-3-pro-high" +# alias: "gemini-3-pro-preview" +# claude: +# - name: "claude-sonnet-4-5-20250929" +# alias: "cs4.5" +# codex: +# - name: "gpt-5" +# alias: "g5" +# qwen: +# - name: "qwen3-coder-plus" +# alias: "qwen-plus" +# iflow: +# - name: "glm-4.7" +# alias: "glm-god" +# kimi: +# - name: "kimi-k2.5" +# alias: "k2.5" + +# OAuth provider excluded models +# oauth-excluded-models: +# gemini-cli: +# - "gemini-2.5-pro" # exclude specific models (exact match) +# - "gemini-2.5-*" # wildcard matching prefix (e.g. gemini-2.5-flash, gemini-2.5-pro) +# - "*-preview" # wildcard matching suffix (e.g. gemini-3-pro-preview) +# - "*flash*" # wildcard matching substring (e.g. gemini-2.5-flash-lite) +# vertex: +# - "gemini-3-pro-preview" +# aistudio: +# - "gemini-3-pro-preview" +# antigravity: +# - "gemini-3-pro-preview" +# claude: +# - "claude-3-5-haiku-20241022" +# codex: +# - "gpt-5-codex-mini" +# qwen: +# - "vision-model" +# iflow: +# - "tstars2.0" +# kimi: +# - "kimi-k2-thinking" + +# Optional payload configuration +# payload: +# default: # Default rules only set parameters when they are missing in the payload. +# - models: +# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*") +# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> value +# "generationConfig.thinkingConfig.thinkingBudget": 32768 +# default-raw: # Default raw rules set parameters using raw JSON when missing (must be valid JSON). +# - models: +# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*") +# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON) +# "generationConfig.responseJsonSchema": "{\"type\":\"object\",\"properties\":{\"answer\":{\"type\":\"string\"}}}" +# override: # Override rules always set parameters, overwriting any existing values. +# - models: +# - name: "gpt-*" # Supports wildcards (e.g., "gpt-*") +# protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> value +# "reasoning.effort": "high" +# override-raw: # Override raw rules always set parameters using raw JSON (must be valid JSON). +# - models: +# - name: "gpt-*" # Supports wildcards (e.g., "gpt-*") +# protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON) +# "response_format": "{\"type\":\"json_schema\",\"json_schema\":{\"name\":\"answer\",\"schema\":{\"type\":\"object\"}}}" +# filter: # Filter rules remove specified parameters from the payload. +# - models: +# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*") +# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON paths (gjson/sjson syntax) to remove from the payload +# - "generationConfig.thinkingConfig.thinkingBudget" +# - "generationConfig.responseJsonSchema" \ No newline at end of file diff --git a/apps/cli-proxy-api/6.8.41/docker-compose.yml b/apps/cli-proxy-api/6.8.41/docker-compose.yml new file mode 100644 index 00000000..3bff3f97 --- /dev/null +++ b/apps/cli-proxy-api/6.8.41/docker-compose.yml @@ -0,0 +1,26 @@ +services: + cli-proxy-api: + image: "eceasy/cli-proxy-api:v6.8.41" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + environment: + - DEPLOY=${DEPLOY:-} + ports: + - "${PANEL_APP_PORT_HTTP}:8317" + - "${CLI_PROXY_PORT_8085}:8085" + - "${CLI_PROXY_PORT_1455}:1455" + - "${CLI_PROXY_PORT_54545}:54545" + - "${CLI_PROXY_PORT_51121}:51121" + - "${CLI_PROXY_PORT_11451}:11451" + volumes: + - "./data/config.yaml:/CLIProxyAPI/config.yaml" + - "./data/auths:/root/.cli-proxy-api" + - "./data/logs:/CLIProxyAPI/logs" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/cli-proxy-api/README.md b/apps/cli-proxy-api/README.md new file mode 100644 index 00000000..c8badb0b --- /dev/null +++ b/apps/cli-proxy-api/README.md @@ -0,0 +1,25 @@ +# CLIProxyAPI + +一个为 CLI 提供 OpenAI/Gemini/Claude/Codex 兼容 API 接口的代理服务器。 + +## 相关链接 +- [GitHub 仓库](https://github.com/router-for-me/CLIProxyAPI) + +## 数据配置说明 +安装本应用后,相关数据和日志挂载在 1Panel 应用安装目录下的 `data` 文件夹中。 +- `config.yaml`: 配置文件 +- `auths`: 认证信息缓存目录 +- `logs`: 运行日志目录 + +## 使用 CLI Proxy API 自带的 Web UI + +**注意**:要开启远程访问,应该先修改 `config.yaml` 里的以下几个部分: +```yaml +remote-management: + allow-remote: false + secret-key: "" +``` + +1. 启动 CLI Proxy API 服务。 +2. 打开:`http://:/management.html` +3. 输入 **管理密钥** 并连接。 diff --git a/apps/cli-proxy-api/data.yml b/apps/cli-proxy-api/data.yml new file mode 100644 index 00000000..42865763 --- /dev/null +++ b/apps/cli-proxy-api/data.yml @@ -0,0 +1,32 @@ +name: CLIProxyAPI +tags: + - AI +title: 一个为 CLI 提供 OpenAI 等兼容 API 接口的代理服务器 +description: 一个为 CLI 提供 OpenAI 等兼容 API 接口的代理服务器 +additionalProperties: + key: cli-proxy-api + name: CLIProxyAPI + tags: + - AI + shortDescZh: 一个为 CLI 提供 OpenAI 等兼容 API 接口的代理服务器 + shortDescEn: A proxy server providing OpenAI-compatible API interfaces for CLI + description: + en: A proxy server providing OpenAI-compatible API interfaces for CLI + ja: CLI向けにOpenAI互換APIインターフェースを提供するプロキシサーバー + ms: Pelayan proksi yang menyediakan antara muka API serasi OpenAI untuk CLI + pt-br: Um servidor proxy que fornece interfaces de API compatíveis com OpenAI para CLI + ru: Прокси-сервер, предоставляющий OpenAI-совместимые API-интерфейсы для CLI + ko: CLI용 OpenAI 호환 API 인터페이스를 제공하는 프록시 서버 + zh-Hant: 一個為 CLI 提供 OpenAI 等兼容 API 接口的 + zh: 一个为 CLI 提供 OpenAI 等兼容 API 接口的代理服务器 + type: website + crossVersionUpdate: true + limit: 0 + recommend: 0 + batchInstallSupport: true + website: https://help.router-for.me/ + github: https://github.com/router-for-me/CLIProxyAPI + document: https://help.router-for.me/ + architectures: + - amd64 + - arm64 diff --git a/apps/cli-proxy-api/latest/.env.sample b/apps/cli-proxy-api/latest/.env.sample new file mode 100644 index 00000000..94e7d3dd --- /dev/null +++ b/apps/cli-proxy-api/latest/.env.sample @@ -0,0 +1,8 @@ +CLI_PROXY_PORT_11451=11451 +CLI_PROXY_PORT_1455=1455 +CLI_PROXY_PORT_51121=51121 +CLI_PROXY_PORT_54545=54545 +CLI_PROXY_PORT_8085=8085 +CONTAINER_NAME="cliproxyapi" +DEPLOY="" +PANEL_APP_PORT_HTTP=8317 diff --git a/apps/cli-proxy-api/latest/data.yml b/apps/cli-proxy-api/latest/data.yml new file mode 100644 index 00000000..ddf03670 --- /dev/null +++ b/apps/cli-proxy-api/latest/data.yml @@ -0,0 +1,108 @@ +additionalProperties: + formFields: + - default: 8317 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port (8317) + labelZh: Web 访问端口 (8317) + label: + en: Web Port (8317) + ja: Webポート (8317) + ms: Web Port (8317) + pt-br: Porta Web (8317) + ru: Веб-порт (8317) + ko: 웹 포트 (8317) + zh-Hant: Web 訪問埠 (8317) + zh: Web 访问端口 (8317) + required: true + rule: paramPort + type: number + - default: 8085 + edit: true + envKey: CLI_PROXY_PORT_8085 + label: + en: Port 8085 + ja: ポート 8085 + ms: Port 8085 + pt-br: Porta 8085 + ru: Порт 8085 + ko: 포트 8085 + zh-Hant: 埠 8085 + zh: 端口 8085 + required: true + rule: paramPort + type: number + - default: 1455 + edit: true + envKey: CLI_PROXY_PORT_1455 + label: + en: Port 1455 + ja: ポート 1455 + ms: Port 1455 + pt-br: Porta 1455 + ru: Порт 1455 + ko: 포트 1455 + zh-Hant: 埠 1455 + zh: 端口 1455 + required: true + rule: paramPort + type: number + - default: 54545 + edit: true + envKey: CLI_PROXY_PORT_54545 + label: + en: Port 54545 + ja: ポート 54545 + ms: Port 54545 + pt-br: Porta 54545 + ru: Порт 54545 + ko: 포트 54545 + zh-Hant: 埠 54545 + zh: 端口 54545 + required: true + rule: paramPort + type: number + - default: 51121 + edit: true + envKey: CLI_PROXY_PORT_51121 + label: + en: Port 51121 + ja: ポート 51121 + ms: Port 51121 + pt-br: Porta 51121 + ru: Порт 51121 + ko: 포트 51121 + zh-Hant: 埠 51121 + zh: 端口 51121 + required: true + rule: paramPort + type: number + - default: 11451 + edit: true + envKey: CLI_PROXY_PORT_11451 + label: + en: Port 11451 + ja: ポート 11451 + ms: Port 11451 + pt-br: Porta 11451 + ru: Порт 11451 + ko: 포트 11451 + zh-Hant: 埠 11451 + zh: 端口 11451 + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: DEPLOY + label: + en: Deploy Env Variable + ja: デプロイ環境変数 + ms: Variabel Persekitaran Deploy + pt-br: Variável de Ambiente de Deploy + ru: Переменная среды развертывания + ko: 배포 환경 변수 + zh-Hant: 部署環境變數 + zh: 部署环境变量 + required: false + type: text diff --git a/apps/cli-proxy-api/latest/data/config.yaml b/apps/cli-proxy-api/latest/data/config.yaml new file mode 100644 index 00000000..fbabac8f --- /dev/null +++ b/apps/cli-proxy-api/latest/data/config.yaml @@ -0,0 +1,330 @@ +# Server host/interface to bind to. Default is empty ("") to bind all interfaces (IPv4 + IPv6). +# Use "127.0.0.1" or "localhost" to restrict access to local machine only. +host: "" + +# Server port +port: 8317 + +# TLS settings for HTTPS. When enabled, the server listens with the provided certificate and key. +tls: + enable: false + cert: "" + key: "" + +# Management API settings +remote-management: + # Whether to allow remote (non-localhost) management access. + # When false, only localhost can access management endpoints (a key is still required). + allow-remote: false + + # Management key. If a plaintext value is provided here, it will be hashed on startup. + # All management requests (even from localhost) require this key. + # Leave empty to disable the Management API entirely (404 for all /v0/management routes). + secret-key: "" + + # Disable the bundled management control panel asset download and HTTP route when true. + disable-control-panel: false + + # GitHub repository for the management control panel. Accepts a repository URL or releases API URL. + panel-github-repository: "https://github.com/router-for-me/Cli-Proxy-API-Management-Center" + +# Authentication directory (supports ~ for home directory) +auth-dir: "~/.cli-proxy-api" + +# API keys for authentication +api-keys: + - "your-api-key-1" + - "your-api-key-2" + - "your-api-key-3" + +# Enable debug logging +debug: false + +# Enable pprof HTTP debug server (host:port). Keep it bound to localhost for safety. +pprof: + enable: false + addr: "127.0.0.1:8316" + +# When true, disable high-overhead HTTP middleware features to reduce per-request memory usage under high concurrency. +commercial-mode: false + +# When true, write application logs to rotating files instead of stdout +logging-to-file: false + +# Maximum total size (MB) of log files under the logs directory. When exceeded, the oldest log +# files are deleted until within the limit. Set to 0 to disable. +logs-max-total-size-mb: 0 + +# Maximum number of error log files retained when request logging is disabled. +# When exceeded, the oldest error log files are deleted. Default is 10. Set to 0 to disable cleanup. +error-logs-max-files: 10 + +# When false, disable in-memory usage statistics aggregation +usage-statistics-enabled: false + +# Proxy URL. Supports socks5/http/https protocols. Example: socks5://user:pass@192.168.1.1:1080/ +proxy-url: "" + +# When true, unprefixed model requests only use credentials without a prefix (except when prefix == model name). +force-model-prefix: false + +# When true, forward filtered upstream response headers to downstream clients. +# Default is false (disabled). +passthrough-headers: false + +# Number of times to retry a request. Retries will occur if the HTTP response code is 403, 408, 500, 502, 503, or 504. +request-retry: 3 + +# Maximum number of different credentials to try for one failed request. +# Set to 0 to keep legacy behavior (try all available credentials). +max-retry-credentials: 0 + +# Maximum wait time in seconds for a cooled-down credential before triggering a retry. +max-retry-interval: 30 + +# Quota exceeded behavior +quota-exceeded: + switch-project: true # Whether to automatically switch to another project when a quota is exceeded + switch-preview-model: true # Whether to automatically switch to a preview model when a quota is exceeded + +# Routing strategy for selecting credentials when multiple match. +routing: + strategy: "round-robin" # round-robin (default), fill-first + +# When true, enable authentication for the WebSocket API (/v1/ws). +ws-auth: false + +# When > 0, emit blank lines every N seconds for non-streaming responses to prevent idle timeouts. +nonstream-keepalive-interval: 0 + +# Streaming behavior (SSE keep-alives + safe bootstrap retries). +# streaming: +# keepalive-seconds: 15 # Default: 0 (disabled). <= 0 disables keep-alives. +# bootstrap-retries: 1 # Default: 0 (disabled). Retries before first byte is sent. + +# Gemini API keys +# gemini-api-key: +# - api-key: "AIzaSy...01" +# prefix: "test" # optional: require calls like "test/gemini-3-pro-preview" to target this credential +# base-url: "https://generativelanguage.googleapis.com" +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" +# models: +# - name: "gemini-2.5-flash" # upstream model name +# alias: "gemini-flash" # client alias mapped to the upstream model +# excluded-models: +# - "gemini-2.5-pro" # exclude specific models from this provider (exact match) +# - "gemini-2.5-*" # wildcard matching prefix (e.g. gemini-2.5-flash, gemini-2.5-pro) +# - "*-preview" # wildcard matching suffix (e.g. gemini-3-pro-preview) +# - "*flash*" # wildcard matching substring (e.g. gemini-2.5-flash-lite) +# - api-key: "AIzaSy...02" + +# Codex API keys +# codex-api-key: +# - api-key: "sk-atSM..." +# prefix: "test" # optional: require calls like "test/gpt-5-codex" to target this credential +# base-url: "https://www.example.com" # use the custom codex API endpoint +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override +# models: +# - name: "gpt-5-codex" # upstream model name +# alias: "codex-latest" # client alias mapped to the upstream model +# excluded-models: +# - "gpt-5.1" # exclude specific models (exact match) +# - "gpt-5-*" # wildcard matching prefix (e.g. gpt-5-medium, gpt-5-codex) +# - "*-mini" # wildcard matching suffix (e.g. gpt-5-codex-mini) +# - "*codex*" # wildcard matching substring (e.g. gpt-5-codex-low) + +# Claude API keys +# claude-api-key: +# - api-key: "sk-atSM..." # use the official claude API key, no need to set the base url +# - api-key: "sk-atSM..." +# prefix: "test" # optional: require calls like "test/claude-sonnet-latest" to target this credential +# base-url: "https://www.example.com" # use the custom claude API endpoint +# headers: +# X-Custom-Header: "custom-value" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override +# models: +# - name: "claude-3-5-sonnet-20241022" # upstream model name +# alias: "claude-sonnet-latest" # client alias mapped to the upstream model +# excluded-models: +# - "claude-opus-4-5-20251101" # exclude specific models (exact match) +# - "claude-3-*" # wildcard matching prefix (e.g. claude-3-7-sonnet-20250219) +# - "*-thinking" # wildcard matching suffix (e.g. claude-opus-4-5-thinking) +# - "*haiku*" # wildcard matching substring (e.g. claude-3-5-haiku-20241022) +# cloak: # optional: request cloaking for non-Claude-Code clients +# mode: "auto" # "auto" (default): cloak only when client is not Claude Code +# # "always": always apply cloaking +# # "never": never apply cloaking +# strict-mode: false # false (default): prepend Claude Code prompt to user system messages +# # true: strip all user system messages, keep only Claude Code prompt +# sensitive-words: # optional: words to obfuscate with zero-width characters +# - "API" +# - "proxy" +# cache-user-id: true # optional: default is false; set true to reuse cached user_id per API key instead of generating a random one each request + +# Default headers for Claude API requests. Update when Claude Code releases new versions. +# These are used as fallbacks when the client does not send its own headers. +# claude-header-defaults: +# user-agent: "claude-cli/2.1.44 (external, sdk-cli)" +# package-version: "0.74.0" +# runtime-version: "v24.3.0" +# timeout: "600" + +# OpenAI compatibility providers +# openai-compatibility: +# - name: "openrouter" # The name of the provider; it will be used in the user agent and other places. +# prefix: "test" # optional: require calls like "test/kimi-k2" to target this provider's credentials +# base-url: "https://openrouter.ai/api/v1" # The base URL of the provider. +# headers: +# X-Custom-Header: "custom-value" +# api-key-entries: +# - api-key: "sk-or-v1-...b780" +# proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override +# - api-key: "sk-or-v1-...b781" # without proxy-url +# models: # The models supported by the provider. +# - name: "moonshotai/kimi-k2:free" # The actual model name. +# alias: "kimi-k2" # The alias used in the API. + +# Vertex API keys (Vertex-compatible endpoints, use API key + base URL) +# vertex-api-key: +# - api-key: "vk-123..." # x-goog-api-key header +# prefix: "test" # optional: require calls like "test/vertex-pro" to target this credential +# base-url: "https://example.com/api" # e.g. https://zenmux.ai/api +# proxy-url: "socks5://proxy.example.com:1080" # optional per-key proxy override +# headers: +# X-Custom-Header: "custom-value" +# models: # optional: map aliases to upstream model names +# - name: "gemini-2.5-flash" # upstream model name +# alias: "vertex-flash" # client-visible alias +# - name: "gemini-2.5-pro" +# alias: "vertex-pro" + +# Amp Integration +# ampcode: +# # Configure upstream URL for Amp CLI OAuth and management features +# upstream-url: "https://ampcode.com" +# # Optional: Override API key for Amp upstream (otherwise uses env or file) +# upstream-api-key: "" +# # Per-client upstream API key mapping +# # Maps client API keys (from top-level api-keys) to different Amp upstream API keys. +# # Useful when different clients need to use different Amp accounts/quotas. +# # If a client key isn't mapped, falls back to upstream-api-key (default behavior). +# upstream-api-keys: +# - upstream-api-key: "amp_key_for_team_a" # Upstream key to use for these clients +# api-keys: # Client keys that use this upstream key +# - "your-api-key-1" +# - "your-api-key-2" +# - upstream-api-key: "amp_key_for_team_b" +# api-keys: +# - "your-api-key-3" +# # Restrict Amp management routes (/api/auth, /api/user, etc.) to localhost only (default: false) +# restrict-management-to-localhost: false +# # Force model mappings to run before checking local API keys (default: false) +# force-model-mappings: false +# # Amp Model Mappings +# # Route unavailable Amp models to alternative models available in your local proxy. +# # Useful when Amp CLI requests models you don't have access to (e.g., Claude Opus 4.5) +# # but you have a similar model available (e.g., Claude Sonnet 4). +# model-mappings: +# - from: "claude-opus-4-5-20251101" # Model requested by Amp CLI +# to: "gemini-claude-opus-4-5-thinking" # Route to this available model instead +# - from: "claude-sonnet-4-5-20250929" +# to: "gemini-claude-sonnet-4-5-thinking" +# - from: "claude-haiku-4-5-20251001" +# to: "gemini-2.5-flash" + +# Global OAuth model name aliases (per channel) +# These aliases rename model IDs for both model listing and request routing. +# Supported channels: gemini-cli, vertex, aistudio, antigravity, claude, codex, qwen, iflow, kimi. +# NOTE: Aliases do not apply to gemini-api-key, codex-api-key, claude-api-key, openai-compatibility, vertex-api-key, or ampcode. +# You can repeat the same name with different aliases to expose multiple client model names. +# oauth-model-alias: +# gemini-cli: +# - name: "gemini-2.5-pro" # original model name under this channel +# alias: "g2.5p" # client-visible alias +# fork: true # when true, keep original and also add the alias as an extra model (default: false) +# vertex: +# - name: "gemini-2.5-pro" +# alias: "g2.5p" +# aistudio: +# - name: "gemini-2.5-pro" +# alias: "g2.5p" +# antigravity: +# - name: "gemini-3-pro-high" +# alias: "gemini-3-pro-preview" +# claude: +# - name: "claude-sonnet-4-5-20250929" +# alias: "cs4.5" +# codex: +# - name: "gpt-5" +# alias: "g5" +# qwen: +# - name: "qwen3-coder-plus" +# alias: "qwen-plus" +# iflow: +# - name: "glm-4.7" +# alias: "glm-god" +# kimi: +# - name: "kimi-k2.5" +# alias: "k2.5" + +# OAuth provider excluded models +# oauth-excluded-models: +# gemini-cli: +# - "gemini-2.5-pro" # exclude specific models (exact match) +# - "gemini-2.5-*" # wildcard matching prefix (e.g. gemini-2.5-flash, gemini-2.5-pro) +# - "*-preview" # wildcard matching suffix (e.g. gemini-3-pro-preview) +# - "*flash*" # wildcard matching substring (e.g. gemini-2.5-flash-lite) +# vertex: +# - "gemini-3-pro-preview" +# aistudio: +# - "gemini-3-pro-preview" +# antigravity: +# - "gemini-3-pro-preview" +# claude: +# - "claude-3-5-haiku-20241022" +# codex: +# - "gpt-5-codex-mini" +# qwen: +# - "vision-model" +# iflow: +# - "tstars2.0" +# kimi: +# - "kimi-k2-thinking" + +# Optional payload configuration +# payload: +# default: # Default rules only set parameters when they are missing in the payload. +# - models: +# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*") +# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> value +# "generationConfig.thinkingConfig.thinkingBudget": 32768 +# default-raw: # Default raw rules set parameters using raw JSON when missing (must be valid JSON). +# - models: +# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*") +# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON) +# "generationConfig.responseJsonSchema": "{\"type\":\"object\",\"properties\":{\"answer\":{\"type\":\"string\"}}}" +# override: # Override rules always set parameters, overwriting any existing values. +# - models: +# - name: "gpt-*" # Supports wildcards (e.g., "gpt-*") +# protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> value +# "reasoning.effort": "high" +# override-raw: # Override raw rules always set parameters using raw JSON (must be valid JSON). +# - models: +# - name: "gpt-*" # Supports wildcards (e.g., "gpt-*") +# protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON) +# "response_format": "{\"type\":\"json_schema\",\"json_schema\":{\"name\":\"answer\",\"schema\":{\"type\":\"object\"}}}" +# filter: # Filter rules remove specified parameters from the payload. +# - models: +# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*") +# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity +# params: # JSON paths (gjson/sjson syntax) to remove from the payload +# - "generationConfig.thinkingConfig.thinkingBudget" +# - "generationConfig.responseJsonSchema" \ No newline at end of file diff --git a/apps/cli-proxy-api/latest/docker-compose.yml b/apps/cli-proxy-api/latest/docker-compose.yml new file mode 100644 index 00000000..e06e8041 --- /dev/null +++ b/apps/cli-proxy-api/latest/docker-compose.yml @@ -0,0 +1,26 @@ +services: + cli-proxy-api: + image: "eceasy/cli-proxy-api:latest" + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + environment: + - DEPLOY=${DEPLOY:-} + ports: + - "${PANEL_APP_PORT_HTTP}:8317" + - "${CLI_PROXY_PORT_8085}:8085" + - "${CLI_PROXY_PORT_1455}:1455" + - "${CLI_PROXY_PORT_54545}:54545" + - "${CLI_PROXY_PORT_51121}:51121" + - "${CLI_PROXY_PORT_11451}:11451" + volumes: + - "./data/config.yaml:/CLIProxyAPI/config.yaml" + - "./data/auths:/root/.cli-proxy-api" + - "./data/logs:/CLIProxyAPI/logs" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/cli-proxy-api/logo.png b/apps/cli-proxy-api/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..31ceced3d05c954fe142106ae789a324972db980 GIT binary patch literal 8721 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 literal 0 HcmV?d00001