coderfleet 0.1.0__tar.gz → 0.1.1__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 (99) hide show
  1. coderfleet-0.1.1/.github/workflows/docs.yml +56 -0
  2. coderfleet-0.1.1/.github/workflows/release.yml +89 -0
  3. {coderfleet-0.1.0 → coderfleet-0.1.1}/.gitignore +2 -0
  4. {coderfleet-0.1.0/coderfleet/data → coderfleet-0.1.1}/Dockerfile +5 -1
  5. {coderfleet-0.1.0 → coderfleet-0.1.1}/PKG-INFO +26 -11
  6. {coderfleet-0.1.0 → coderfleet-0.1.1}/README.md +24 -10
  7. {coderfleet-0.1.0 → coderfleet-0.1.1}/accounts.conf.example +7 -1
  8. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/cli.py +1 -1
  9. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/compose.py +16 -1
  10. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/config_cmds.py +6 -6
  11. {coderfleet-0.1.0 → coderfleet-0.1.1/coderfleet/data}/Dockerfile +5 -1
  12. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/data/accounts.conf.example +7 -1
  13. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/data/entrypoint.sh +10 -0
  14. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/init_wizard.py +1 -0
  15. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/login_cmd.py +24 -3
  16. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/main.py +207 -0
  17. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/models.py +194 -2
  18. coderfleet-0.1.1/coderfleet/server/push_manager.py +140 -0
  19. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/scheduler.py +348 -2
  20. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/css/main.css +850 -13
  21. coderfleet-0.1.1/coderfleet/server/static/css/renderer.css +341 -0
  22. coderfleet-0.1.1/coderfleet/server/static/icons/icon.svg +6 -0
  23. coderfleet-0.1.1/coderfleet/server/static/icons/logo-mark.png +0 -0
  24. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/index.html +201 -4
  25. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/app.js +2 -0
  26. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/chat.js +134 -10
  27. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/log.js +2 -0
  28. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/nav.js +4 -2
  29. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/renderer.js +120 -4
  30. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/state.js +41 -0
  31. coderfleet-0.1.1/coderfleet/server/static/js/workflows.js +1248 -0
  32. coderfleet-0.1.1/coderfleet/server/static/manifest.json +26 -0
  33. coderfleet-0.1.1/coderfleet/server/static/mobile.html +1169 -0
  34. coderfleet-0.1.1/coderfleet/server/static/sw.js +61 -0
  35. coderfleet-0.1.1/coderfleet/server/static/vendor/drawflow.min.css +1 -0
  36. coderfleet-0.1.1/coderfleet/server/static/vendor/drawflow.min.js +1 -0
  37. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/task_cmds.py +1 -1
  38. coderfleet-0.1.1/docs/accounts.md +72 -0
  39. coderfleet-0.1.1/docs/assets/logo-mark.png +0 -0
  40. coderfleet-0.1.1/docs/assets/logo.svg +5 -0
  41. coderfleet-0.1.1/docs/assets/screenshots/app-accounts.png +0 -0
  42. coderfleet-0.1.1/docs/assets/screenshots/app-chat.png +0 -0
  43. coderfleet-0.1.1/docs/assets/screenshots/app-projects.png +0 -0
  44. coderfleet-0.1.1/docs/assets/screenshots/app-tasks.png +0 -0
  45. coderfleet-0.1.1/docs/commands.md +50 -0
  46. coderfleet-0.1.1/docs/concepts.md +47 -0
  47. coderfleet-0.1.1/docs/configuration.md +62 -0
  48. coderfleet-0.1.1/docs/custom-image.md +34 -0
  49. coderfleet-0.1.1/docs/faq.md +29 -0
  50. coderfleet-0.1.1/docs/index.md +135 -0
  51. coderfleet-0.1.1/docs/install.md +58 -0
  52. coderfleet-0.1.1/docs/migration.md +89 -0
  53. coderfleet-0.1.1/docs/network.md +51 -0
  54. coderfleet-0.1.1/docs/projects.md +51 -0
  55. coderfleet-0.1.1/docs/quickstart.md +96 -0
  56. coderfleet-0.1.1/docs/requirements.txt +2 -0
  57. coderfleet-0.1.1/docs/server.md +45 -0
  58. coderfleet-0.1.1/docs/stylesheets/extra.css +202 -0
  59. coderfleet-0.1.1/docs/tasks.md +67 -0
  60. {coderfleet-0.1.0 → coderfleet-0.1.1}/entrypoint.sh +10 -0
  61. coderfleet-0.1.1/mkdocs.yml +80 -0
  62. {coderfleet-0.1.0 → coderfleet-0.1.1}/pyproject.toml +2 -1
  63. {coderfleet-0.1.0 → coderfleet-0.1.1}/tests/test_coderfleet_cli.py +47 -0
  64. {coderfleet-0.1.0 → coderfleet-0.1.1}/tests/test_scheduler.py +132 -0
  65. coderfleet-0.1.1/tests/test_static_ui.py +398 -0
  66. coderfleet-0.1.1/uv.lock +1631 -0
  67. coderfleet-0.1.0/tests/test_static_ui.py +0 -196
  68. {coderfleet-0.1.0 → coderfleet-0.1.1}/.github/workflows/publish.yml +0 -0
  69. {coderfleet-0.1.0 → coderfleet-0.1.1}/CLAUDE.md +0 -0
  70. {coderfleet-0.1.0 → coderfleet-0.1.1}/changelog/migrate-from-aicm-to-coderfleet.md +0 -0
  71. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/__init__.py +0 -0
  72. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/__main__.py +0 -0
  73. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/config.py +0 -0
  74. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/data/__init__.py +0 -0
  75. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/data/config.conf.example +0 -0
  76. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/data/projects.conf.example +0 -0
  77. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/data/scripts/coderfleet_usage_status.py +0 -0
  78. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/docker_ops.py +0 -0
  79. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/__init__.py +0 -0
  80. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/docker_mgr.py +0 -0
  81. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/accounts.js +0 -0
  82. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/projects.js +0 -0
  83. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/submit.js +0 -0
  84. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/tasks.js +0 -0
  85. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/terminal.js +0 -0
  86. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/js/utils.js +0 -0
  87. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/vendor/marked.min.js +0 -0
  88. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/vendor/xterm/addon-fit.js +0 -0
  89. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/vendor/xterm/xterm.css +0 -0
  90. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/static/vendor/xterm/xterm.js +0 -0
  91. {coderfleet-0.1.0 → coderfleet-0.1.1}/coderfleet/server/terminal.py +0 -0
  92. {coderfleet-0.1.0 → coderfleet-0.1.1}/config.conf.example +0 -0
  93. {coderfleet-0.1.0 → coderfleet-0.1.1}/conversations/.gitkeep +0 -0
  94. {coderfleet-0.1.0 → coderfleet-0.1.1}/projects.conf.example +0 -0
  95. {coderfleet-0.1.0 → coderfleet-0.1.1}/scripts/coderfleet_usage_status.py +0 -0
  96. {coderfleet-0.1.0 → coderfleet-0.1.1}/tasks/.gitkeep +0 -0
  97. {coderfleet-0.1.0 → coderfleet-0.1.1}/tests/conftest.py +0 -0
  98. {coderfleet-0.1.0 → coderfleet-0.1.1}/tests/test_terminal.py +0 -0
  99. {coderfleet-0.1.0 → coderfleet-0.1.1}/tests/test_usage_status_script.py +0 -0
