# jianda-proxy CLI 工具打包设计 **日期**:2026-05-28 **状态**:已批准 ## 背景 jianda-proxy 是一个 HTTP 代理工具,用于解决腾讯云 ICP 拦截导致 Git LFS 下载失败的问题。目前是一个单文件 Python 脚本(`lfs-proxy.py`),配置硬编码,直接 `python3` 运行。 目标:将其打包为标准的 Python CLI 工具,支持 `uvx` 运行、`uv tool install` 安装、后台运行、CLI 管理生命周期,且跨平台(Windows + Mac)。 ## 决策 - **工具名称**:`jianda-proxy` - **配置格式**:INI(标准库 `configparser`,零外部依赖) - **后台运行**:纯标准库,Unix 用 `os.fork()`,Windows 用 `subprocess` detached - **配置管理**:CLI `config` 子命令 ## 项目结构 ``` jianda-proxy/ ├── pyproject.toml ├── src/ │ └── jianda_proxy/ │ ├── __init__.py │ ├── cli.py # CLI 入口,argparse 子命令 │ ├── proxy.py # 代理逻辑 │ ├── config.py # INI 配置管理 │ └── daemon.py # 跨平台后台运行管理 ├── docs/ │ └── superpowers/specs/ └── README.md ``` ### pyproject.toml - 零外部依赖 - Python >= 3.8 - 入口点:`jianda-proxy = "jianda_proxy.cli:main"` - 构建后端:`hatchling` ## 打包与分发 - `uv tool install .`:本地安装 - `uvx jianda-proxy`:临时运行(需发布到 PyPI 后) - `python -m jianda_proxy`:作为模块运行 ## 配置管理 ### 配置文件位置 - Mac/Linux:`~/.config/jianda-proxy/config.ini` - Windows:`%APPDATA%\jianda-proxy\config.ini` 通过 `os.path.expanduser` 和 `os.environ` 手动拼路径,不引入 `platformdirs`。 ### 配置格式 ```ini [proxy] remote_domain = git.zz.com upstream_host = 62.234.191.215 upstream_port = 3000 listen_port = 13000 listen_host = 127.0.0.1 ``` ### config 子命令 | 命令 | 说明 | |------|------| | `config set ` | 设置配置项 | | `config get ` | 查看单个配置项 | | `config list` | 列出所有配置 | | `config path` | 显示配置文件路径 | 首次使用 `start` 时,如果配置文件不存在,用默认值自动创建。 ## CLI 命令 ``` jianda-proxy start [--port PORT] [--foreground] jianda-proxy stop jianda-proxy status jianda-proxy config set jianda-proxy config get jianda-proxy config list jianda-proxy config path ``` `start --foreground`:前台运行,日志直接输出到终端,方便调试。 ## 后台运行与生命周期 ### start 1. 检查端口是否可用 2. 检查是否已有进程运行(PID 文件存在且进程存活) 3. 如果已有进程:提示错误并退出 4. 如果 PID 文件存在但进程已死:清理后继续 5. Unix:`os.fork()` 后父进程退出 6. Windows:`subprocess.Popen` with `CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS` 7. 写 PID 文件 8. 日志写到 `~/.config/jianda-proxy/proxy.log`(RotatingFileHandler,5MB,3 个备份) ### stop 1. 读取 PID 文件 2. 检查进程是否存在 3. 发送 SIGTERM(Unix)/ taskkill(Windows) 4. 等待最多 3 秒 5. 如果还活着:SIGKILL / 强制终止 6. 删除 PID 文件 ### status 1. 读取 PID 文件 2. 检查进程是否存在 3. 显示:运行状态、PID、端口、运行时间 ### PID 文件 位置:和配置文件同目录 `~/.config/jianda-proxy/daemon.pid`。 ## 跨平台细节 | 操作 | Unix | Windows | |------|------|---------| | 后台启动 | `os.fork()` | `subprocess.Popen` + detached flags | | 进程检查 | `os.kill(pid, 0)` | `ctypes` 调用 `kernel32.OpenProcess` + `GetExitCodeProcess` | | 优雅停止 | `SIGTERM` | `taskkill /PID` | | 强制停止 | `SIGKILL` | `taskkill /F /PID` | | 配置目录 | `~/.config/` | `%APPDATA%` | ## 错误处理 - 端口被占用:提示具体端口和可能原因 - 配置无效(IP 格式错误等):启动前验证 - 权限不足:提示需要的权限 - 后台进程异常退出:`status` 检测到后提示查看日志 ## 模块职责 - **cli.py**:argparse 定义、子命令分发、入口函数 - **config.py**:读写配置文件、默认值管理、配置路径计算 - **proxy.py**:HTTP 代理服务器逻辑(从现有 `lfs-proxy.py` 提取) - **daemon.py**:PID 文件管理、跨平台进程检查、进程终止