ccoding 0.1.0__tar.gz → 0.2.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.
Files changed (31) hide show
  1. {ccoding-0.1.0 → ccoding-0.2.0}/PKG-INFO +49 -4
  2. ccoding-0.2.0/README.md +146 -0
  3. {ccoding-0.1.0 → ccoding-0.2.0}/pyproject.toml +6 -1
  4. ccoding-0.2.0/src/ccode/__init__.py +12 -0
  5. {ccoding-0.1.0 → ccoding-0.2.0}/src/ccode/api.py +5 -1
  6. {ccoding-0.1.0 → ccoding-0.2.0}/src/ccode/config.py +31 -0
  7. ccoding-0.2.0/src/ccode/remote.py +673 -0
  8. ccoding-0.2.0/src/ccode/remote_cli.py +182 -0
  9. {ccoding-0.1.0 → ccoding-0.2.0}/src/ccode/ui.py +130 -9
  10. ccoding-0.2.0/src/ccode/web/__init__.py +0 -0
  11. ccoding-0.2.0/src/ccode/web/app.js +270 -0
  12. ccoding-0.2.0/src/ccode/web/assets/addon-fit.js +2 -0
  13. ccoding-0.2.0/src/ccode/web/assets/xterm-LICENSE +21 -0
  14. ccoding-0.2.0/src/ccode/web/assets/xterm-addon-fit-LICENSE +19 -0
  15. ccoding-0.2.0/src/ccode/web/assets/xterm.css +285 -0
  16. ccoding-0.2.0/src/ccode/web/assets/xterm.js +2 -0
  17. ccoding-0.2.0/src/ccode/web/index.html +39 -0
  18. ccoding-0.2.0/src/ccode/web/style.css +170 -0
  19. ccoding-0.2.0/uv.lock +680 -0
  20. ccoding-0.1.0/README.md +0 -104
  21. ccoding-0.1.0/src/ccode/__init__.py +0 -7
  22. ccoding-0.1.0/uv.lock +0 -31
  23. {ccoding-0.1.0 → ccoding-0.2.0}/.github/workflows/publish.yml +0 -0
  24. {ccoding-0.1.0 → ccoding-0.2.0}/.gitignore +0 -0
  25. {ccoding-0.1.0 → ccoding-0.2.0}/CLAUDE.md +0 -0
  26. {ccoding-0.1.0 → ccoding-0.2.0}/LICENSE +0 -0
  27. {ccoding-0.1.0 → ccoding-0.2.0}/screenshot.png +0 -0
  28. {ccoding-0.1.0 → ccoding-0.2.0}/src/ccode/__main__.py +0 -0
  29. {ccoding-0.1.0 → ccoding-0.2.0}/src/ccode/cli.py +0 -0
  30. {ccoding-0.1.0 → ccoding-0.2.0}/src/ccode/env_schema.json +0 -0
  31. {ccoding-0.1.0 → ccoding-0.2.0}/src/ccode/logo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccoding
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Curses-based Claude Code launcher
5
5
  Project-URL: Homepage, https://github.com/8DE4732A/ccode
6
6
  Project-URL: Repository, https://github.com/8DE4732A/ccode
@@ -35,6 +35,9 @@ Classifier: Operating System :: POSIX
35
35
  Classifier: Programming Language :: Python :: 3
36
36
  Classifier: Programming Language :: Python :: 3 :: Only
37
37
  Requires-Python: >=3.10
38
+ Requires-Dist: fastapi
39
+ Requires-Dist: pexpect; sys_platform != 'win32'
40
+ Requires-Dist: uvicorn[standard]
38
41
  Requires-Dist: windows-curses; sys_platform == 'win32'
39
42
  Description-Content-Type: text/markdown
40
43
 
@@ -52,6 +55,7 @@ Description-Content-Type: text/markdown
52
55
  - 配置界面编辑 Base URL / API Key 与常用开关
53
56
  - 自动保存配置到 `~/.ccode/config.json`
54
57
  - 直接启动 `claude` 并传递额外参数
58
+ - Remote/Web 模式:启动共享 FastAPI hub,通过 tmux 保留多个 Claude Code 会话并在浏览器中选择操作
55
59
  - 动画 Logo(颜色渐变 / 流光 / 轻微波浪)
56
60
 
57
61
  ## 安装