@@ -0,0 +1,56 @@
1
+ name: Deploy documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "docs/**"
9
+ - "mkdocs.yml"
10
+ - ".github/workflows/docs.yml"
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+ pages: write
16
+ id-token: write
17
+
18
+ concurrency:
19
+ group: pages
20
+ cancel-in-progress: false
21
+
22
+ jobs:
23
+ build:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - name: Check out repository
27
+ uses: actions/checkout@v4
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: "3.12"
33
+
34
+ - name: Install documentation dependencies
35
+ run: |
36
+ python -m pip install --upgrade pip
37
+ python -m pip install -r docs/requirements.txt
38
+
39
+ - name: Build site
40
+ run: mkdocs build --strict
41
+
42
+ - name: Upload artifact
43
+ uses: actions/upload-pages-artifact@v3
44
+ with:
45
+ path: site
46
+
47
+ deploy:
48
+ environment:
49
+ name: github-pages
50
+ url: ${{ steps.deployment.outputs.page_url }}
51
+ runs-on: ubuntu-latest
52
+ needs: build
53
+ steps:
54
+ - name: Deploy to GitHub Pages
55
+ id: deployment
56
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,89 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ bump:
7
+ description: 'Next dev version bump after this release'
8
+ required: true
9
+ default: patch
10
+ type: choice
11
+ options:
12
+ - patch
13
+ - minor
14
+ - major
15
+
16
+ permissions:
17
+ contents: write
18
+
19
+ jobs:
20
+ release:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: Checkout main
24
+ uses: actions/checkout@v4
25
+ with:
26
+ token: ${{ secrets.GITHUB_TOKEN }}
27
+ fetch-depth: 0
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: '3.12'
33
+
34
+ - name: Compute versions
35
+ id: ver
36
+ env:
37
+ BUMP: ${{ inputs.bump }}
38
+ run: |
39
+ python - <<'PY'
40
+ import re, os, tomllib
41
+ from pathlib import Path
42
+
43
+ raw = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
44
+ m = re.match(r'^(\d+)\.(\d+)\.(\d+)(?:\.dev\d+)?$', raw)
45
+ if not m:
46
+ raise SystemExit(f"Cannot parse version: {raw!r}")
47
+ major, minor, patch = map(int, m.groups())
48
+ release = f"{major}.{minor}.{patch}"
49
+
50
+ bump = os.environ["BUMP"]
51
+ if bump == "major":
52
+ nxt = f"{major+1}.0.0.dev0"
53
+ elif bump == "minor":
54
+ nxt = f"{major}.{minor+1}.0.dev0"
55
+ else:
56
+ nxt = f"{major}.{minor}.{patch+1}.dev0"
57
+
58
+ Path(os.environ["GITHUB_OUTPUT"]).open("a").write(
59
+ f"release={release}\ntag=v{release}\nnext={nxt}\n"
60
+ )
61
+ PY
62
+
63
+ - name: Configure git
64
+ run: |
65
+ git config user.name "github-actions[bot]"
66
+ git config user.email "github-actions[bot]@users.noreply.github.com"
67
+
68
+ - name: Set release version
69
+ run: |
70
+ sed -i 's/^version = .*/version = "${{ steps.ver.outputs.release }}"/' pyproject.toml
71
+ git add pyproject.toml
72
+ git diff --cached --quiet || git commit -m "release: v${{ steps.ver.outputs.release }}"
73
+ git push origin main
74
+
75
+ - name: Create GitHub Release
76
+ env:
77
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78
+ run: |
79
+ gh release create ${{ steps.ver.outputs.tag }} \
80
+ --title "v${{ steps.ver.outputs.release }}" \
81
+ --generate-notes \
82
+ --latest
83
+
84
+ - name: Bump to next dev version
85
+ run: |
86
+ sed -i 's/^version = .*/version = "${{ steps.ver.outputs.next }}"/' pyproject.toml
87
+ git add pyproject.toml
88
+ git commit -m "chore: bump to ${{ steps.ver.outputs.next }}"
89
+ git push origin main
@@ -53,3 +53,5 @@ core
53
53
  .*-uploads
