cc-switch-ui 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ *.egg-info/
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: cc-switch-ui
3
+ Version: 0.1.0
4
+ Summary: CC Switch Web UI —— Claude Code 多供应商 / 多账号管理面板
5
+ Project-URL: Homepage, https://github.com/cliecy/cc-switch-ui
6
+ Project-URL: Issues, https://github.com/cliecy/cc-switch-ui/issues
7
+ Author: cliecy
8
+ License: MIT
9
+ Keywords: anthropic,claude,claude-code,deepseek,glm,kimi,management,openrouter,qwen,web-ui
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
20
+ Classifier: Topic :: Software Development :: User Interfaces
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.12
23
+ Requires-Dist: flask>=3.1
24
+ Description-Content-Type: text/markdown
25
+
26
+ # CC Switch · Web UI 管理面板
27
+
28
+ 一个用来管理 **Claude Code** 多供应商 / 多账号配置的本地 Web 面板。
29
+ 后端 Flask 提供 REST API + SSE,前端是单个 `index.html`(内嵌 CSS/JS)。
30
+
31
+ ## 功能
32
+
33
+ - 查看 / 切换当前供应商:Claude 官方、DeepSeek、Kimi、GLM、Qwen、OpenRouter,以及一个空白的 **自定义** 供应商
34
+ - **自定义端点**:点供应商卡片上的 ⚙ 可改任意供应商的 Base URL / 模型 ID / 鉴权环境变量,接入任意 Anthropic 兼容端点(中转、自建代理等)
35
+ - 管理 API Key:增 / 删 / 改,本地存储于 `~/.ccm_config`
36
+ - 每个供应商支持多账号,一键切换激活账号
37
+ - 启动 / 重启 / 停止 `claude` 进程(通过 pty 运行)
38
+ - **保活看门狗**:勾选「保活」后,claude 进程意外退出会自动重启(带快速失败熔断),界面显示重启次数 / 退出码
39
+ - **内嵌 xterm.js 真终端**:完整渲染 claude 的交互式 TUI(边框 / 颜色 / 方向键 / 回车),可直接在网页里打字操作
40
+ - **工作目录可选**:启动前用目录选择器(「浏览…」)或手填路径指定 claude 的运行目录
41
+ - **恢复聊天记录**:启动时可选「新会话 / 继续上次(`--continue`) / 恢复历史(`--resume`)」
42
+ - 深色主题,简洁现代
43
+
44
+ > 终端用 xterm.js(从 `cdn.jsdelivr.net` 加载)。离线环境会自动降级——供应商/账号管理照常可用,仅终端不可用。
45
+
46
+ ## 运行环境
47
+
48
+ - Python 3.12(用 [uv](https://github.com/astral-sh/uv) 管理依赖)
49
+ - 已安装 `claude`(Claude Code CLI)
50
+ - `ccm` 命令为可选:存在时切换会额外调用 `ccm use <provider>`,不存在则直接写配置 + 注入环境变量
51
+
52
+ ## 启动
53
+
54
+ 依赖已写进 `pyproject.toml` / `uv.lock`,`uv run` 会自动创建虚拟环境并装好依赖,无需手动 install:
55
+
56
+ ```bash
57
+ cd ~/cc-switch-ui
58
+
59
+ # 直接启动(首次会自动同步依赖,默认 127.0.0.1:8765)
60
+ uv run app.py
61
+
62
+ # 或指定地址端口:
63
+ uv run app.py --host 127.0.0.1 --port 8765
64
+ ```
65
+
66
+ 然后浏览器打开 **http://127.0.0.1:8765** 即可(`index.html` 由后端同源托管,无需单独打开文件,避免跨域问题)。
67
+
68
+ > 想显式同步环境:`uv sync`。也可以用 venv 解释器直接跑:`.venv/bin/python app.py`
69
+
70
+ ### 项目文件
71
+
72
+ | 文件 | 作用 |
73
+ |------|------|
74
+ | `app.py` | Flask 后端 |
75
+ | `index.html` | 单文件前端 |
76
+ | `pyproject.toml` | 项目元数据 + 依赖声明(flask) |
77
+ | `uv.lock` | 锁定的依赖版本,保证可复现 |
78
+ | `.python-version` | 指定 Python 3.12 |
79
+ | `.gitignore` | 忽略 `.venv/` 等 |
80
+ | `run.sh` | 守护脚本(常驻 / 崩溃自愈,无需 root) |
81
+
82
+ ## 长期挂着 / 常驻部署
83
+
84
+ 直接 `uv run app.py` 是**前台**运行——SSH 一断、终端一关,进程收 SIGHUP 就没了。要让它「一直挂着」,有两层保活:
85
+
86
+ - **进程级**:界面里的「保活」开关 —— claude 崩了由后端看门狗自动拉起。
87
+ - **服务级**:让 `app.py` 本身常驻、崩溃自愈 —— 用下面的 `run.sh`(app 退出时会自动清理它的 claude 子进程,不留「失联孤儿」)。
88
+
89
+ ### 方式 A:`run.sh` 守护脚本(推荐,无需 root、不依赖 systemd)
90
+
91
+ 用 `setsid` 脱离终端会话(SSH 断开也不死)+ 内部循环实现崩溃自动重启:
92
+
93
+ ```bash
94
+ ./run.sh start # 后台启动,脱离终端
95
+ ./run.sh status # 查看状态
96
+ ./run.sh log # 跟踪日志
97
+ ./run.sh stop # 停止(连同 claude 一起清理)
98
+ ./run.sh restart
99
+
100
+ # 自定义地址端口:
101
+ CC_PORT=9000 CC_HOST=0.0.0.0 ./run.sh start
102
+ ```
103
+
104
+ 开机自启(仍然无需 root,用 crontab):
105
+
106
+ ```bash
107
+ crontab -e
108
+ # 加一行:
109
+ @reboot cd ~/cc-switch-ui && ./run.sh start
110
+ ```
111
+
112
+ ### 方式 B:systemd 用户服务(如果你的服务器有 systemd)
113
+
114
+ > `systemctl --user` 是**用户级**的,**不需要 root / sudo**。但前提是机器装了 systemd(部分容器 / 共享主机没有),且开机自启需要 `loginctl enable-linger`(个别受限环境可能被禁用)。用不了就走方式 A。
115
+
116
+ `~/.config/systemd/user/cc-switch.service`:
117
+
118
+ ```ini
119
+ [Unit]
120
+ Description=CC Switch Web UI
121
+ After=network.target
122
+
123
+ [Service]
124
+ WorkingDirectory=%h/cc-switch-ui
125
+ ExecStart=%h/.local/bin/uv run app.py --host 127.0.0.1 --port 8765
126
+ Restart=always
127
+ RestartSec=3
128
+
129
+ [Install]
130
+ WantedBy=default.target
131
+ ```
132
+
133
+ ```bash
134
+ systemctl --user daemon-reload
135
+ systemctl --user enable --now cc-switch # 启动 + 开机自启
136
+ loginctl enable-linger "$USER" # 登出后仍保持运行(如被禁则用方式 A)
137
+ journalctl --user -u cc-switch -f # 看日志
138
+ ```
139
+
140
+ ## 配置文件
141
+
142
+ `~/.ccm_config`(JSON,权限 `0600`,由本服务读写)。结构示例:
143
+
144
+ ```json
145
+ {
146
+ "current_provider": "deepseek",
147
+ "providers": {
148
+ "deepseek": {
149
+ "label": "DeepSeek",
150
+ "base_url": "https://api.deepseek.com/anthropic",
151
+ "auth_var": "ANTHROPIC_AUTH_TOKEN",
152
+ "model": "deepseek-chat",
153
+ "accounts": [
154
+ { "id": "b68b00552679", "name": "个人", "api_key": "sk-..." }
155
+ ],
156
+ "active_account": "b68b00552679"
157
+ }
158
+ }
159
+ }
160
+ ```
161
+
162
+ 切换/启动时,后端按当前供应商 + 激活账号注入环境变量给 `claude`:
163
+ `ANTHROPIC_BASE_URL`、`ANTHROPIC_AUTH_TOKEN`(或官方的 `ANTHROPIC_API_KEY`)、`ANTHROPIC_MODEL`。
164
+ 各供应商的 `base_url` / `model` 均为内置默认值,可在配置文件中按需修改。
165
+
166
+ ## REST API 一览
167
+
168
+ | 方法 | 路径 | 说明 |
169
+ |------|------|------|
170
+ | GET | `/api/state` | 全量状态(密钥脱敏)+ 进程状态 |
171
+ | POST | `/api/provider/switch` | 切换当前供应商 `{provider}` |
172
+ | PUT | `/api/provider/<id>` | 编辑端点 `{label?, base_url?, model?, auth_var?}` |
173
+ | POST | `/api/account` | 新增账号 `{provider,name,api_key}` |
174
+ | PUT | `/api/account/<id>` | 编辑账号 `{provider,name,api_key?}` |
175
+ | DELETE | `/api/account/<id>?provider=` | 删除账号 |
176
+ | POST | `/api/account/activate` | 设为激活账号 `{provider,account_id}` |
177
+ | POST | `/api/claude/start` | 启动进程 `{args?, cwd?, rows?, cols?}` |
178
+ | POST | `/api/claude/restart` | 重启进程(复用上次启动参数) |
179
+ | POST | `/api/claude/stop` | 停止进程 |
180
+ | POST | `/api/claude/input` | 向进程发送输入:`{raw}` 原始按键流 / `{text}` 整行 |
181
+ | POST | `/api/claude/resize` | 调整 pty 窗口大小 `{rows, cols}` |
182
+ | POST | `/api/claude/keepalive` | 开关保活看门狗 `{enabled}` |
183
+ | GET | `/api/claude/status` | 进程状态 |
184
+ | GET | `/api/claude/stream` | SSE 实时输出 |
185
+ | GET | `/api/fs/list?path=` | 列出子目录(目录选择器用) |
186
+
187
+ ## 安全提示
188
+
189
+ - 仅监听 `127.0.0.1`,请勿暴露到公网(API Key 以明文存于配置文件)。
190
+ - 配置文件权限已设为 `0600`。
@@ -0,0 +1,165 @@
1
+ # CC Switch · Web UI 管理面板
2
+
3
+ 一个用来管理 **Claude Code** 多供应商 / 多账号配置的本地 Web 面板。
4
+ 后端 Flask 提供 REST API + SSE,前端是单个 `index.html`(内嵌 CSS/JS)。
5
+
6
+ ## 功能
7
+
8
+ - 查看 / 切换当前供应商:Claude 官方、DeepSeek、Kimi、GLM、Qwen、OpenRouter,以及一个空白的 **自定义** 供应商
9
+ - **自定义端点**:点供应商卡片上的 ⚙ 可改任意供应商的 Base URL / 模型 ID / 鉴权环境变量,接入任意 Anthropic 兼容端点(中转、自建代理等)
10
+ - 管理 API Key:增 / 删 / 改,本地存储于 `~/.ccm_config`
11
+ - 每个供应商支持多账号,一键切换激活账号
12
+ - 启动 / 重启 / 停止 `claude` 进程(通过 pty 运行)
13
+ - **保活看门狗**:勾选「保活」后,claude 进程意外退出会自动重启(带快速失败熔断),界面显示重启次数 / 退出码
14
+ - **内嵌 xterm.js 真终端**:完整渲染 claude 的交互式 TUI(边框 / 颜色 / 方向键 / 回车),可直接在网页里打字操作
15
+ - **工作目录可选**:启动前用目录选择器(「浏览…」)或手填路径指定 claude 的运行目录
16
+ - **恢复聊天记录**:启动时可选「新会话 / 继续上次(`--continue`) / 恢复历史(`--resume`)」
17
+ - 深色主题,简洁现代
18
+
19
+ > 终端用 xterm.js(从 `cdn.jsdelivr.net` 加载)。离线环境会自动降级——供应商/账号管理照常可用,仅终端不可用。
20
+
21
+ ## 运行环境
22
+
23
+ - Python 3.12(用 [uv](https://github.com/astral-sh/uv) 管理依赖)
24
+ - 已安装 `claude`(Claude Code CLI)
25
+ - `ccm` 命令为可选:存在时切换会额外调用 `ccm use <provider>`,不存在则直接写配置 + 注入环境变量
26
+
27
+ ## 启动
28
+
29
+ 依赖已写进 `pyproject.toml` / `uv.lock`,`uv run` 会自动创建虚拟环境并装好依赖,无需手动 install:
30
+
31
+ ```bash
32
+ cd ~/cc-switch-ui
33
+
34
+ # 直接启动(首次会自动同步依赖,默认 127.0.0.1:8765)
35
+ uv run app.py
36
+
37
+ # 或指定地址端口:
38
+ uv run app.py --host 127.0.0.1 --port 8765
39
+ ```
40
+
41
+ 然后浏览器打开 **http://127.0.0.1:8765** 即可(`index.html` 由后端同源托管,无需单独打开文件,避免跨域问题)。
42
+
43
+ > 想显式同步环境:`uv sync`。也可以用 venv 解释器直接跑:`.venv/bin/python app.py`
44
+
45
+ ### 项目文件
46
+
47
+ | 文件 | 作用 |
48
+ |------|------|
49
+ | `app.py` | Flask 后端 |
50
+ | `index.html` | 单文件前端 |
51
+ | `pyproject.toml` | 项目元数据 + 依赖声明(flask) |
52
+ | `uv.lock` | 锁定的依赖版本,保证可复现 |
53
+ | `.python-version` | 指定 Python 3.12 |
54
+ | `.gitignore` | 忽略 `.venv/` 等 |
55
+ | `run.sh` | 守护脚本(常驻 / 崩溃自愈,无需 root) |
56
+
57
+ ## 长期挂着 / 常驻部署
58
+
59
+ 直接 `uv run app.py` 是**前台**运行——SSH 一断、终端一关,进程收 SIGHUP 就没了。要让它「一直挂着」,有两层保活:
60
+
61
+ - **进程级**:界面里的「保活」开关 —— claude 崩了由后端看门狗自动拉起。
62
+ - **服务级**:让 `app.py` 本身常驻、崩溃自愈 —— 用下面的 `run.sh`(app 退出时会自动清理它的 claude 子进程,不留「失联孤儿」)。
63
+
64
+ ### 方式 A:`run.sh` 守护脚本(推荐,无需 root、不依赖 systemd)
65
+
66
+ 用 `setsid` 脱离终端会话(SSH 断开也不死)+ 内部循环实现崩溃自动重启:
67
+
68
+ ```bash
69
+ ./run.sh start # 后台启动,脱离终端
70
+ ./run.sh status # 查看状态
71
+ ./run.sh log # 跟踪日志
72
+ ./run.sh stop # 停止(连同 claude 一起清理)
73
+ ./run.sh restart
74
+
75
+ # 自定义地址端口:
76
+ CC_PORT=9000 CC_HOST=0.0.0.0 ./run.sh start
77
+ ```
78
+
79
+ 开机自启(仍然无需 root,用 crontab):
80
+
81
+ ```bash
82
+ crontab -e
83
+ # 加一行:
84
+ @reboot cd ~/cc-switch-ui && ./run.sh start
85
+ ```
86
+
87
+ ### 方式 B:systemd 用户服务(如果你的服务器有 systemd)
88
+
89
+ > `systemctl --user` 是**用户级**的,**不需要 root / sudo**。但前提是机器装了 systemd(部分容器 / 共享主机没有),且开机自启需要 `loginctl enable-linger`(个别受限环境可能被禁用)。用不了就走方式 A。
90
+
91
+ `~/.config/systemd/user/cc-switch.service`:
92
+
93
+ ```ini
94
+ [Unit]
95
+ Description=CC Switch Web UI
96
+ After=network.target
97
+
98
+ [Service]
99
+ WorkingDirectory=%h/cc-switch-ui
100
+ ExecStart=%h/.local/bin/uv run app.py --host 127.0.0.1 --port 8765
101
+ Restart=always
102
+ RestartSec=3
103
+
104
+ [Install]
105
+ WantedBy=default.target
106
+ ```
107
+
108
+ ```bash
109
+ systemctl --user daemon-reload
110
+ systemctl --user enable --now cc-switch # 启动 + 开机自启
111
+ loginctl enable-linger "$USER" # 登出后仍保持运行(如被禁则用方式 A)
112
+ journalctl --user -u cc-switch -f # 看日志
113
+ ```
114
+
115
+ ## 配置文件
116
+
117
+ `~/.ccm_config`(JSON,权限 `0600`,由本服务读写)。结构示例:
118
+
119
+ ```json
120
+ {
121
+ "current_provider": "deepseek",
122
+ "providers": {
123
+ "deepseek": {
124
+ "label": "DeepSeek",
125
+ "base_url": "https://api.deepseek.com/anthropic",
126
+ "auth_var": "ANTHROPIC_AUTH_TOKEN",
127
+ "model": "deepseek-chat",
128
+ "accounts": [
129
+ { "id": "b68b00552679", "name": "个人", "api_key": "sk-..." }
130
+ ],
131
+ "active_account": "b68b00552679"
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ 切换/启动时,后端按当前供应商 + 激活账号注入环境变量给 `claude`:
138
+ `ANTHROPIC_BASE_URL`、`ANTHROPIC_AUTH_TOKEN`(或官方的 `ANTHROPIC_API_KEY`)、`ANTHROPIC_MODEL`。
139
+ 各供应商的 `base_url` / `model` 均为内置默认值,可在配置文件中按需修改。
140
+
141
+ ## REST API 一览
142
+
143
+ | 方法 | 路径 | 说明 |
144
+ |------|------|------|
145
+ | GET | `/api/state` | 全量状态(密钥脱敏)+ 进程状态 |
146
+ | POST | `/api/provider/switch` | 切换当前供应商 `{provider}` |
147
+ | PUT | `/api/provider/<id>` | 编辑端点 `{label?, base_url?, model?, auth_var?}` |
148
+ | POST | `/api/account` | 新增账号 `{provider,name,api_key}` |
149
+ | PUT | `/api/account/<id>` | 编辑账号 `{provider,name,api_key?}` |
150
+ | DELETE | `/api/account/<id>?provider=` | 删除账号 |
151
+ | POST | `/api/account/activate` | 设为激活账号 `{provider,account_id}` |
152
+ | POST | `/api/claude/start` | 启动进程 `{args?, cwd?, rows?, cols?}` |
153
+ | POST | `/api/claude/restart` | 重启进程(复用上次启动参数) |
154
+ | POST | `/api/claude/stop` | 停止进程 |
155
+ | POST | `/api/claude/input` | 向进程发送输入:`{raw}` 原始按键流 / `{text}` 整行 |
156
+ | POST | `/api/claude/resize` | 调整 pty 窗口大小 `{rows, cols}` |
157
+ | POST | `/api/claude/keepalive` | 开关保活看门狗 `{enabled}` |
158
+ | GET | `/api/claude/status` | 进程状态 |
159
+ | GET | `/api/claude/stream` | SSE 实时输出 |
160
+ | GET | `/api/fs/list?path=` | 列出子目录(目录选择器用) |
161
+
162
+ ## 安全提示
163
+
164
+ - 仅监听 `127.0.0.1`,请勿暴露到公网(API Key 以明文存于配置文件)。
165
+ - 配置文件权限已设为 `0600`。
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "cc-switch-ui"
7
+ version = "0.1.0"
8
+ description = "CC Switch Web UI —— Claude Code 多供应商 / 多账号管理面板"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.12"
12
+ authors = [
13
+ {name = "cliecy"},
14
+ ]
15
+ keywords = [
16
+ "claude-code",
17
+ "claude",
18
+ "anthropic",
19
+ "deepseek",
20
+ "kimi",
21
+ "glm",
22
+ "qwen",
23
+ "openrouter",
24
+ "web-ui",
25
+ "management",
26
+ ]
27
+ classifiers = [
28
+ "Development Status :: 3 - Alpha",
29
+ "Environment :: Web Environment",
30
+ "Intended Audience :: Developers",
31
+ "License :: OSI Approved :: MIT License",
32
+ "Operating System :: POSIX :: Linux",
33
+ "Operating System :: MacOS",
34
+ "Programming Language :: Python :: 3",
35
+ "Programming Language :: Python :: 3.12",
36
+ "Programming Language :: Python :: 3.13",
37
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
38
+ "Topic :: Software Development :: User Interfaces",
39
+ "Topic :: Utilities",
40
+ ]
41
+ dependencies = [
42
+ "flask>=3.1",
43
+ ]
44
+
45
+ [project.scripts]
46
+ cc-switch-ui = "cc_switch_ui.app:main"
47
+
48
+ [project.urls]
49
+ Homepage = "https://github.com/cliecy/cc-switch-ui"
50
+ Issues = "https://github.com/cliecy/cc-switch-ui/issues"
51
+
52
+ [tool.hatch.build.targets.wheel]
53
+ packages = ["src/cc_switch_ui"]
54
+
55
+ [tool.hatch.build.targets.sdist]
56
+ include = [
57
+ "src/cc_switch_ui/**/*.py",
58
+ "src/cc_switch_ui/index.html",
59
+ "README.md",
60
+ "run.sh",
61
+ ]
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+ # CC Switch 守护脚本 —— 无需 root、不依赖 systemd,任意普通用户可用。
3
+ # - setsid 让服务脱离当前终端会话:SSH 断开 / 终端关闭都不会被 SIGHUP 杀掉
4
+ # - while 循环实现崩溃自动重启(app.py 退出后 2 秒拉起)
5
+ # - app 退出时会自行清理它的 claude 子进程(见 app.py 的 SIGTERM 处理)
6
+ #
7
+ # 用法: ./run.sh {start|stop|restart|status|log|fg}
8
+ set -u
9
+ cd "$(dirname "$0")" || exit 1
10
+
11
+ HOST="${CC_HOST:-127.0.0.1}"
12
+ PORT="${CC_PORT:-8765}"
13
+ PIDFILE=".cc-switch.pid"
14
+ LOG="cc-switch.log"
15
+
16
+ is_running() { [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; }
17
+
18
+ start() {
19
+ if is_running; then
20
+ echo "已在运行 (pid $(cat "$PIDFILE")) → http://$HOST:$PORT"; return 0
21
+ fi
22
+ # setsid: 新会话,脱离终端;内部 while 循环:app 崩了自动重启
23
+ setsid bash -c '
24
+ while true; do
25
+ uv run cc-switch-ui --host '"$HOST"' --port '"$PORT"' >> "'"$LOG"'" 2>&1
26
+ echo "[$(date "+%F %T")] app.py 退出(code $?),2 秒后自动重启…" >> "'"$LOG"'"
27
+ sleep 2
28
+ done
29
+ ' </dev/null >/dev/null 2>&1 &
30
+ echo $! > "$PIDFILE"
31
+ sleep 2
32
+ if is_running; then
33
+ echo "已启动 → http://$HOST:$PORT (pid $(cat "$PIDFILE"), 日志: $LOG)"
34
+ else
35
+ echo "启动失败,请看日志: $LOG"; rm -f "$PIDFILE"; return 1
36
+ fi
37
+ }
38
+
39
+ stop() {
40
+ if ! is_running; then echo "未运行"; rm -f "$PIDFILE"; return 0; fi
41
+ local pid; pid="$(cat "$PIDFILE")"
42
+ # 负号 = 杀整个进程组(守护循环 + uv + app.py);app 收到 SIGTERM 会清理 claude
43
+ kill -TERM -- "-$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null
44
+ sleep 2
45
+ kill -KILL -- "-$pid" 2>/dev/null
46
+ rm -f "$PIDFILE"
47
+ echo "已停止"
48
+ }
49
+
50
+ case "${1:-}" in
51
+ start) start ;;
52
+ stop) stop ;;
53
+ restart) stop; sleep 1; start ;;
54
+ status) if is_running; then echo "运行中 (pid $(cat "$PIDFILE")) → http://$HOST:$PORT";
55
+ else echo "未运行"; fi ;;
56
+ log) tail -n 60 -f "$LOG" ;;
57
+ fg) exec uv run cc-switch-ui --host "$HOST" --port "$PORT" ;; # 前台调试用
58
+ *) echo "用法: ./run.sh {start|stop|restart|status|log|fg}";
59
+ echo "可用环境变量覆盖: CC_HOST(默认 127.0.0.1) CC_PORT(默认 8765)";;
60
+ esac
@@ -0,0 +1,3 @@
1
+ """CC Switch Web UI —— Claude Code 多供应商 / 多账号管理面板"""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,41 @@
1
+ """
2
+ CC Switch Web UI —— CLI 入口点。
3
+ """
4
+
5
+ import argparse
6
+ import atexit
7
+ import os
8
+ import signal
9
+
10
+ from .config import CONFIG_PATH
11
+ from .server import create_app
12
+
13
+
14
+ def main():
15
+ parser = argparse.ArgumentParser(
16
+ description="CC Switch Web UI —— Claude Code 多供应商 / 多账号管理面板",
17
+ )
18
+ parser.add_argument("--host", default="127.0.0.1", help="监听地址 (默认 127.0.0.1)")
19
+ parser.add_argument("--port", type=int, default=8765, help="监听端口 (默认 8765)")
20
+ args = parser.parse_args()
21
+
22
+ app = create_app()
23
+ claude_proc = app._claude_proc
24
+
25
+ def _cleanup_children(*_):
26
+ """app 退出时连带停掉 claude 子进程,避免留下「失联孤儿」。"""
27
+ claude_proc.keepalive = False
28
+ claude_proc.stop()
29
+
30
+ # 进程退出 / 被 systemd 或守护脚本 SIGTERM 时,清理 claude 子进程
31
+ atexit.register(_cleanup_children)
32
+ signal.signal(signal.SIGTERM, lambda *a: (_cleanup_children(), os._exit(0)))
33
+
34
+ print(f"配置文件: {CONFIG_PATH}")
35
+ print(f"CC Switch Web UI 已启动 → http://{args.host}:{args.port}")
36
+ # threaded=True 保证 SSE 长连接不阻塞其它请求
37
+ app.run(host=args.host, port=args.port, threaded=True, debug=False)
38
+
39
+
40
+ if __name__ == "__main__":
41
+ main()