@@ -73,11 +77,12 @@ pipx install ccoding
73
77
  ```bash
74
78
  ccode
75
79
  ccode --chrome # 传递额外参数给 claude
80
+ ccode-remote # 查看 remote hub 状态和存活会话
76
81
  ```
77
82
 
78
83
  ## 开发者运行方式
79
84
 
80
- 需要 Python >=3.10,仅依赖标准库(Windows 额外需要 `windows-curses`,安装时自动处理)。
85
+ 需要 Python >=3.10。Remote/Web 模式依赖 FastAPI、uvicorn、pexpect,并需要系统中已安装 `tmux` 和 `claude`;Windows 建议在 WSL 中使用 remote 模式。
81
86
 
82
87
  ```bash
83
88
  git clone https://github.com/8DE4732A/ccode
@@ -92,8 +97,9 @@ uv run ccode
92
97
  - `← / →`:切换 owned_by / model_id 字段
93
98
  - `a / d`:循环切换当前字段的可选项
94
99
  - `b`:刷新模型列表
100
+ - `r`:切换 Remote/Web 模式
95
101
  - `c`:进入配置界面
96
- - `Enter`:校验并启动 `claude`
102
+ - `Enter`:校验并启动本地 `claude`;Remote/Web 模式开启时启动本地 Web 服务
97
103
  - `q`:退出
98
104
 
99
105
  > 提示:首次使用需要先在配置界面设置 Base URL 与 API Key,然后返回主界面按 `b` 获取模型列表。
@@ -102,7 +108,8 @@ uv run ccode
102
108
 
103
109
  - `↑ / ↓`:移动字段焦点
104
110
  - `Enter / Space`:切换开关项
105
- - 文本输入:编辑 BASE_URL / API_KEY
111
+ - 文本输入:编辑 BASE_URL / API_KEY、remote host/port/prefix/token
112
+ - `g`:在 Remote token 字段重新生成访问 token
106
113
  - `ESC`:自动保存并返回主界面
107
114
 
108
115
  ## 配置文件
@@ -128,10 +135,48 @@ uv run ccode
128
135
  "CLAUDE_CODE_ENABLE_TELEMETRY": "0",
129
136
  "DISABLE_COST_WARNINGS": "1",
130
137
  "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
138
+ },
139
+ "remote": {
140
+ "enabled": false,
141
+ "host": "127.0.0.1",
142
+ "port": 8765,
143
+ "token": "",
144
+ "session_name": "ccode-claude",
145
+ "reuse_session": true
131
146
  }
132
147
  }
133
148
  ```
134
149
 
150
+ ## Remote/Web 模式
151
+
152
+ Remote/Web 模式默认只监听 `127.0.0.1`。token 完全来自用户配置,不会在每次启动时随机生成;如果未配置 token,remote 启动会提示先在配置页设置,或在 TOKEN 字段按 `g` 生成一次并保存。
153
+
154
+ 终端只输出不带 token 的本地访问地址。浏览器打开页面后需要手动输入 token,前端会用 `sessionStorage` 做浏览器会话级缓存;关闭该浏览器会话后需要重新输入。所有 session list、session API 和 WebSocket 连接仍然需要 token。
155
+
156
+ 每个启用 Remote/Web 的 `ccode` 进程都会创建一个唯一 tmux session,session 名由配置里的 `session_name` 前缀、时间戳和进程号组成,例如 `ccode-claude-20260509-153022-12345`。本机只启动一个共享 remote hub,多个本地 `ccode` 进程会复用同一个 host/port/token。浏览器认证后会先显示 session 列表,选择某个 session 后进入 terminal,也可以返回列表重新选择。
157
+
158
+ session registry 保存在 `~/.ccode/remote_sessions.json`,只记录 tmux session、cwd、args、创建时间和 owner pid,不保存 API key、token 或环境变量。刷新 session 列表时会实时检查 tmux 是否仍存在,并自动隐藏和清理已结束的 session。
159
+
160
+ 浏览器和多个本地终端可以同时 attach 到同一个 tmux session,输入会互相影响,这是预期行为。可用 `Ctrl+B` 然后 `D` 从本地终端 detach;remote hub 会独立留在后台运行,tmux session 不会因为浏览器断开而被 kill。
161
+
162
+ 如果通过 Cloudflare Tunnel、Nginx、Caddy 等反向代理暴露服务,建议额外叠加 Cloudflare Access、Basic Auth 或 OAuth2 proxy。监听 `0.0.0.0` 会把所有 remote sessions 暴露给网络内可达主机,请保护好访问入口和 token。
163
+
164
+ 示例:
165
+
166
+ ```bash
167
+ cloudflared tunnel --url http://127.0.0.1:8765
168
+ ```
169
+
170
+ remote hub 日志写入 `~/.ccode/remote_server.log`,记录 hub 启停、session 注册、session API 和 WebSocket 连接状态,不记录 token。启动 hub 前如果日志超过 5 MiB 会自动轮转,默认保留 3 个历史文件(`.1`、`.2`、`.3`)。可用 `ccode-remote` 管理 remote hub 并查看监听地址、日志位置、日志大小和存活 tmux sessions;不传子命令时默认等同于 `status`:
171
+
172
+ ```bash
173
+ ccode-remote
174
+ ccode-remote status
175
+ ccode-remote start
176
+ ccode-remote restart
177
+ ccode-remote stop
178
+ ```
179
+
135
180
  ## 工作原理