54
54
 
55
55
  server.log
56
+
57
+ update.sh
@@ -1,6 +1,6 @@
1
1
  # ============================================================
2
2
  # CoderFleet — 统一工作容器镜像
3
- # 包含:Python 3.12 / Node.js 20 / Codex CLI / Claude Code
3
+ # 包含:Python 3.12 / Node.js 20 / Codex CLI / Claude Code / OpenCode
4
4
  # 平台:linux/amd64(Apple Silicon 通过 Docker 模拟运行)
5
5
  # ============================================================
6
6
 
@@ -55,6 +55,9 @@ RUN npm install -g @openai/codex
55
55
  # ── Claude Code CLI ───────────────────────────────────────
56
56
  RUN npm install -g @anthropic-ai/claude-code
57
57
 
58
+ # ── OpenCode CLI ──────────────────────────────────────────
59
+ RUN npm install -g opencode-ai@latest
60
+
58
61
  # ── Rust 1.93.0 ───────────────────────────────────────────
59
62
  ENV RUSTUP_HOME=/usr/local/rustup \
60
63
  CARGO_HOME=/usr/local/cargo \
@@ -75,6 +78,7 @@ WORKDIR /workspace
75
78
  # 这两个目录由 CoderFleet 按账号挂载,容器内路径固定
76
79
  # Codex 认证:/home/byclaw/.codex 由 CODEX_HOME 控制
77
80
  # Claude 认证:/home/byclaw/.claude 由 CLAUDE_CONFIG_DIR 控制
81
+ # OpenCode 数据:/home/byclaw/.opencode(运行时按账号挂载并设置 XDG_*_HOME)
78
82
  ENV CODEX_HOME=/home/byclaw/.codex
79
83
  ENV CLAUDE_CONFIG_DIR=/home/byclaw/.claude
80
84
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderfleet
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: CoderFleet - Run multiple Claude Code/Codex accounts in isolated Docker containers
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: aiofiles==24.1.0
@@ -9,6 +9,7 @@ Requires-Dist: fastapi==0.115.0
9
9
  Requires-Dist: httpx>=0.27
10
10
  Requires-Dist: pydantic==2.9.2
11
11
  Requires-Dist: python-multipart==0.0.9
12
+ Requires-Dist: pywebpush>=2.0.0
12
13
  Requires-Dist: pyyaml>=6.0
13
14
  Requires-Dist: uvicorn[standard]==0.30.6
14
15
  Description-Content-Type: text/markdown
@@ -21,14 +22,14 @@ Description-Content-Type: text/markdown
21
22
 
22
23
  ### 💡 为什么需要 CoderFleet?
23
24
 
24
- 在重度 AI 编程开发中,大模型 CLI(如 Claude Code、Codex)经常面临尴尬的使用瓶颈:
25
+ 在重度 AI 编程开发中,大模型 CLI(如 Claude Code、Codex、OpenCode)经常面临尴尬的使用瓶颈:
25
26
  * **单账号(Plus 会员)的额度不够用**,频繁遭遇大模型服务商的 Rate Limit 或当日使用上限;
26
27
  * **高倍数套餐(如 5x/10x/20x 的账号会员)又极其昂贵**,在周期内往往使用量根本用不完,性价比极低;
27
28
  * 最优解通常是**自己申请 2 个或多个普通/Plus 账号轮流使用**,但在单台机器上频繁切换账号、管理认证数据、以及维护隔离的网络环境极其繁杂。
28
29
 
29
30
  **CoderFleet 正是为此而生!**
30
31
 
31
- 它允许你在单台宿主机上同时运行多个 **Codex CLI** 和 **Claude Code** 账号。每个账号采用独立容器隔离、独立会话认证,并设计了严密的**物理内网中继代理(gost)**网络,确保所有网络出站流量强制且唯一地走宿主机代理出口,完美解决国内开发者在调用 Claude / OpenAI 服务时遭遇的网络封锁和封号风险,实现"多账号额度无缝叠加、轮询提效"。
32
+ 它允许你在单台宿主机上同时运行多个 **Codex CLI**、**Claude Code** 和 **OpenCode** 账号。每个账号采用独立容器隔离、独立会话认证,并设计了严密的**物理内网中继代理(gost)**网络,确保所有网络出站流量强制且唯一地走宿主机代理出口,完美解决国内开发者在调用 Claude / OpenAI 服务时遭遇的网络封锁和封号风险,实现"多账号额度无缝叠加、轮询提效"。
32
33
 
33
34
  ---
34
35
 
