From c580e0e8c0caefce503a68574e35a8c1b6c72059 Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:40:11 +0800
Subject: [PATCH 1/9] feat(proxy): add --host / CSSWITCH_BIND_HOST for
containerized deployments
---
proxy/csswitch_proxy.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/proxy/csswitch_proxy.py b/proxy/csswitch_proxy.py
index 60780dd..ebfada9 100644
--- a/proxy/csswitch_proxy.py
+++ b/proxy/csswitch_proxy.py
@@ -640,6 +640,8 @@ def _replay_as_sse(self, aresp):
ap = argparse.ArgumentParser()
ap.add_argument("--provider", default=os.environ.get("CSSWITCH_PROVIDER", "deepseek"),
choices=list(PROVIDERS.keys()))
+ ap.add_argument("--host", default=os.environ.get("CSSWITCH_BIND_HOST", "127.0.0.1"),
+ help="绑定地址(默认 127.0.0.1;容器内可设 0.0.0.0)")
ap.add_argument("--port", type=int, default=18991)
ap.add_argument("--env-file", default=None)
ap.add_argument("--log", default=None)
@@ -648,6 +650,7 @@ def _replay_as_sse(self, aresp):
PROV_NAME = args.provider
PROV = PROVIDERS[PROV_NAME]
LOG = args.log
+ HOST = args.host
KEY = load_key(PROV, args)
AUTH_SECRET = os.environ.get("CSSWITCH_AUTH_TOKEN") or args.auth_token
_up = os.environ.get("CSSWITCH_UPSTREAM_URL")
@@ -657,14 +660,14 @@ def _replay_as_sse(self, aresp):
if not KEY:
print(f"找不到 {PROV['key_env']}。用环境变量或 --env-file <路径> 提供。", file=sys.stderr)
sys.exit(1)
- log(f"CSSwitch 代理启动 127.0.0.1:{args.port} provider={PROV_NAME} "
+ log(f"CSSwitch 代理启动 {HOST}:{args.port} provider={PROV_NAME} "
f"key=已加载(未显示) 上游={PROV['url']}")
# 绑定重试:上次会话遗留的孤儿代理可能还占着端口(app 侧会主动清,但退干净需一点时间)。
# 重试 ~3s 等端口释放,避免一次绑不上就直接失败(Errno 48)。
srv = None
for attempt in range(10):
try:
- srv = ThreadingHTTPServer(("127.0.0.1", args.port), H)
+ srv = ThreadingHTTPServer((HOST, args.port), H)
break
except OSError as e:
if attempt == 9:
From aca03de4bd0f1a8646a458c237003bddac01416e Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:40:42 +0800
Subject: [PATCH 2/9] feat(docker): add Dockerfile and build artifacts for
containerized proxy
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
基于 python:3.11-slim,非 root 运行(csswitch 用户),零 pip 依赖。
.dockerignore 排除 .git、.sandbox、findings、desktop 等无关文件。
.env.example 提供完整的环境变量模板与注释。
Co-Authored-By: Claude
---
docker/.dockerignore | 35 +++++++++++++++++++++++++++++++++++
docker/.env.example | 29 +++++++++++++++++++++++++++++
docker/Dockerfile | 22 ++++++++++++++++++++++
3 files changed, 86 insertions(+)
create mode 100644 docker/.dockerignore
create mode 100644 docker/.env.example
create mode 100644 docker/Dockerfile
diff --git a/docker/.dockerignore b/docker/.dockerignore
new file mode 100644
index 0000000..8dd407e
--- /dev/null
+++ b/docker/.dockerignore
@@ -0,0 +1,35 @@
+# 版本管理
+.git
+.gitignore
+.gitattributes
+.gitmodules
+.gitleaks.toml
+
+# GitHub
+.github
+
+# 运行时
+.sandbox
+.sandbox/
+
+# 项目特有
+findings/
+findings/auto-maint/
+desktop/
+docs/assets/
+node_modules/
+
+# Python
+__pycache__/
+*.pyc
+*.pyo
+*.egg-info/
+
+# 日志
+*.log
+logs/
+
+# 本地 env(按 .env.example 模板手写,不提交)
+.env
+.env.local
+.env.production
diff --git a/docker/.env.example b/docker/.env.example
new file mode 100644
index 0000000..1a4b715
--- /dev/null
+++ b/docker/.env.example
@@ -0,0 +1,29 @@
+# CSSwitch 代理容器环境变量配置
+# 复制本文件为 .env,填入你的 key 后启动:
+# cp .env.example .env
+# docker compose -f docker/docker-compose.yml up -d
+
+# ----- 必须设置一个(视 provider 而定)-----
+
+# DeepSeek API Key(选择 deepseek provider 时必填)
+# 获取:https://platform.deepseek.com/api_keys
+DEEPSEEK_API_KEY=
+
+# 阿里通义千问(DashScope)API Key(选择 qwen provider 时必填)
+# 获取:https://bailian.console.aliyun.com/
+DASHSCOPE_API_KEY=
+
+# ----- 可选配置(按需修改)-----
+
+# Provider:deepseek(默认)或 qwen
+CSSWITCH_PROVIDER=deepseek
+
+# 代理监听端口(需与 docker-compose.yml ports 映射一致)
+CSSWITCH_PROXY_PORT=18991
+
+# 路径 secret 鉴权(建议设置随机字符串,防未经授权的访问)
+# 生成:openssl rand -hex 16
+CSSWITCH_AUTH_TOKEN=
+
+# 上游 URL 覆盖(仅高级用法,一般无需设置)
+# CSSWITCH_UPSTREAM_URL=https://api.deepseek.com/anthropic/v1/messages
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..c1cc34a
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,22 @@
+# CSSwitch 翻译代理容器镜像
+# - 纯 Python 标准库,零 pip 依赖
+# - 非 root 运行,最小攻击面
+# - 默认监听 0.0.0.0:18991(`--host` 与 `--port` 可通过 CMD 覆盖)
+FROM python:3.11-slim
+
+# 创建非 root 用户
+RUN addgroup --system --gid 1001 csswitch \
+ && adduser --system --uid 1001 --ingroup csswitch --no-create-home csswitch
+
+WORKDIR /app
+
+# 只拷代理脚本及支持文件(纯 stdlib,无 requirements.txt)
+COPY proxy/csswitch_proxy.py /app/csswitch_proxy.py
+
+# 容器内默认绑定 0.0.0.0 监听所有接口
+ENV PYTHONUNBUFFERED=1
+EXPOSE 18991
+
+USER csswitch
+
+ENTRYPOINT ["python3", "/app/csswitch_proxy.py", "--host", "0.0.0.0"]
From d5f67b7d9c8e3b9da05e3a6892e759c4a747c3d7 Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:41:16 +0800
Subject: [PATCH 3/9] feat(docker): add docker-compose.yml with healthcheck and
optional Caddy TLS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Compose 编排:csswitch-proxy 服务(env_file、端口映射、健康检查、
自动重启、日志卷)。提供可选 Caddy TLS 反代(--profile tls),
在 WSL2 内部用自签名证书保护 path secret 的传输安全。
Co-Authored-By: Claude
---
docker/Caddyfile | 22 ++++++++++++
docker/docker-compose.yml | 71 +++++++++++++++++++++++++++++++++++++++
2 files changed, 93 insertions(+)
create mode 100644 docker/Caddyfile
create mode 100644 docker/docker-compose.yml
diff --git a/docker/Caddyfile b/docker/Caddyfile
new file mode 100644
index 0000000..7ddb537
--- /dev/null
+++ b/docker/Caddyfile
@@ -0,0 +1,22 @@
+# CSSwitch Caddy TLS 反代配置
+# 用于 docker-compose.yml 的 caddy 服务(需启用 tls profile)
+# 提供本地 HTTPS 保护 path secret 不在 WSL2 内部明文传输。
+#
+# 使用自签名证书(tls internal),适合本地/内网环境。
+# 生产环境请替换 tls internal 为你的真实证书。
+
+:8443 {
+ # 自签名 TLS(本地/内网适用)
+ tls internal
+
+ # 反代到 csswitch-proxy 容器
+ reverse_proxy csswitch-proxy:{$CSSWITCH_PROXY_PORT:-18991} {
+ # 透传原始 Host 与 path(含 path secret)
+ header_up Host {host}
+ }
+
+ # 访问日志
+ log {
+ output file /data/access.log
+ }
+}
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..a3bd7ea
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,71 @@
+# CSSwitch 容器化代理 — Docker Compose 编排
+#
+# 快速启动:
+# cp docker/.env.example docker/.env
+# # 编辑 docker/.env,填入你的 API key
+# docker compose -f docker/docker-compose.yml up -d
+#
+# 可选 Caddy TLS 反代(保护 path secret 不在明文 HTTP 上传输):
+# 取消下方 caddy 服务的注释,并:
+# docker compose -f docker/docker-compose.yml --profile tls up -d
+#
+# 健康检查:
+# curl http://localhost:18991//health
+
+name: csswitch
+
+services:
+ csswitch-proxy:
+ image: csswitch-proxy:latest
+ build:
+ context: ..
+ dockerfile: docker/Dockerfile
+ container_name: csswitch-proxy
+ env_file:
+ - .env
+ ports:
+ - "${CSSWITCH_PROXY_PORT:-18991}:${CSSWITCH_PROXY_PORT:-18991}"
+ healthcheck:
+ test: ["CMD", "python3", "-c",
+ "import urllib.request, os, sys; \
+ t=os.environ.get('CSSWITCH_AUTH_TOKEN',''); \
+ p=os.environ.get('CSSWITCH_PROXY_PORT','18991'); \
+ u=f'http://127.0.0.1:{p}/{\"\" if not t else t}/health'; \
+ sys.exit(0 if 200==urllib.request.urlopen(u,timeout=3).getcode() else 1)"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 5s
+ volumes:
+ - csswitch-logs:/app/logs
+ restart: unless-stopped
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "10m"
+ max-file: "3"
+
+# ── 可选:Caddy 本地 HTTPS/TLS 反代 ──────────────────────────
+# 启用方式:docker compose -f docker/docker-compose.yml --profile tls up -d
+#
+# 注意:Caddy 使用自签名证书(tls internal),适合本地/内网环境。
+# 浏览器首次访问需确认安全例外;curl 加 -k 或 --insecure。
+#
+# caddy:
+# profiles: ["tls"]
+# image: caddy:2-alpine
+# container_name: csswitch-caddy
+# restart: unless-stopped
+# ports:
+# - "8443:8443"
+# volumes:
+# - ./Caddyfile:/etc/caddy/Caddyfile:ro
+# - caddy-data:/data
+# - caddy-config:/config
+# depends_on:
+# - csswitch-proxy
+
+volumes:
+ csswitch-logs:
+ caddy-data:
+ caddy-config:
From d69932036502dd341fc81aae22597db62e246fc1 Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:47:06 +0800
Subject: [PATCH 4/9] feat: add container deploy scripts
---
scripts/docker-build.sh | 10 +++++++
scripts/wsl2-deploy.sh | 65 +++++++++++++++++++++++++++++++++++++++++
scripts/wsl2-logs.sh | 7 +++++
scripts/wsl2-stop.sh | 9 ++++++
4 files changed, 91 insertions(+)
create mode 100644 scripts/docker-build.sh
create mode 100644 scripts/wsl2-deploy.sh
create mode 100644 scripts/wsl2-logs.sh
create mode 100644 scripts/wsl2-stop.sh
diff --git a/scripts/docker-build.sh b/scripts/docker-build.sh
new file mode 100644
index 0000000..f0cf8c2
--- /dev/null
+++ b/scripts/docker-build.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# 构建 CSSwitch 代理容器镜像。
+set -euo pipefail
+
+cd "$(dirname "$0")/.."
+
+echo "构建 CSSwitch 代理容器镜像…"
+docker build -t csswitch-proxy:latest -f docker/Dockerfile .
+echo "构建完成:csswitch-proxy:latest"
+docker images csswitch-proxy:latest
diff --git a/scripts/wsl2-deploy.sh b/scripts/wsl2-deploy.sh
new file mode 100644
index 0000000..8dbf385
--- /dev/null
+++ b/scripts/wsl2-deploy.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+# 在 WSL2 / Linux 上启动 CSSwitch 代理容器。
+# 检查依赖与 .env,调用 docker compose up -d,输出连接地址。
+set -euo pipefail
+
+cd "$(dirname "$0")/.."
+
+# ── 依赖检查 ──
+if ! command -v docker >/dev/null 2>&1; then
+ echo "✗ 未找到 docker。请先安装 Docker Engine 或 Docker Desktop。"
+ echo " 参考:https://docs.docker.com/engine/install/"
+ exit 1
+fi
+
+if ! docker compose version >/dev/null 2>&1; then
+ echo "✗ 未找到 docker compose 插件。请安装 Docker Compose v2 插件。"
+ exit 1
+fi
+
+# ── .env 检查 ──
+ENV_FILE="docker/.env"
+if [ ! -f "$ENV_FILE" ]; then
+ echo "✗ 未找到 $ENV_FILE"
+ echo " 从模板复制:cp docker/.env.example docker/.env"
+ echo " 然后编辑 docker/.env,填入你的 API key。"
+ exit 1
+fi
+
+# 提示检查 key(只检查变量名是否存在,不读值)
+set +u
+source <(grep -E '^(DEEPSEEK_API_KEY|DASHSCOPE_API_KEY)=' "$ENV_FILE" 2>/dev/null)
+PROVIDER="${CSSWITCH_PROVIDER:-deepseek}"
+if [ "$PROVIDER" = "deepseek" ] && [ -z "${DEEPSEEK_API_KEY:-}" ]; then
+ echo "⚠ CSSWITCH_PROVIDER=deepseek,但 DEEPSEEK_API_KEY 在 .env 中未设置"
+ echo " 继续启动,但代理会因缺 key 退出。"
+elif [ "$PROVIDER" = "qwen" ] && [ -z "${DASHSCOPE_API_KEY:-}" ]; then
+ echo "⚠ CSSWITCH_PROVIDER=qwen,但 DASHSCOPE_API_KEY 在 .env 中未设置"
+ echo " 继续启动,但代理会因缺 key 退出。"
+fi
+set -u
+
+# ── 启动 ──
+echo "启动 CSSwitch 代理容器…"
+docker compose -f docker/docker-compose.yml up -d
+echo
+
+# ── 输出连接地址 ──
+PORT="${CSSWITCH_PROXY_PORT:-18991}"
+SECRET="${CSSWITCH_AUTH_TOKEN:-}"
+
+# 探测 WSL2 IP(WSL2 内运行)
+WSL2_IP=""
+if [ -f /proc/version ] && grep -qi microsoft /proc/version; then
+ WSL2_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
+fi
+
+echo "=== 代理已启动 ==="
+echo " 容器内健康检查:curl http://127.0.0.1:$PORT/${SECRET:+$SECRET/}health"
+if [ -n "$WSL2_IP" ]; then
+ echo " WSL2 IP:$WSL2_IP"
+ echo " Windows 客户端 ANTHROPIC_BASE_URL:http://$WSL2_IP:$PORT/${SECRET:+$SECRET/}"
+fi
+echo ""
+echo "如需查看日志:bash scripts/wsl2-logs.sh"
+echo "如需停止服务:bash scripts/wsl2-stop.sh"
diff --git a/scripts/wsl2-logs.sh b/scripts/wsl2-logs.sh
new file mode 100644
index 0000000..bce7540
--- /dev/null
+++ b/scripts/wsl2-logs.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+# 查看 CSSwitch 代理容器日志。
+set -euo pipefail
+
+cd "$(dirname "$0")/.."
+
+docker compose -f docker/docker-compose.yml logs -f csswitch-proxy
diff --git a/scripts/wsl2-stop.sh b/scripts/wsl2-stop.sh
new file mode 100644
index 0000000..d2c64b4
--- /dev/null
+++ b/scripts/wsl2-stop.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# 停止 CSSwitch 代理容器。
+set -euo pipefail
+
+cd "$(dirname "$0")/.."
+
+echo "停止 CSSwitch 代理容器…"
+docker compose -f docker/docker-compose.yml down
+echo "已停止。"
From 9cf893928238b99ffe9e92034065190129f7db17 Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:47:35 +0800
Subject: [PATCH 5/9] feat: add Windows PowerShell deploy scripts
---
scripts/wsl2-deploy.ps1 | 107 ++++++++++++++++++++++++++++++++++++++++
scripts/wsl2-logs.ps1 | 52 +++++++++++++++++++
scripts/wsl2-stop.ps1 | 55 +++++++++++++++++++++
3 files changed, 214 insertions(+)
create mode 100644 scripts/wsl2-deploy.ps1
create mode 100644 scripts/wsl2-logs.ps1
create mode 100644 scripts/wsl2-stop.ps1
diff --git a/scripts/wsl2-deploy.ps1 b/scripts/wsl2-deploy.ps1
new file mode 100644
index 0000000..6b5d910
--- /dev/null
+++ b/scripts/wsl2-deploy.ps1
@@ -0,0 +1,107 @@
+<#
+.SYNOPSIS
+ 在 WSL2 中部署 CSSwitch 代理容器。
+.DESCRIPTION
+ 检测 WSL2 环境,在 WSL2 中调用 docker compose up -d,
+ 自动探测 WSL2 IP 并输出供 Windows 客户端使用的 ANTHROPIC_BASE_URL。
+.PARAMETER WslDistro
+ WSL2 发行版名称(默认:从 wsl -l -v 自动选择运行中的发行版)。
+.PARAMETER ProjectPath
+ 项目在 WSL2 中的绝对路径(默认:自动映射当前目录)。
+#>
+
+param(
+ [string]$WslDistro = "",
+ [string]$ProjectPath = ""
+)
+
+$ErrorActionPreference = "Stop"
+
+# ── 检测 WSL ──
+$wslAvailable = Get-Command "wsl.exe" -ErrorAction SilentlyContinue
+if (-not $wslAvailable) {
+ Write-Host "✗ 未找到 wsl.exe。请先安装 WSL2。" -ForegroundColor Red
+ exit 1
+}
+
+# 列出运行中的 WSL2 发行版
+$distros = wsl -l -v --quiet 2>$null | Where-Object { $_ -match 'Running' }
+if (-not $distros) {
+ Write-Host "✗ 未找到运行中的 WSL2 发行版。" -ForegroundColor Red
+ Write-Host " 请先启动一个 WSL2 发行版(例如:wsl -d Ubuntu)" -ForegroundColor Yellow
+ exit 1
+}
+
+if (-not $WslDistro) {
+ # 取第一个运行中的发行版
+ $WslDistro = ($distros[0] -split '\s+')[0]
+ Write-Host " 自动选择 WSL2 发行版:$WslDistro" -ForegroundColor Cyan
+}
+
+# ── 映射项目路径 ──
+if (-not $ProjectPath) {
+ $hostPath = Get-Location
+ # 尝试将 Windows 路径(如 G:\WorkSpace\CSswitch)映射到 WSL2 挂载点
+ if ($hostPath.Path -match '^([A-Z]):\\(.*)') {
+ $driveLetter = $matches[1].ToLower()
+ $restPath = $matches[2] -replace '\\', '/'
+ $ProjectPath = "/mnt/$driveLetter/$restPath"
+ } else {
+ Write-Host "✗ 无法自动映射路径:$hostPath" -ForegroundColor Red
+ Write-Host " 请用 -ProjectPath 参数指定 WSL2 内的项目路径" -ForegroundColor Yellow
+ exit 1
+ }
+}
+
+# ── 检查 .env ──
+$checkResult = wsl -d $WslDistro --cd $ProjectPath bash -c 'test -f docker/.env && echo "EXISTS" || echo "MISSING"'
+if ($checkResult -eq "MISSING") {
+ Write-Host "✗ 未找到 docker/.env" -ForegroundColor Red
+ Write-Host " 在 WSL2 中执行:cp docker/.env.example docker/.env" -ForegroundColor Yellow
+ Write-Host " 然后编辑 docker/.env,填入你的 API key。" -ForegroundColor Yellow
+ Write-Host ""
+ Write-Host " 快速编辑(在 WSL2 中):wsl -d $WslDistro --cd $ProjectPath vi docker/.env" -ForegroundColor Cyan
+ exit 1
+}
+
+# ── 部署 ──
+Write-Host "部署 CSSwitch 代理容器到 WSL2($WslDistro)…" -ForegroundColor Green
+Write-Host " 项目路径:$ProjectPath" -ForegroundColor Cyan
+
+$deployResult = wsl -d $WslDistro --cd $ProjectPath bash -c 'docker compose -f docker/docker-compose.yml up -d 2>&1'
+if ($LASTEXITCODE -ne 0) {
+ Write-Host "✗ 部署失败:" -ForegroundColor Red
+ Write-Host $deployResult -ForegroundColor Red
+ exit 1
+}
+Write-Host $deployResult
+Write-Host "✓ 容器已启动" -ForegroundColor Green
+
+# ── 获取 WSL2 IP ──
+$wsl2Ip = wsl -d $WslDistro --cd $ProjectPath bash -c "hostname -I 2>/dev/null | awk '{print \$1}'" 2>$null
+if (-not $wsl2Ip) {
+ $wsl2Ip = wsl -d $WslDistro --cd $ProjectPath bash -c "ip route get 1 2>/dev/null | head -1 | awk '{print \$7}'" 2>$null
+}
+
+# ── 读取端口与 secret ──
+$proxyPort = wsl -d $WslDistro --cd $ProjectPath bash -c 'grep "^CSSWITCH_PROXY_PORT=" docker/.env 2>/dev/null | cut -d= -f2 || echo "18991"'
+$authToken = wsl -d $WslDistro --cd $ProjectPath bash -c 'grep "^CSSWITCH_AUTH_TOKEN=" docker/.env 2>/dev/null | cut -d= -f2 || echo ""'
+
+$baseUrl = "http://${wsl2Ip}:${proxyPort}/"
+if ($authToken) {
+ $baseUrl += "${authToken}/"
+}
+
+Write-Host ""
+Write-Host "=== 部署完成 ===" -ForegroundColor Green
+Write-Host " WSL2 IP:$wsl2Ip" -ForegroundColor Cyan
+Write-Host " 代理端口:$proxyPort" -ForegroundColor Cyan
+Write-Host ""
+Write-Host " Windows 客户端配置 ANTHROPIC_BASE_URL:" -ForegroundColor White
+Write-Host " $baseUrl" -ForegroundColor Yellow
+Write-Host ""
+Write-Host " 健康检查命令(在 Windows PowerShell 中):" -ForegroundColor White
+Write-Host " curl.exe -s -m 5 $($baseUrl)health" -ForegroundColor Gray
+Write-Host ""
+Write-Host " 查看日志:.\scripts\wsl2-logs.ps1" -ForegroundColor Gray
+Write-Host " 停止服务:.\scripts\wsl2-stop.ps1" -ForegroundColor Gray
diff --git a/scripts/wsl2-logs.ps1 b/scripts/wsl2-logs.ps1
new file mode 100644
index 0000000..835ca2c
--- /dev/null
+++ b/scripts/wsl2-logs.ps1
@@ -0,0 +1,52 @@
+<#
+.SYNOPSIS
+ 查看 WSL2 中 CSSwitch 代理容器的实时日志。
+.PARAMETER WslDistro
+ WSL2 发行版名称(默认:自动选择运行中的发行版)。
+.PARAMETER ProjectPath
+ 项目在 WSL2 中的绝对路径(默认:自动映射当前目录)。
+.PARAMETER Lines
+ 显示最近 N 行日志(默认:显示所有历史并 follow)。
+#>
+
+param(
+ [string]$WslDistro = "",
+ [string]$ProjectPath = "",
+ [int]$Lines = 0
+)
+
+$ErrorActionPreference = "Stop"
+
+# ── 检测 WSL ──
+$wslAvailable = Get-Command "wsl.exe" -ErrorAction SilentlyContinue
+if (-not $wslAvailable) {
+ Write-Host "✗ 未找到 wsl.exe。" -ForegroundColor Red
+ exit 1
+}
+
+$distros = wsl -l -v --quiet 2>$null | Where-Object { $_ -match 'Running' }
+if (-not $distros) {
+ Write-Host "✗ 未找到运行中的 WSL2 发行版。" -ForegroundColor Red
+ exit 1
+}
+
+if (-not $WslDistro) {
+ $WslDistro = ($distros[0] -split '\s+')[0]
+}
+
+if (-not $ProjectPath) {
+ $hostPath = Get-Location
+ if ($hostPath.Path -match '^([A-Z]):\\(.*)') {
+ $driveLetter = $matches[1].ToLower()
+ $restPath = $matches[2] -replace '\\', '/'
+ $ProjectPath = "/mnt/$driveLetter/$restPath"
+ } else {
+ Write-Host "✗ 无法自动映射路径:$hostPath" -ForegroundColor Red
+ exit 1
+ }
+}
+
+$tailFlag = if ($Lines -gt 0) { "--tail=$Lines" } else { "" }
+
+Write-Host "查看 WSL2($WslDistro)CSSwitch 代理日志(按 Ctrl+C 退出)…" -ForegroundColor Cyan
+wsl -d $WslDistro --cd $ProjectPath bash -c "docker compose -f docker/docker-compose.yml logs -f $tailFlag csswitch-proxy"
diff --git a/scripts/wsl2-stop.ps1 b/scripts/wsl2-stop.ps1
new file mode 100644
index 0000000..23fda35
--- /dev/null
+++ b/scripts/wsl2-stop.ps1
@@ -0,0 +1,55 @@
+<#
+.SYNOPSIS
+ 停止 WSL2 中的 CSSwitch 代理容器。
+.PARAMETER WslDistro
+ WSL2 发行版名称(默认:自动选择运行中的发行版)。
+.PARAMETER ProjectPath
+ 项目在 WSL2 中的绝对路径(默认:自动映射当前目录)。
+#>
+
+param(
+ [string]$WslDistro = "",
+ [string]$ProjectPath = ""
+)
+
+$ErrorActionPreference = "Stop"
+
+# ── 检测 WSL ──
+$wslAvailable = Get-Command "wsl.exe" -ErrorAction SilentlyContinue
+if (-not $wslAvailable) {
+ Write-Host "✗ 未找到 wsl.exe。" -ForegroundColor Red
+ exit 1
+}
+
+$distros = wsl -l -v --quiet 2>$null | Where-Object { $_ -match 'Running' }
+if (-not $distros) {
+ Write-Host "✗ 未找到运行中的 WSL2 发行版。" -ForegroundColor Red
+ exit 1
+}
+
+if (-not $WslDistro) {
+ $WslDistro = ($distros[0] -split '\s+')[0]
+}
+
+# ── 映射路径 ──
+if (-not $ProjectPath) {
+ $hostPath = Get-Location
+ if ($hostPath.Path -match '^([A-Z]):\\(.*)') {
+ $driveLetter = $matches[1].ToLower()
+ $restPath = $matches[2] -replace '\\', '/'
+ $ProjectPath = "/mnt/$driveLetter/$restPath"
+ } else {
+ Write-Host "✗ 无法自动映射路径:$hostPath" -ForegroundColor Red
+ exit 1
+ }
+}
+
+Write-Host "停止 WSL2($WslDistro)中的 CSSwitch 代理容器…" -ForegroundColor Yellow
+$result = wsl -d $WslDistro --cd $ProjectPath bash -c 'docker compose -f docker/docker-compose.yml down 2>&1'
+if ($LASTEXITCODE -ne 0) {
+ Write-Host "✗ 停止失败:" -ForegroundColor Red
+ Write-Host $result -ForegroundColor Red
+ exit 1
+}
+Write-Host $result
+Write-Host "✓ 容器已停止" -ForegroundColor Green
From c962fcfe075600fcb24496827593d89aecee2638 Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:48:19 +0800
Subject: [PATCH 6/9] feat(docs): add WSL2 container deployment guide and
update README
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
新增 docs/wsl2-container-deployment.md:前置条件、快速开始
(PowerShell 与 WSL2 内)、配置、网络说明、可选 Caddy TLS、
故障排查、安全提示、限制。
README.md 增加 WSL2/Docker 徽章与入口链接。
Co-Authored-By: Claude
---
README.md | 3 +
docs/wsl2-container-deployment.md | 294 ++++++++++++++++++++++++++++++
2 files changed, 297 insertions(+)
create mode 100644 docs/wsl2-container-deployment.md
diff --git a/README.md b/README.md
index e81ff75..437f857 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@
+
# CSSwitch
@@ -56,6 +57,8 @@ DeepSeek 原生 Anthropic 端点 / 通义千问等 OpenAI 兼容端点
开发者的命令行用法(手动起代理与沙箱)、构建与测试,见 [`docs/DEVELOPMENT.md`](./docs/DEVELOPMENT.md) 与 [`desktop/README.md`](./desktop/README.md)。
+**Windows / WSL2 用户**:可将 CSSwitch 翻译代理以 Docker 容器方式部署在 WSL2 中,供 Windows 上的 OpenAI/Anthropic 兼容客户端使用。见 [`docs/wsl2-container-deployment.md`](./docs/wsl2-container-deployment.md)(实验性)。
+
## 更新计划(Roadmap)
以下为规划方向,不代表时间承诺。欢迎以 issue / PR 参与共建。
diff --git a/docs/wsl2-container-deployment.md b/docs/wsl2-container-deployment.md
new file mode 100644
index 0000000..3e998c1
--- /dev/null
+++ b/docs/wsl2-container-deployment.md
@@ -0,0 +1,294 @@
+# CSSwitch WSL2 容器化部署指南
+
+> **实验性支持**:本方案仅容器化 CSSwitch 翻译代理(`csswitch_proxy.py`),
+> 在 WSL2 中以 Docker 容器形式运行代理服务。
+> Claude Science 本身仍是 macOS 应用,不在此部署范围内。
+> 容器化代理适合 Windows 上的 **OpenAI/Anthropic 兼容客户端** 使用,
+> 或作为局域网内的代理服务。
+
+## 目录
+
+- [前置条件](#前置条件)
+- [快速开始(PowerShell)](#快速开始powershell)
+- [快速开始(WSL2 内)](#快速开始wsl2-内)
+- [配置说明](#配置说明)
+- [网络说明](#网络说明)
+- [可选:Caddy TLS 反代](#可选caddy-tls-反代)
+- [手动构建镜像](#手动构建镜像)
+- [故障排查](#故障排查)
+- [安全提示](#安全提示)
+- [限制](#限制)
+
+## 前置条件
+
+- **Windows 10/11** 已安装 **WSL2** 和 **Docker**(以下任选其一):
+ - [Docker Desktop for Windows](https://docs.docker.com/desktop/wsl/)(推荐,集成 WSL2 后端)
+ - 或 WSL2 发行版内安装 [Docker Engine](https://docs.docker.com/engine/install/ubuntu/)
+- WSL2 发行版(推荐 Ubuntu 22.04+)
+- 一个有效的 **第三方 API key**([DeepSeek](https://platform.deepseek.com/api_keys) 或 [阿里通义千问(DashScope)](https://bailian.console.aliyun.com/))
+- PowerShell 5.1+(Windows 自带)或 PowerShell 7+
+
+## 快速开始(PowerShell)
+
+从 Windows 一侧一键部署,适合不常进 WSL2 终端的用户。
+
+### 步骤 1:克隆项目到 Windows
+
+```powershell
+git clone https://github.com/SuperJJ007/CSswitch.git
+cd CSswitch
+```
+
+### 步骤 2:配置环境变量
+
+从模板复制 `.env` 文件:
+
+```powershell
+# 在 PowerShell 中:
+wsl -d <你的发行版> --cd /mnt/g/WorkSpace/CSswitch cp docker/.env.example docker/.env
+```
+
+或用 WSL2 终端编辑:
+
+```bash
+cd /mnt/g/WorkSpace/CSswitch # 替换为你实际的挂载路径
+cp docker/.env.example docker/.env
+vi docker/.env
+```
+
+填入你的 key:
+
+```ini
+# 选 deepseek 时填写
+DEEPSEEK_API_KEY=sk-your-deepseek-key-here
+
+# 强烈建议设置随机 path secret
+CSSWITCH_AUTH_TOKEN=your-random-secret-here
+```
+
+### 步骤 3:一键部署
+
+```powershell
+.\scripts\wsl2-deploy.ps1
+```
+
+脚本会自动:
+1. 检测 WSL2 运行中的发行版
+2. 在 WSL2 内执行 `docker compose up -d`
+3. 探测 WSL2 IP 地址
+4. 输出供 Windows 客户端使用的 `ANTHROPIC_BASE_URL`
+
+### 步骤 4:验证代理
+
+```powershell
+# 用 .\scripts\wsl2-deploy.ps1 输出的地址测试健康检查
+curl.exe -s -m 5 http://:18991//health
+# 预期:{"status":"ok","provider":"deepseek",...}
+```
+
+### 步骤 5:停止服务
+
+```powershell
+.\scripts\wsl2-stop.ps1
+```
+
+## 快速开始(WSL2 内)
+
+### 步骤 1:进入项目目录
+
+```bash
+cd /path/to/CSswitch
+```
+
+### 步骤 2:配置环境变量
+
+```bash
+cp docker/.env.example docker/.env
+vi docker/.env
+```
+
+### 步骤 3:构建并启动
+
+```bash
+# 构建镜像
+bash scripts/docker-build.sh
+
+# 启动容器
+bash scripts/wsl2-deploy.sh
+```
+
+### 步骤 4:验证
+
+```bash
+# 健康检查
+curl http://localhost:18991//health
+
+# 查看日志
+bash scripts/wsl2-logs.sh
+```
+
+### 步骤 5:停止
+
+```bash
+bash scripts/wsl2-stop.sh
+```
+
+## 配置说明
+
+全部配置通过 `docker/.env` 文件设定(参见 `docker/.env.example` 模板):
+
+| 变量 | 说明 | 默认值 |
+|------|------|--------|
+| `DEEPSEEK_API_KEY` | DeepSeek API Key(`deepseek` provider 时必填) | — |
+| `DASHSCOPE_API_KEY` | 阿里 DashScope API Key(`qwen` provider 时必填) | — |
+| `CSSWITCH_PROVIDER` | 模型提供商:`deepseek` 或 `qwen` | `deepseek` |
+| `CSSWITCH_PROXY_PORT` | 代理监听端口 | `18991` |
+| `CSSWITCH_AUTH_TOKEN` | 路径 secret 鉴权(**强烈建议设置**) | — |
+| `CSSWITCH_UPSTREAM_URL` | 上游 API 地址覆盖(高级用法) | — |
+
+> **安全**:`DEEPSEEK_API_KEY` 与 `DASHSCOPE_API_KEY` 只通过 `.env` → 环境变量注入容器,
+> 不会出现在镜像层、git 历史或命令行参数中。
+
+## 网络说明
+
+### WSL2 IP 地址
+
+WSL2 使用虚拟化网络,其 IP 地址可能随重启变化。`wsl2-deploy.ps1` 会自动探测当前 IP 并输出。
+
+```powershell
+# 手动获取 WSL2 IP:
+wsl hostname -I
+```
+
+### Docker Desktop 模式
+
+使用 Docker Desktop with WSL2 backend 时,容器可通过 `localhost` 直接访问(Docker Desktop 自动端口转发):
+
+```powershell
+curl.exe http://localhost:18991//health
+```
+
+### 传统 WSL2 模式
+
+在 WSL2 发行版内直接安装 Docker Engine 时,需要 **通过 WSL2 IP 访问**:
+
+```powershell
+curl.exe http://:18991//health
+```
+
+### Windows 防火墙
+
+如果从局域网其他机器访问,需要在 Windows 防火墙中放行 WSL2 代理端口:
+
+```powershell
+# 以管理员身份运行
+New-NetFirewallRule -DisplayName "CSSwitch Proxy" -Direction Inbound -LocalPort 18991 -Protocol TCP -Action Allow
+```
+
+### 客户端配置
+
+Windows 上的 OpenAI/Anthropic 兼容客户端需设置:
+
+```ini
+ANTHROPIC_BASE_URL=http://:18991//
+# 或(使用 Docker Desktop 时)
+ANTHROPIC_BASE_URL=http://localhost:18991//
+```
+
+## 可选:Caddy TLS 反代
+
+容器编排支持可选 Caddy 服务,在 WSL2 内部提供 **自签名 HTTPS**,保护 path secret 不在明文 HTTP 上传输。
+
+启用方式:
+
+```bash
+# 构建并启动(包含 Caddy)
+docker compose -f docker/docker-compose.yml --profile tls up -d
+```
+
+访问地址变为:
+
+```
+https://:8443//health
+```
+
+> **注意**:自签名证书首次访问时浏览器会提示安全风险,确认即可。
+> `curl` 需加 `-k` 或 `--insecure` 参数。
+
+## 构建镜像
+
+```bash
+bash scripts/docker-build.sh
+```
+
+或手动:
+
+```bash
+docker build -t csswitch-proxy:latest -f docker/Dockerfile .
+```
+
+也可通过 Compose 自动构建(首次 `up -d` 时自动完成):
+
+```bash
+docker compose -f docker/docker-compose.yml up -d
+```
+
+## 故障排查
+
+### 代理启动后立即退出
+
+最常见原因——**缺少 API key**:
+
+```bash
+# 检查 .env 是否正确
+cat docker/.env | grep -E '^(DEEPSEEK|DASHSCOPE)_API_KEY='
+
+# 查看容器日志
+bash scripts/wsl2-logs.sh
+```
+
+### 端口已被占用
+
+```bash
+# 修改 docker/.env 中的端口
+CSSWITCH_PROXY_PORT=18992
+
+# 重写 docker-compose.yml 映射(或在 docker-compose.yml 中改 ports)
+```
+
+### WSL2 IP 变化
+
+重新运行 `bash scripts/wsl2-deploy.sh` 或 `.\scripts\wsl2-deploy.ps1` 获取新的 IP 地址。
+
+### curl 连接被拒绝
+
+1. 确认容器在运行:`docker ps | grep csswitch-proxy`
+2. 确认端口映射正确:`docker port csswitch-proxy`
+3. 确认 Windows 防火墙未拦截:临时关闭防火墙测试
+4. 在 WSL2 内测试回环地址:`curl http://127.0.0.1:18991//health`
+
+### Docker Desktop 无法启动 WSL2 后端
+
+- 确认 WSL2 已安装:`wsl --set-default-version 2`
+- 确认发行版版本为 2:`wsl -l -v`
+- 升级:`wsl --set-version <发行版名> 2`
+- 在 Docker Desktop Settings → Resources → WSL Integration 中启用集成
+
+## 安全提示
+
+- **path secret 保护**:容器到客户端的 HTTP 连接在本地 WSL2 桥上为明文。
+ 在不可信网络(如公司内网、跨子网)上使用时,强烈建议启用 [Caddy TLS](#可选caddy-tls-反代)。
+- **API key 保护**:`docker/.env` 包含你的第三方 API key,请勿提交到 git。
+ `.env` 已在 `.gitignore` 中(被 `.dockerignore` 排除),但仍需注意不要手动 `git add` 它。
+- **容器权限**:容器内以非 root 用户(`csswitch`)运行,最小权限原则。
+- **监听地址**:容器内默认监听 `0.0.0.0`(由 Dockerfile 的 `ENTRYPOINT` 指定 `--host 0.0.0.0`);
+ 在本机使用 `127.0.0.1:18991`(通过 Docker Desktop 的端口转发),局域网可用 WSL2 IP 访问。
+- **端口 8765 禁止**:脚本与 compose 配置均拒绝使用 8765 端口(真实 Claude Science 保留端口)。
+
+## 限制
+
+- **Claude Science 不在此范围内**:容器化的是翻译代理,Claude Science 仍需运行在 macOS 上。
+- **Windows 上无 Claude Science**:目前 Anthropic 未提供 Windows 版 Claude Science,
+ 因此本容器方案面向的是其他 OpenAI/Anthropic 兼容客户端的用户。
+- **实验性**:WSL2 容器化部署为实验性功能,未经过生产环境的充分验证。
+- **网络依赖**:WSL2 网络架构(NAT)可能在某些 VPN/代理环境下需要额外配置。
From c90dbb5511169ddb1625fd21d715414ace6e67b7 Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:48:31 +0800
Subject: [PATCH 7/9] feat(doctor): add Docker / WSL2 detection to diagnostics
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
新增 [容器 / WSL2] 诊断段:检测 docker CLI 与 compose 插件、
检测 WSL2 环境(/proc/version 或 WSL_DISTRO_NAME)、
只读检查 csswitch-proxy:latest 镜像是否存在。
Co-Authored-By: Claude
---
scripts/doctor.sh | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/scripts/doctor.sh b/scripts/doctor.sh
index 23bb875..76f6ffe 100755
--- a/scripts/doctor.sh
+++ b/scripts/doctor.sh
@@ -67,6 +67,32 @@ else
warn "config 不存在(首次运行 GUI 会创建):$CONFIG"
fi
+echo "[容器 / WSL2]"
+# Docker CLI
+if command -v docker >/dev/null 2>&1; then
+ pass "docker $(docker --version 2>&1 | awk '{print $3}' | sed 's/,//')"
+ # docker compose 插件
+ if docker compose version >/dev/null 2>&1; then
+ pass "docker compose 插件可用"
+ else
+ warn "未检测到 docker compose 插件(容器编排需要)"
+ fi
+else
+ warn "未找到 docker(容器化部署需要)"
+fi
+# WSL2 检测(/proc/version 含 Microsoft 或 WSL_DISTRO_NAME 环境变量)
+if [ -f /proc/version ] && grep -qi microsoft /proc/version; then
+ pass "WSL2 环境(/proc/version 含 Microsoft)"
+elif [ -n "${WSL_DISTRO_NAME:-}" ]; then
+ pass "WSL2 环境(WSL_DISTRO_NAME=$WSL_DISTRO_NAME)"
+fi
+# csswitch-proxy 镜像检测(只读,不构建)
+if docker image inspect csswitch-proxy:latest >/dev/null 2>&1; then
+ pass "csswitch-proxy:latest 镜像存在"
+else
+ warn "csswitch-proxy:latest 镜像不存在(执行 bash scripts/docker-build.sh 构建)"
+fi
+
echo "[铁律]"
if [ -d "$REAL_DIR" ]; then pass "真实目录存在(本工具只读诊断,绝不写/删):$REAL_DIR"; else warn "未见真实 Science 目录:$REAL_DIR"; fi
From b277e6d5aa876c9dac9fa7c4d7f8b4e8d7ae3e7d Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:50:14 +0800
Subject: [PATCH 8/9] test(container): add optional container smoke test and
update runner
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
新增 test/test_container.sh(RUN_CONTAINER_TESTS=1 时启用):
构建镜像、创建临时 .env、启动容器、健康检查、/v1/models 验证、清理。
test/run_all.sh 新增 container test 调用。
Co-Authored-By: Claude
---
test/run_all.sh | 2 +
test/test_container.sh | 96 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 98 insertions(+)
create mode 100644 test/test_container.sh
diff --git a/test/run_all.sh b/test/run_all.sh
index a396c5f..bafa6c1 100644
--- a/test/run_all.sh
+++ b/test/run_all.sh
@@ -10,4 +10,6 @@ echo "== bash scripts =="
bash test/test_scripts.sh
echo "== bash ops scripts (doctor/verify-proxy/self-test) =="
bash test/test_ops_scripts.sh
+echo "== container smoke test (RUN_CONTAINER_TESTS=1) =="
+bash test/test_container.sh
echo "ALL GREEN"
diff --git a/test/test_container.sh b/test/test_container.sh
new file mode 100644
index 0000000..efd356d
--- /dev/null
+++ b/test/test_container.sh
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+# CSSwitch 容器冒烟测试。
+# 受 RUN_CONTAINER_TESTS=1 环境变量控制,默认跳过。
+# 依赖:docker 与 docker compose。
+set -euo pipefail
+
+if [ "${RUN_CONTAINER_TESTS:-0}" != "1" ]; then
+ echo " 跳过容器测试(RUN_CONTAINER_TESTS=1 时启用)"
+ exit 0
+fi
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+
+# ── 依赖检查 ──
+if ! command -v docker >/dev/null 2>&1; then
+ echo " SKIP: 无 docker 命令"
+ exit 0
+fi
+if ! docker compose version >/dev/null 2>&1; then
+ echo " SKIP: 无 docker compose 插件"
+ exit 0
+fi
+
+PASS=0; FAIL=0
+pass() { echo " ✓ $1"; PASS=$((PASS + 1)); }
+fail() { echo " ✗ $1"; FAIL=$((FAIL + 1)); }
+
+# ── 清理函数 ──
+cleanup() {
+ echo " 清理容器…"
+ docker compose -f docker/docker-compose.yml down 2>/dev/null || true
+}
+trap cleanup EXIT
+
+echo "== container smoke test =="
+
+# ── 1. 构建镜像 ──
+echo " [构建]"
+if docker build -t csswitch-proxy:test -f docker/Dockerfile . >/dev/null 2>&1; then
+ pass "镜像构建成功"
+else
+ fail "镜像构建失败"
+ echo " FAIL: 构建失败,跳过后续测试"
+ exit 1
+fi
+
+# ── 2. 准备临时 .env ──
+echo " [配置]"
+cp docker/.env.example docker/.env
+# 写入 dummy key(不会真正调用上游,只测代理启动与健康检查)
+cat >> docker/.env <<'EOF'
+DEEPSEEK_API_KEY=sk-test-dummy-key
+CSSWITCH_AUTH_TOKEN=test-secret-123
+CSSWITCH_PROXY_PORT=18991
+EOF
+pass "临时 .env 就绪(dummy key)"
+
+# ── 3. 启动容器 ──
+echo " [启动]"
+if docker compose -f docker/docker-compose.yml up -d 2>/dev/null; then
+ pass "容器启动成功"
+else
+ fail "容器启动失败"
+ exit 1
+fi
+
+# ── 4. 等待健康检查 ──
+echo " [健康检查]"
+HEALTH_OK=0
+for i in $(seq 1 10); do
+ if curl -s -m 3 http://127.0.0.1:18991/test-secret-123/health >/dev/null 2>&1; then
+ HEALTH_OK=1
+ break
+ fi
+ sleep 2
+done
+if [ "$HEALTH_OK" = "1" ]; then
+ pass "健康检查通过(/health 200)"
+else
+ fail "健康检查超时"
+ docker compose -f docker/docker-compose.yml logs --tail=20 csswitch-proxy 2>/dev/null || true
+fi
+
+# ── 5. 验证 /v1/models ──
+if curl -s -m 5 http://127.0.0.1:18991/test-secret-123/v1/models | grep -q '"data"' 2>/dev/null; then
+ pass "/v1/models 返回模型列表"
+else
+ fail "/v1/models 未返回预期数据"
+fi
+
+# ── 汇总 ──
+echo "----"
+echo "容器测试:通过 ${PASS},失败 ${FAIL}"
+[ "$FAIL" -eq 0 ] || exit 1
+echo "ALL CONTAINER TESTS GREEN"
From 6af2a4166dd6e8b822f03398bfc710da0510fa36 Mon Sep 17 00:00:00 2001
From: visail
Date: Fri, 3 Jul 2026 10:50:51 +0800
Subject: [PATCH 9/9] docs(changelog): add WSL2 containerized deployment to
Unreleased
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
记录本次 feature/wsl2-containerized-deployment 分支的全部变更:
proxy --host 参数、Dockerfile/compose、WSL2 脚本与 PowerShell 包装、
部署文档、doctor 诊断增强、容器冒烟测试。
Co-Authored-By: Claude
---
CHANGELOG.md | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f35eb27..df5933a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,21 @@
> **约定**:已修问题从 [`docs/known-issues.md`](docs/known-issues.md)「毕业」到这里(发布即定稿);未修/进行中留在 known-issues;硬 bug 的根因证据链存在 [`findings/`](findings/)。
+## Unreleased — WSL2 容器化部署
+
+### 新增 Added
+
+- **WSL2 容器化代理部署**(实验性):将 CSSwitch 翻译代理以 Docker 容器方式部署在 WSL2 中,供 Windows 上的 OpenAI/Anthropic 兼容客户端使用。
+ - `proxy/csswitch_proxy.py` 新增 `--host` / `CSSWITCH_BIND_HOST` 参数,支持绑定 `0.0.0.0` 监听任意地址(默认 `127.0.0.1`)。
+ - `docker/Dockerfile`:基于 `python:3.11-slim`,非 root 运行,零 pip 依赖。
+ - `docker/docker-compose.yml`:proxy 服务编排(env_file、端口映射、健康检查、自动重启、日志卷),可选 Caddy TLS 反代(`--profile tls`)。
+ - `scripts/docker-build.sh` / `scripts/wsl2-deploy.sh` / `scripts/wsl2-stop.sh` / `scripts/wsl2-logs.sh`:WSL2/Linux 容器管理脚本。
+ - `scripts/wsl2-deploy.ps1` / `scripts/wsl2-stop.ps1` / `scripts/wsl2-logs.ps1`:Windows PowerShell 包装,自动探测 WSL2 IP 并输出连接地址。
+ - `docs/wsl2-container-deployment.md`:完整部署指南(前置条件、网络说明、安全提示、故障排查)。
+ - `scripts/doctor.sh` 新增 Docker / docker compose / WSL2 环境检测。
+ - `test/test_container.sh`:可选容器冒烟测试(`RUN_CONTAINER_TESTS=1`)。
+- README 增加 WSL2/Docker 徽章与入口链接。
+
## [0.2.0] — 2026-07-03
> 主题:一键**幂等化**——修 #3(运行中再点的分派)与 #6 的「复发」部分(更新后对话「不见」),并随三轮外部复审全面加固代理与进程路径。