136
181
 
137
182
  1. 从 `~/.ccode/config.json` 读取配置(首次运行自动生成)。
@@ -0,0 +1,146 @@
1
+ # CCode Launcher (Curses UI)
2
+
3
+ 这是一个基于 curses 的 Claude Code 启动器,提供键盘全操作的终端界面,用于配置 Base URL、API Key、模型选择和常用开关,并直接启动 `claude` 命令。
4
+
5
+ 工具的目的是方便启动claude code时快速选择想要使用的模型。模型网关可以使用litellm proxy 或者 CLI Proxy API
6
+
7
+ ![](screenshot.png)
8
+ ## 功能特点
9
+
10
+ - 终端 curses UI,键盘全操作
11
+ - 主界面选择 OPUS / SONNET / HAIKU 的 owned_by 与 model_id
12
+ - 配置界面编辑 Base URL / API Key 与常用开关
13
+ - 自动保存配置到 `~/.ccode/config.json`
14
+ - 直接启动 `claude` 并传递额外参数
15
+ - Remote/Web 模式:启动共享 FastAPI hub,通过 tmux 保留多个 Claude Code 会话并在浏览器中选择操作
16
+ - 动画 Logo(颜色渐变 / 流光 / 轻微波浪)
17
+
18
+ ## 安装
19
+
20
+ 推荐使用 [uv](https://github.com/astral-sh/uv) 安装为全局工具:
21
+
22
+ ```bash
23
+ uv tool install ccoding
24
+ ```
25
+
26
+ 或使用 [pipx](https://pipx.pypa.io):
27
+
28
+ ```bash
29
+ pipx install ccoding
30
+ ```
31
+
32
+ 安装后直接运行:
33
+
34
+ ```bash
35
+ ccode
36
+ ccode --chrome # 传递额外参数给 claude
37
+ ccode-remote # 查看 remote hub 状态和存活会话
38
+ ```
39
+
40
+ ## 开发者运行方式
41
+
42
+ 需要 Python >=3.10。Remote/Web 模式依赖 FastAPI、uvicorn、pexpect,并需要系统中已安装 `tmux` 和 `claude`;Windows 建议在 WSL 中使用 remote 模式。
43
+
44
+ ```bash
45
+ git clone https://github.com/8DE4732A/ccode
46
+ cd ccode
47
+ uv sync
48
+ uv run ccode
49
+ ```
50
+
51
+ ## 快捷键(主界面)
52
+
53
+ - `↑ / ↓`:切换 OPUS / SONNET / HAIKU 行
54
+ - `← / →`:切换 owned_by / model_id 字段
55
+ - `a / d`:循环切换当前字段的可选项
56
+ - `b`:刷新模型列表
57
+ - `r`:切换 Remote/Web 模式
58
+ - `c`:进入配置界面
59
+ - `Enter`:校验并启动本地 `claude`;Remote/Web 模式开启时启动本地 Web 服务
60
+ - `q`:退出
61
+
62
+ > 提示:首次使用需要先在配置界面设置 Base URL 与 API Key,然后返回主界面按 `b` 获取模型列表。
63
+
64
+ ## 快捷键(配置界面)
65
+
66
+ - `↑ / ↓`:移动字段焦点
67
+ - `Enter / Space`:切换开关项
68
+ - 文本输入:编辑 BASE_URL / API_KEY、remote host/port/prefix/token
69
+ - `g`:在 Remote token 字段重新生成访问 token
70
+ - `ESC`:自动保存并返回主界面
71
+
72
+ ## 配置文件
73
+
74
+ 配置自动保存在:
75
+
76
+ ```
77
+ ~/.ccode/config.json
78
+ ```
79
+
80
+ 示例结构:
81
+
82
+ ```json
83
+ {
84
+ "base_url": "http://127.0.0.1:8317",
85
+ "api_key": "",
86
+ "models": {
87
+ "opus": {"owned_by": null, "id": null},
88
+ "sonnet": {"owned_by": null, "id": null},
89
+ "haiku": {"owned_by": null, "id": null}
90
+ },
91
+ "toggles": {
92
+ "CLAUDE_CODE_ENABLE_TELEMETRY": "0",
93
+ "DISABLE_COST_WARNINGS": "1",
94
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
95
+ },
96
+ "remote": {
97
+ "enabled": false,
98
+ "host": "127.0.0.1",
99
+ "port": 8765,
100
+ "token": "",
101
+ "session_name": "ccode-claude",
102
+ "reuse_session": true
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## Remote/Web 模式
108
+
109
+ Remote/Web 模式默认只监听 `127.0.0.1`。token 完全来自用户配置,不会在每次启动时随机生成;如果未配置 token,remote 启动会提示先在配置页设置,或在 TOKEN 字段按 `g` 生成一次并保存。
110
+
111
+ 终端只输出不带 token 的本地访问地址。浏览器打开页面后需要手动输入 token,前端会用 `sessionStorage` 做浏览器会话级缓存;关闭该浏览器会话后需要重新输入。所有 session list、session API 和 WebSocket 连接仍然需要 token。
112
+
113
+ 每个启用 Remote/Web 的 `ccode` 进程都会创建一个唯一 tmux session,session 名由配置里的 `session_name` 前缀、时间戳和进程号组成,例如 `ccode-claude-20260509-153022-12345`。本机只启动一个共享 remote hub,多个本地 `ccode` 进程会复用同一个 host/port/token。浏览器认证后会先显示 session 列表,选择某个 session 后进入 terminal,也可以返回列表重新选择。
114
+
115
+ session registry 保存在 `~/.ccode/remote_sessions.json`,只记录 tmux session、cwd、args、创建时间和 owner pid,不保存 API key、token 或环境变量。刷新 session 列表时会实时检查 tmux 是否仍存在,并自动隐藏和清理已结束的 session。
116
+
117
+ 浏览器和多个本地终端可以同时 attach 到同一个 tmux session,输入会互相影响,这是预期行为。可用 `Ctrl+B` 然后 `D` 从本地终端 detach;remote hub 会独立留在后台运行,tmux session 不会因为浏览器断开而被 kill。
118
+
119
+ 如果通过 Cloudflare Tunnel、Nginx、Caddy 等反向代理暴露服务,建议额外叠加 Cloudflare Access、Basic Auth 或 OAuth2 proxy。监听 `0.0.0.0` 会把所有 remote sessions 暴露给网络内可达主机,请保护好访问入口和 token。
120
+
121
+ 示例:
122
+
123
+ ```bash
124
+ cloudflared tunnel --url http://127.0.0.1:8765
125
+ ```
126
+
127
+ remote hub 日志写入 `~/.ccode/remote_server.log`,记录 hub 启停、session 注册、session API 和 WebSocket 连接状态,不记录 token。启动 hub 前如果日志超过 5 MiB 会自动轮转,默认保留 3 个历史文件(`.1`、`.2`、`.3`)。可用 `ccode-remote` 管理 remote hub 并查看监听地址、日志位置、日志大小和存活 tmux sessions;不传子命令时默认等同于 `status`:
128
+
129
+ ```bash
130
+ ccode-remote
131
+ ccode-remote status
132
+ ccode-remote start
133
+ ccode-remote restart
134
+ ccode-remote stop
135
+ ```
136
+
137
+ ## 工作原理
138
+
139
+ 1. 从 `~/.ccode/config.json` 读取配置(首次运行自动生成)。
140
+ 2. 根据 Base URL + API Key 拉取模型列表。
141
+ 3. 选择 OPUS / SONNET / HAIKU 的 owned_by / model_id。
142
+ 4. 启动 `claude`,并注入相关环境变量。
143
+
144
+ ## 许可证
145
+
146
+ MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ccoding"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "Curses-based Claude Code launcher"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -15,6 +15,9 @@ classifiers = [
15
15
  "Programming Language :: Python :: 3 :: Only",
16
16
  ]
17
17
  dependencies = [
18
+ "fastapi",
19
+ "uvicorn[standard]",
20
+ "pexpect; sys_platform != 'win32'",
18
21
  "windows-curses; sys_platform == 'win32'",
19
22
  ]
20
23
 
@@ -25,6 +28,7 @@ Issues = "https://github.com/8DE4732A/ccode/issues"
25
28
 
26
29
  [project.scripts]
27
30
  ccode = "ccode.cli:main"
31
+ ccode-remote = "ccode.remote_cli:main"
28
32
 
29
33
  [build-system]
30
34
  requires = ["hatchling"]
@@ -32,3 +36,4 @@ build-backend = "hatchling.build"
32
36
 
33
37
  [tool.hatch.build.targets.wheel]
34
38
  packages = ["src/ccode"]
39
+ artifacts = ["src/ccode/web/**"]
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ __version__ = "0.2.0"
4
+
5
+
6
+ def main() -> None:
7
+ from .cli import main as cli_main
8
+
9
+ cli_main()
10
+
11
+
12
+ __all__ = ["main", "__version__"]
@@ -125,6 +125,10 @@ def validate_launch_requirements(config: dict[str, Any]) -> str | None:
125
125
  return None
126
126
 
127
127
 
128
+ def build_claude_command(args: list[str]) -> list[str]:
129
+ return ["claude", *args]
130
+
131
+
128
132
  def launch_claude(config: dict[str, Any], args: list[str]) -> str | None:
129
133
  base_url = config.get("base_url", "").strip()
130
134
  api_key = config.get("api_key", "").strip()
@@ -132,7 +136,7 @@ def launch_claude(config: dict[str, Any], args: list[str]) -> str | None:
132
136
  return "Missing base URL or API key."
133
137
  env = build_env(config, masked=False)
134
138
  try:
135
- subprocess.run(["claude", *args], check=True, env=env)
139
+ subprocess.run(build_claude_command(args), check=True, env=env)
136
140
  except FileNotFoundError:
137
141
  return "Could not find 'claude' on PATH."
138
142
  except subprocess.CalledProcessError as exc:
@@ -21,6 +21,15 @@ DEFAULT_TOGGLES: dict[str, str] = {
21
21
  "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
22
22
  }
23
23
 
24
+ DEFAULT_REMOTE: dict[str, Any] = {
25
+ "enabled": False,
26
+ "host": "127.0.0.1",
27
+ "port": 8765,
28
+ "token": "",
29
+ "session_name": "ccode-claude",
30
+ "reuse_session": True,
31
+ }
32
+
24
33
  _ENV_SCHEMA: dict[str, Any] = {}
25
34
  _SCHEMA_KEYS: list[str] = []
26
35
 
@@ -65,6 +74,7 @@ def default_config() -> dict[str, Any]:
65
74
  "haiku": {"owned_by": None, "id": None},
66
75
  },
67
76
  "toggles": DEFAULT_TOGGLES.copy(),
77
+ "remote": DEFAULT_REMOTE.copy(),
68
78
  }
69
79
 
70
80
 
@@ -114,6 +124,27 @@ def load_config() -> dict[str, Any]:
114
124
  if new_toggles:
115
125
  config["toggles"] = new_toggles
116
126
 
127
+ remote = data.get("remote")
128
+ if isinstance(remote, dict):
129
+ enabled = remote.get("enabled")
130
+ if isinstance(enabled, bool):
131
+ config["remote"]["enabled"] = enabled
132
+ host = remote.get("host")
133
+ if isinstance(host, str):
134
+ config["remote"]["host"] = host
135
+ port = remote.get("port")
136
+ if isinstance(port, int):
137
+ config["remote"]["port"] = port
138
+ token = remote.get("token")
139
+ if isinstance(token, str):
140
+ config["remote"]["token"] = token
141
+ session_name = remote.get("session_name")
142
+ if isinstance(session_name, str):
143
+ config["remote"]["session_name"] = session_name
144
+ reuse_session = remote.get("reuse_session")
145
+ if isinstance(reuse_session, bool):
146
+ config["remote"]["reuse_session"] = reuse_session
147
+
117
148
  return config
118
149
 
119
150