@@ -137,6 +138,7 @@ coderfleet build
137
138
  - Rust 1.93.0
138
139
  - Codex CLI(`@openai/codex`)
139
140
  - Claude Code(`@anthropic-ai/claude-code`)
141
+ - OpenCode(`opencode-ai`)
140
142
 
141
143
  ### 3. 添加账号与项目
142
144
 
@@ -148,6 +150,10 @@ coderfleet project add app-a alice ~/projects/app-a
148
150
  # Claude Code 账号
149
151
  coderfleet account add bob TYPE=claude
150
152
  coderfleet project add app-b bob ~/projects/app-b
153
+
154
+ # OpenCode 账号
155
+ coderfleet account add carol TYPE=opencode
156
+ coderfleet project add app-c carol ~/projects/app-c
151
157
  ```
152
158
 
153
159
  ### 4. 生成配置并启动容器
@@ -172,6 +178,7 @@ CLI 会输出授权 URL,在宿主机浏览器打开 → 完成授权 → 复
172
178
  # 打开多个终端
173
179
  coderfleet enter app-a # 进入 app-a 项目容器(使用 Codex 账号 alice)
174
180
  coderfleet enter app-b # 进入 app-b 项目容器(使用 Claude Code 账号 bob)
181
+ coderfleet enter app-c # 进入 app-c 项目容器(使用 OpenCode 账号 carol)
175
182
  ```
176
183
 
177
184
  进入后使用对应 CLI:
@@ -182,6 +189,9 @@ codex "帮我实现用户认证模块"
182
189
 
183
190
  # Claude Code 容器内
184
191
  claude "帮我重构这个函数"
192
+
193
+ # OpenCode 容器内
194
+ opencode run "帮我修复测试"
185
195
  ```
186
196
 
187
197
  ### 7. 启动调度服务与 Web 控制台
@@ -239,7 +249,7 @@ coderfleet task logs <任务ID> -f
239
249
 
240
250
  | 命令 | 说明 |
241
251
  |------|------|
242
- | `coderfleet account add <名称> TYPE=codex\|claude [--auth env] [--env-file 路径] [--proxy relay\|off]` | 添加账号 |
252
+ | `coderfleet account add <名称> TYPE=codex\|claude\|opencode [--auth env] [--env-file 路径] [--proxy relay\|off]` | 添加账号 |
243
253
  | `coderfleet account remove <名称>` | 删除账号(自动停止关联容器) |
244
254
  | `coderfleet account list` | 列出所有账号及运行状态 |
245
255
  | `coderfleet login <账号名\|all>` | 登录账号并持久化认证文件 |
@@ -302,10 +312,12 @@ RELAY_IMAGE=gogost/gost:3
302
312
  ### accounts.conf
303
313
 
304
314
  ```conf
305
- # 格式:NAME=<名称> TYPE=codex|claude [AUTH=login|env] [ENV_FILE=路径] [PROXY=relay|off]
315
+ # 格式:NAME=<名称> TYPE=codex|claude|opencode [AUTH=login|env] [ENV_FILE=路径] [PROXY=relay|off]
306
316
  NAME=alice TYPE=codex
307
317
  NAME=bob TYPE=claude
318
+ NAME=carol TYPE=opencode
308
319
  NAME=claude-api TYPE=claude AUTH=env ENV_FILE=./accounts/claude-api/env
320
+ NAME=opencode-api TYPE=opencode AUTH=env ENV_FILE=./accounts/opencode-api/env
309
321
  NAME=local TYPE=claude PROXY=off
310
322
  ```
311
323
 
@@ -314,16 +326,17 @@ NAME=local TYPE=claude PROXY=off
314
326
  | 字段 | 必填 | 说明 |
315
327
  |------|------|------|
316
328
  | `NAME` | 是 | 账号名,只允许字母/数字/连字符 |
317
- | `TYPE` | 是 | `codex` 使用 Codex CLI,`claude` 使用 Claude Code |
318
- | `AUTH` | 否 | 认证方式,默认 `login`;Claude Code 可用 `env` 通过 API Key 认证 |
329
+ | `TYPE` | 是 | `codex` 使用 Codex CLI,`claude` 使用 Claude Code,`opencode` 使用 OpenCode |
330
+ | `AUTH` | 否 | 认证方式,默认 `login`;Claude Code / OpenCode 可用 `env` 通过 API Key 认证 |
319
331
  | `ENV_FILE` | 否 | Docker Compose env_file 路径;`AUTH=env` 时省略则默认 `./accounts/<名称>/env` |
320
332
  | `PROXY` | 否 | 默认 `relay`(走代理中继);`off` 表示不注入代理变量,连接普通网络 |
321
333
 
322
- `AUTH=env` 用于 Claude Code API key 场景:
334
+ `AUTH=env` 用于 Claude Code / OpenCode API key 场景:
323
335
 
324
336
  ```bash
325
337
  coderfleet account add claude-api TYPE=claude --auth env
326
- # 然后编辑 ~/.coderfleet/accounts/claude-api/env
338
+ coderfleet account add opencode-api TYPE=opencode --auth env
339
+ # 然后编辑对应的 ~/.coderfleet/accounts/<账号名>/env
327
340
  ```
328
341
 
329
342
  示例 env 文件:
@@ -333,7 +346,7 @@ ANTHROPIC_API_KEY=sk-ant-...
333
346
  # ANTHROPIC_BASE_URL=https://api.anthropic.com
334
347
  ```
335
348
 
336
- > 注意:Claude Code 会优先使用 `ANTHROPIC_API_KEY`,`login all` 会自动跳过 `AUTH=env` 账号。
349
+ > 注意:Claude Code 会优先使用 `ANTHROPIC_API_KEY`;OpenCode 会读取环境变量和项目 `.env` 中的 provider API key;`login all` 会自动跳过 `AUTH=env` 账号。
337
350
 
338
351
  ### projects.conf
339
352
 
@@ -400,13 +413,15 @@ coderfleet check-proxy
400
413
 
401
414
  ## 认证机制
402
415
 
403
- 两种 CLI 的认证目录挂载方式:
416
+ 三种 CLI 的认证目录挂载方式:
404
417
 
405
418
  | CLI | 认证目录(容器内) | 本地存储 |
406
419
  |-----|--------------------|----------|
407
420
  | Codex | `/home/byclaw/.codex` | `accounts/<名称>/` |
408
421
  | Claude Code | `/home/byclaw/.claude` | `accounts/<名称>/` |
409
422
  | Claude Code API key | 同上 | `ENV_FILE` 指定文件 |
423
+ | OpenCode | `/home/byclaw/.opencode`(内部设置 XDG data/config/state/cache) | `accounts/<名称>/` |
424
+ | OpenCode API key | 环境变量 | `ENV_FILE` 指定文件 |
410
425
 
411
426
  每个账号的认证数据独立存储,容器删除重建后无需重新登录。
412
427
 
@@ -6,14 +6,14 @@
6
6
 
7
7
  ### 💡 为什么需要 CoderFleet?
8
8
 
9
- 在重度 AI 编程开发中,大模型 CLI(如 Claude Code、Codex)经常面临尴尬的使用瓶颈:
9
+ 在重度 AI 编程开发中,大模型 CLI(如 Claude Code、Codex、OpenCode)经常面临尴尬的使用瓶颈:
10
10
  * **单账号(Plus 会员)的额度不够用**,频繁遭遇大模型服务商的 Rate Limit 或当日使用上限;
11
11
  * **高倍数套餐(如 5x/10x/20x 的账号会员)又极其昂贵**,在周期内往往使用量根本用不完,性价比极低;
12
12
  * 最优解通常是**自己申请 2 个或多个普通/Plus 账号轮流使用**,但在单台机器上频繁切换账号、管理认证数据、以及维护隔离的网络环境极其繁杂。
13
13
 
14
14
  **CoderFleet 正是为此而生!**
15
15
 
16
- 它允许你在单台宿主机上同时运行多个 **Codex CLI** 和 **Claude Code** 账号。每个账号采用独立容器隔离、独立会话认证,并设计了严密的**物理内网中继代理(gost)**网络,确保所有网络出站流量强制且唯一地走宿主机代理出口,完美解决国内开发者在调用 Claude / OpenAI 服务时遭遇的网络封锁和封号风险,实现"多账号额度无缝叠加、轮询提效"。
16
+ 它允许你在单台宿主机上同时运行多个 **Codex CLI**、**Claude Code** 和 **OpenCode** 账号。每个账号采用独立容器隔离、独立会话认证,并设计了严密的**物理内网中继代理(gost)**网络,确保所有网络出站流量强制且唯一地走宿主机代理出口,完美解决国内开发者在调用 Claude / OpenAI 服务时遭遇的网络封锁和封号风险,实现"多账号额度无缝叠加、轮询提效"。
17
17
 
18
18
  ---
19
19
 
@@ -122,6 +122,7 @@ coderfleet build
122
122
  - Rust 1.93.0
123
123
  - Codex CLI(`@openai/codex`)
124
124
  - Claude Code(`@anthropic-ai/claude-code`)
125
+ - OpenCode(`opencode-ai`)
125
126
 
126
127
  ### 3. 添加账号与项目
127
128
 
@@ -133,6 +134,10 @@ coderfleet project add app-a alice ~/projects/app-a
133
134
  # Claude Code 账号
134
135
  coderfleet account add bob TYPE=claude
135
136
  coderfleet project add app-b bob ~/projects/app-b
137
+
138
+ # OpenCode 账号
139
+ coderfleet account add carol TYPE=opencode
140
+ coderfleet project add app-c carol ~/projects/app-c
136
141
  ```
137
142
 
138
143
  ### 4. 生成配置并启动容器
@@ -157,6 +162,7 @@ CLI 会输出授权 URL,在宿主机浏览器打开 → 完成授权 → 复
157
162
  # 打开多个终端
158
163
  coderfleet enter app-a # 进入 app-a 项目容器(使用 Codex 账号 alice)
159
164
  coderfleet enter app-b # 进入 app-b 项目容器(使用 Claude Code 账号 bob)
165
+ coderfleet enter app-c # 进入 app-c 项目容器(使用 OpenCode 账号 carol)
160
166
  ```
161
167
 
162
168
  进入后使用对应 CLI:
@@ -167,6 +173,9 @@ codex "帮我实现用户认证模块"
167
173
 
168
174
  # Claude Code 容器内
169
175
  claude "帮我重构这个函数"
176
+
177
+ # OpenCode 容器内
178
+ opencode run "帮我修复测试"
170
179
  ```
171
180
 
172
181
  ### 7. 启动调度服务与 Web 控制台
@@ -224,7 +233,7 @@ coderfleet task logs <任务ID> -f
224
233
 
225
234
  | 命令 | 说明 |
226
235
  |------|------|
227
- | `coderfleet account add <名称> TYPE=codex\|claude [--auth env] [--env-file 路径] [--proxy relay\|off]` | 添加账号 |
236
+ | `coderfleet account add <名称> TYPE=codex\|claude\|opencode [--auth env] [--env-file 路径] [--proxy relay\|off]` | 添加账号 |
228
237
  | `coderfleet account remove <名称>` | 删除账号(自动停止关联容器) |
229
238
  | `coderfleet account list` | 列出所有账号及运行状态 |
230
239
  | `coderfleet login <账号名\|all>` | 登录账号并持久化认证文件 |
@@ -287,10 +296,12 @@ RELAY_IMAGE=gogost/gost:3
287
296
  ### accounts.conf
288
297
 
289
298
  ```conf
290
- # 格式:NAME=<名称> TYPE=codex|claude [AUTH=login|env] [ENV_FILE=路径] [PROXY=relay|off]
299
+ # 格式:NAME=<名称> TYPE=codex|claude|opencode [AUTH=login|env] [ENV_FILE=路径] [PROXY=relay|off]
291
300
  NAME=alice TYPE=codex
292
301
  NAME=bob TYPE=claude
302
+ NAME=carol TYPE=opencode
293
303
  NAME=claude-api TYPE=claude AUTH=env ENV_FILE=./accounts/claude-api/env
304
+ NAME=opencode-api TYPE=opencode AUTH=env ENV_FILE=./accounts/opencode-api/env
294
305
  NAME=local TYPE=claude PROXY=off
295
306
  ```
296
307
 
@@ -299,16 +310,17 @@ NAME=local TYPE=claude PROXY=off
299
310
  | 字段 | 必填 | 说明 |
300
311
  |------|------|------|
301
312
  | `NAME` | 是 | 账号名,只允许字母/数字/连字符 |
302
- | `TYPE` | 是 | `codex` 使用 Codex CLI,`claude` 使用 Claude Code |
303
- | `AUTH` | 否 | 认证方式,默认 `login`;Claude Code 可用 `env` 通过 API Key 认证 |
313
+ | `TYPE` | 是 | `codex` 使用 Codex CLI,`claude` 使用 Claude Code,`opencode` 使用 OpenCode |
314
+ | `AUTH` | 否 | 认证方式,默认 `login`;Claude Code / OpenCode 可用 `env` 通过 API Key 认证 |
304
315
  | `ENV_FILE` | 否 | Docker Compose env_file 路径;`AUTH=env` 时省略则默认 `./accounts/<名称>/env` |
305
316
  | `PROXY` | 否 | 默认 `relay`(走代理中继);`off` 表示不注入代理变量,连接普通网络 |
306
317
 
307
- `AUTH=env` 用于 Claude Code API key 场景:
318
+ `AUTH=env` 用于 Claude Code / OpenCode API key 场景:
308
319
 
309
320
  ```bash
310
321
  coderfleet account add claude-api TYPE=claude --auth env
311
- # 然后编辑 ~/.coderfleet/accounts/claude-api/env
322
+ coderfleet account add opencode-api TYPE=opencode --auth env
323
+ # 然后编辑对应的 ~/.coderfleet/accounts/<账号名>/env
312
324
  ```
313
325
 
314
326
  示例 env 文件:
@@ -318,7 +330,7 @@ ANTHROPIC_API_KEY=sk-ant-...
318
330
  # ANTHROPIC_BASE_URL=https://api.anthropic.com
319
331
  ```
320
332
 
321
- > 注意:Claude Code 会优先使用 `ANTHROPIC_API_KEY`,`login all` 会自动跳过 `AUTH=env` 账号。
333
+ > 注意:Claude Code 会优先使用 `ANTHROPIC_API_KEY`;OpenCode 会读取环境变量和项目 `.env` 中的 provider API key;`login all` 会自动跳过 `AUTH=env` 账号。
322
334
 
323
335
  ### projects.conf
324
336
 
@@ -385,13 +397,15 @@ coderfleet check-proxy
385
397
 
386
398
  ## 认证机制
387
399
 
388
- 两种 CLI 的认证目录挂载方式:
400
+ 三种 CLI 的认证目录挂载方式:
389
401
 
390
402
  | CLI | 认证目录(容器内) | 本地存储 |
391
403
  |-----|--------------------|----------|
392
404
  | Codex | `/home/byclaw/.codex` | `accounts/<名称>/` |
393
405
  | Claude Code | `/home/byclaw/.claude` | `accounts/<名称>/` |
394
406
  | Claude Code API key | 同上 | `ENV_FILE` 指定文件 |
407
+ | OpenCode | `/home/byclaw/.opencode`(内部设置 XDG data/config/state/cache) | `accounts/<名称>/` |
408
+ | OpenCode API key | 环境变量 | `ENV_FILE` 指定文件 |
395
409
 
396
410
  每个账号的认证数据独立存储,容器删除重建后无需重新登录。
397
411
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # 字段说明:
5
5
  # NAME=<名称> 必填,只允许字母/数字/连字符
6
- # TYPE=codex|claude 必填,使用哪个 AI CLI
6
+ # TYPE=codex|claude|opencode 必填,使用哪个 AI CLI
7
7
  # AUTH=login|env 可选,默认 login
8
8
  # ENV_FILE=<路径> AUTH=env 时可选;省略则默认 ./accounts/<名称>/env
9
9
  # PROXY=relay|off 可选,默认 relay;off 表示该账号下项目不注入代理
@@ -18,9 +18,15 @@
18
18
  # Claude Code 账号示例
19
19
  # NAME=claude-alice TYPE=claude
20
20
 
21
+ # OpenCode 账号示例
22
+ # NAME=opencode-alice TYPE=opencode
23
+
21
24
  # Claude Code 环境变量认证示例(API key 会优先于订阅登录态)
22
25
  # NAME=claude-api TYPE=claude AUTH=env
23
26
  # NAME=claude-api TYPE=claude AUTH=env ENV_FILE=./accounts/claude-api/env
24
27
 
28
+ # OpenCode 环境变量认证示例(可在 env 文件中配置各 provider API key)
29
+ # NAME=opencode-api TYPE=opencode AUTH=env
30
+
25
31
  # 不走代理的账号示例(该账号关联的项目都会直连)
26
32
  # NAME=claude-local TYPE=claude PROXY=off
@@ -113,7 +113,7 @@ def _server_status(ws: Path) -> None:
113
113
  def main(ctx: click.Context) -> None:
114
114
  """CoderFleet
115
115
 
116
- Manages multiple Claude Code / Codex accounts in isolated Docker containers.
116
+ Manages multiple Claude Code / Codex / OpenCode accounts in isolated Docker containers.
117
117
 
118
118
  Workspace location: CODERFLEET_WORKSPACE env var, or ~/.coderfleet/ by default.
119
119
  """
@@ -14,6 +14,13 @@ import yaml
14
14
  from coderfleet.config import load_config, parse_conf
15
15
 
16
16
 
17
+ AUTH_DIRS = {
18
+ "codex": "/home/byclaw/.codex",
19
+ "claude": "/home/byclaw/.claude",
20
+ "opencode": "/home/byclaw/.opencode",
21
+ }
22
+
23
+
17
24
  def _make_dumper() -> type[yaml.Dumper]:
18
25
  """Return a YAML Dumper that quotes YAML-1.1 boolean-ambiguous strings."""
19
26
  _BOOL_LIKE = frozenset(["off", "on", "yes", "no", "true", "false", "null", "~"])
@@ -94,7 +101,7 @@ def generate_compose(ws: Path) -> dict[str, Any]:
94
101
  svc_name = f"{acc_type}-project-{pname}"
95
102
  ctr_name = f"{acc_type}-{pname}"
96
103
  auth_src = f"./accounts/{paccount}"
97
- auth_dst = "/home/byclaw/.codex" if acc_type == "codex" else "/home/byclaw/.claude"
104
+ auth_dst = AUTH_DIRS.get(acc_type, "/home/byclaw/.codex")
98
105
 
99
106
  (ws / "accounts" / paccount).mkdir(parents=True, exist_ok=True)
100
107
 
@@ -107,6 +114,14 @@ def generate_compose(ws: Path) -> dict[str, Any]:
107
114
  "CODERFLEET_ACCOUNT_PROXY": acc_proxy,
108
115
  }
109
116
 
117
+ if acc_type == "opencode":
118
+ environment.update({
119
+ "XDG_DATA_HOME": "/home/byclaw/.opencode/data",
120
+ "XDG_CONFIG_HOME": "/home/byclaw/.opencode/config",
121
+ "XDG_STATE_HOME": "/home/byclaw/.opencode/state",
122
+ "XDG_CACHE_HOME": "/home/byclaw/.opencode/cache",
123
+ })
124
+
110
125
  if acc_proxy != "off":
111
126
  environment.update({
112
127
  "HTTP_PROXY": proxy_url,
@@ -38,7 +38,7 @@ def account_group() -> None:
38
38
 
39
39
  @account_group.command("add")
40
40
  @click.argument("name")
41
- @click.argument("typearg", metavar="TYPE=codex|claude")
41
+ @click.argument("typearg", metavar="TYPE=codex|claude|opencode")
42
42
  @click.option("--auth", default="login", type=click.Choice(["login", "env"]),
43
43
  show_default=True, help="Authentication method")
44
44
  @click.option("--env-file", "env_file", default=None,
@@ -54,19 +54,19 @@ def cmd_account_add(
54
54
  env_file: Optional[str],
55
55
  proxy: str,
56
56
  ) -> None:
57
- """Add a new account. TYPE=codex|claude (or just codex|claude)."""
57
+ """Add a new account. TYPE=codex|claude|opencode (or just codex|claude|opencode)."""
58
58
  ws: Path = ctx.obj["workspace"]
59
59
 
60
60
  if not _NAME_RE.match(name):
61
61
  raise click.ClickException("NAME 只能包含字母、数字、连字符")
62
62
 
63
63
  acc_type = typearg[5:] if typearg.startswith("TYPE=") else typearg
64
- if acc_type not in ("codex", "claude"):
65
- raise click.ClickException("TYPE 不合法,只支持 TYPE=codex 或 TYPE=claude")
64
+ if acc_type not in ("codex", "claude", "opencode"):
65
+ raise click.ClickException("TYPE 不合法,只支持 TYPE=codex、TYPE=claude 或 TYPE=opencode")
66
66
 
67
67
  if auth == "env":
68
- if acc_type != "claude":
69
- raise click.ClickException("AUTH=env 目前只支持 TYPE=claude")
68
+ if acc_type not in ("claude", "opencode"):
69
+ raise click.ClickException("AUTH=env 目前只支持 TYPE=claude 或 TYPE=opencode")
70
70
  if not env_file:
71
71
  env_file = f"./accounts/{name}/env"
72
72
 
@@ -1,6 +1,6 @@
1
1
  # ============================================================
2
2
  # CoderFleet — 统一工作容器镜像
3
- # 包含:Python 3.12 / Node.js 20 / Codex CLI / Claude Code
3
+ # 包含:Python 3.12 / Node.js 20 / Codex CLI / Claude Code / OpenCode
4
4
  # 平台:linux/amd64(Apple Silicon 通过 Docker 模拟运行)
5
5
  # ============================================================
6
6
 
@@ -55,6 +55,9 @@ RUN npm install -g @openai/codex
55
55
  # ── Claude Code CLI ───────────────────────────────────────
56
56
  RUN npm install -g @anthropic-ai/claude-code
57
57
 
58
+ # ── OpenCode CLI ──────────────────────────────────────────
59
+ RUN npm install -g opencode-ai@latest
60
+
58
61
  # ── Rust 1.93.0 ───────────────────────────────────────────
59
62
  ENV RUSTUP_HOME=/usr/local/rustup \
60
63
  CARGO_HOME=/usr/local/cargo \
@@ -75,6 +78,7 @@ WORKDIR /workspace
75
78
  # 这两个目录由 CoderFleet 按账号挂载,容器内路径固定
76
79
  # Codex 认证:/home/byclaw/.codex 由 CODEX_HOME 控制
77
80
  # Claude 认证:/home/byclaw/.claude 由 CLAUDE_CONFIG_DIR 控制
81
+ # OpenCode 数据:/home/byclaw/.opencode(运行时按账号挂载并设置 XDG_*_HOME)
78
82
  ENV CODEX_HOME=/home/byclaw/.codex
79
83
  ENV CLAUDE_CONFIG_DIR=/home/byclaw/.claude
80
84
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # 字段说明:
5
5
  # NAME=<名称> 必填,只允许字母/数字/连字符
6
- # TYPE=codex|claude 必填,使用哪个 AI CLI
6
+ # TYPE=codex|claude|opencode 必填,使用哪个 AI CLI
7
7
  # AUTH=login|env 可选,默认 login
8
8
  # ENV_FILE=<路径> AUTH=env 时可选;省略则默认 ./accounts/<名称>/env
9
9
  # PROXY=relay|off 可选,默认 relay;off 表示该账号下项目不注入代理
@@ -18,9 +18,15 @@
18
18
  # Claude Code 账号示例
19
19
  # NAME=claude-alice TYPE=claude
20
20
 
21
+ # OpenCode 账号示例
22
+ # NAME=opencode-alice TYPE=opencode
23
+
21
24
  # Claude Code 环境变量认证示例(API key 会优先于订阅登录态)
22
25
  # NAME=claude-api TYPE=claude AUTH=env
23
26
  # NAME=claude-api TYPE=claude AUTH=env ENV_FILE=./accounts/claude-api/env
24
27
 
28
+ # OpenCode 环境变量认证示例(可在 env 文件中配置各 provider API key)
29
+ # NAME=opencode-api TYPE=opencode AUTH=env
30
+
25
31
  # 不走代理的账号示例(该账号关联的项目都会直连)
26
32
  # NAME=claude-local TYPE=claude PROXY=off
@@ -49,6 +49,16 @@ case "$ACCOUNT_TYPE" in
49
49
  echo "Claude 环境变量认证:已启用"
50
50
  fi
51
51
  ;;
52
+ opencode)
53
+ mkdir -p "${XDG_DATA_HOME:-/home/byclaw/.opencode/data}"
54
+ mkdir -p "${XDG_CONFIG_HOME:-/home/byclaw/.opencode/config}"
55
+ mkdir -p "${XDG_STATE_HOME:-/home/byclaw/.opencode/state}"
56
+ mkdir -p "${XDG_CACHE_HOME:-/home/byclaw/.opencode/cache}"
57
+ echo "OpenCode 数据目录:${XDG_DATA_HOME:-/home/byclaw/.opencode/data}"
58
+ if [ "${CODERFLEET_ACCOUNT_AUTH:-login}" = "env" ]; then
59
+ echo "OpenCode 环境变量认证:已启用"
60
+ fi
61
+ ;;
52
62
  esac
53
63
 
54
64
  # ── 执行传入的命令(默认 sleep infinity)─────────────────
@@ -209,6 +209,7 @@ def _print_next_steps(ws: Path) -> None:
209
209
  click.secho(f" coderfleet build", fg="cyan")
210
210
  click.echo(f" 2. 添加账号:")
211
211
  click.secho(f" coderfleet account add <名称> TYPE=claude", fg="cyan")
212
+ click.secho(f" coderfleet account add <名称> TYPE=opencode", fg="cyan")
212
213
  click.echo(f" 3. 添加项目:")
213
214
  click.secho(f" coderfleet project add <名称> <账号名> ~/projects/myproject", fg="cyan")
214
215
  click.echo(f" 4. 应用配置并启动容器:")