openclacky 0.9.28 → 0.9.29
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/docs/deploy-architecture.md +619 -0
- data/lib/clacky/agent/message_compressor_helper.rb +2 -3
- data/lib/clacky/agent/skill_manager.rb +2 -2
- data/lib/clacky/brand_config.rb +4 -3
- data/lib/clacky/clacky_auth_client.rb +152 -0
- data/lib/clacky/clacky_cloud_config.rb +123 -0
- data/lib/clacky/client.rb +5 -4
- data/lib/clacky/cloud_project_client.rb +169 -0
- data/lib/clacky/default_agents/base_prompt.md +1 -0
- data/lib/clacky/default_parsers/doc_parser.rb +9 -9
- data/lib/clacky/default_skills/browser-setup/SKILL.md +9 -0
- data/lib/clacky/default_skills/channel-setup/SKILL.md +21 -4
- data/lib/clacky/default_skills/channel-setup/feishu_setup.rb +8 -2
- data/lib/clacky/default_skills/deploy/SKILL.md +96 -5
- data/lib/clacky/default_skills/deploy/scripts/rails_deploy.rb +1268 -274
- data/lib/clacky/default_skills/deploy/tools/create_database_service.rb +341 -0
- data/lib/clacky/default_skills/deploy/tools/execute_deployment.rb +72 -147
- data/lib/clacky/default_skills/deploy/tools/fetch_runtime_logs.rb +60 -50
- data/lib/clacky/default_skills/deploy/tools/list_services.rb +47 -60
- data/lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb +147 -96
- data/lib/clacky/default_skills/new/SKILL.md +117 -5
- data/lib/clacky/default_skills/new/scripts/cloud_project_init.sh +74 -0
- data/lib/clacky/default_skills/new/scripts/create_rails_project.sh +32 -0
- data/lib/clacky/deploy_api_client.rb +484 -0
- data/lib/clacky/message_format/bedrock.rb +3 -2
- data/lib/clacky/server/browser_manager.rb +3 -1
- data/lib/clacky/server/channel/adapters/feishu/ws_client.rb +2 -1
- data/lib/clacky/server/http_server.rb +12 -2
- data/lib/clacky/server/server_master.rb +43 -7
- data/lib/clacky/skill.rb +6 -2
- data/lib/clacky/tools/run_project.rb +4 -1
- data/lib/clacky/tools/shell.rb +7 -1
- data/lib/clacky/utils/arguments_parser.rb +22 -5
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +37 -5
- data/lib/clacky/web/app.js +99 -10
- data/lib/clacky/web/sessions.js +48 -8
- data/lib/clacky/web/skills.js +8 -2
- data/lib/clacky.rb +3 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de10c61ed7d2c530c9bdacae8ddc625cfb969d98316f51f45e1833e87ed9b0cb
|
|
4
|
+
data.tar.gz: 42787ba0901cfc544fde90b46e1a2b4ffe07ce6636bcccbf364141dce3fe0515
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7017db404d793f1cb9483a4a28119f252c8e2e0fd1f183bbe8edf6cf1d1fb5748be2225658dbb666e753499a6694be7c1027e4a448898f22298c5c9bc6d1e2e6
|
|
7
|
+
data.tar.gz: 406a93802ab41cb68a8c098db3b0777583d888e19f84efa871549832216729ef59899f00512351727a2ceaf0ec209bfc2105061e9e7f050fcfbcca0cf6e50a64
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.29] - 2026-04-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Rails deployment skill**: full end-to-end deploy workflow — create Railway project, provision Postgres, set environment variables, and deploy in one conversation
|
|
14
|
+
- **Skill Chinese names**: skills can now declare a `name_zh` field; the Web UI shows the localized name when the language is set to Chinese
|
|
15
|
+
- **Skill name with underscores**: skill identifiers now support underscores (e.g. `my_skill`), not just hyphens
|
|
16
|
+
|
|
17
|
+
### Improved
|
|
18
|
+
- **LLM request timeout**: increased from 120 s to 300 s, reducing timeouts when models output large responses or run slowly
|
|
19
|
+
- **Message compressor**: compressor no longer runs when the agent is idle, avoiding unnecessary token consumption
|
|
20
|
+
- **Socket reliability**: improved WebSocket and browser socket handling to prevent dropped connections under load
|
|
21
|
+
- **Word (.doc) file parsing**: Linux/WSL now uses `antiword` as fallback when `strings` is unavailable, improving compatibility
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **Session name badge clipping**: long session names in the sidebar no longer overflow or get clipped
|
|
25
|
+
- **Browser setup**: `install_browser.sh` is now automatically executed when Node.js is missing during browser setup
|
|
26
|
+
- **Feishu channel setup**: retry login check up to 3 times before falling back to manual setup; fixed tab-closed error on entry; browser tool API timeout raised to 30 s
|
|
27
|
+
- **Language switch rendering**: skill cards and session list now re-render correctly after switching UI language
|
|
28
|
+
- **File path arguments**: argument parser now correctly handles paths with spaces and edge-case formats
|
|
29
|
+
- **Agent working directory**: base prompt now reliably sets the correct working directory for all operations
|
|
30
|
+
- **Feishu WebSocket reconnect**: improved reconnection logic for long-lived Feishu channel connections
|
|
31
|
+
|
|
10
32
|
## [0.9.28] - 2026-04-10
|
|
11
33
|
|
|
12
34
|
### Added
|
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
# Deploy Architecture - 部署架构文档
|
|
2
|
+
|
|
3
|
+
**Last Updated**: 2026-04-11
|
|
4
|
+
|
|
5
|
+
本文档描述 OpenClacky 部署功能的完整架构,包括客户端(openclacky gem)、后端 API(cloud_backend)和 Railway 平台之间的交互。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 整体架构
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
13
|
+
│ OpenClacky 客户端 (Ruby Gem) │
|
|
14
|
+
│ │
|
|
15
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
16
|
+
│ │ Deploy Skill (default_skills/deploy/) │ │
|
|
17
|
+
│ │ - 项目类型检测 │ │
|
|
18
|
+
│ │ - Rails Template 或 Generic Subagent 路由 │ │
|
|
19
|
+
│ │ - 工具调用:set_deploy_variables, check_health │ │
|
|
20
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
21
|
+
│ ↓ │
|
|
22
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
23
|
+
│ │ DeployApiClient (lib/clacky/deploy_api_client.rb) │ │
|
|
24
|
+
│ │ - create_task() # 创建部署任务 │ │
|
|
25
|
+
│ │ - services() # 轮询中间件状态 │ │
|
|
26
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
27
|
+
│ ↓ │
|
|
28
|
+
│ 使用 project token 调用 │
|
|
29
|
+
│ Railway CLI: railway variables --set │
|
|
30
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
31
|
+
↓
|
|
32
|
+
HTTP API (JSON over TLS)
|
|
33
|
+
↓
|
|
34
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
35
|
+
│ Clacky Cloud Backend (Go Service) │
|
|
36
|
+
│ https://api.clacky.ai │
|
|
37
|
+
│ │
|
|
38
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
39
|
+
│ │ POST /openclacky/v1/deploy/create-task │ │
|
|
40
|
+
│ │ - 创建 PlatformProject(如果不存在) │ │
|
|
41
|
+
│ │ - 创建 project token: CreateProjectToken() │ │
|
|
42
|
+
│ │ - 部署 Postgres 中间件(使用 master token) │ │
|
|
43
|
+
│ │ - 返回 project token 给客户端 │ │
|
|
44
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
45
|
+
│ ↓ │
|
|
46
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
47
|
+
│ │ GET /openclacky/v1/deploy/services │ │
|
|
48
|
+
│ │ - 查询 Railway 服务状态 │ │
|
|
49
|
+
│ │ - 客户端轮询直到 Postgres 就绪 │ │
|
|
50
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
51
|
+
│ ↓ │
|
|
52
|
+
│ 使用 master token 调用 │
|
|
53
|
+
│ Railway GraphQL API │
|
|
54
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
55
|
+
↓
|
|
56
|
+
GraphQL API
|
|
57
|
+
↓
|
|
58
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
59
|
+
│ Railway Platform │
|
|
60
|
+
│ │
|
|
61
|
+
│ ┌─────────────────────────┐ │
|
|
62
|
+
│ │ Clacky Master Account │ │
|
|
63
|
+
│ │ (单一 Railway Workspace) │ │
|
|
64
|
+
│ └─────────────────────────┘ │
|
|
65
|
+
│ │ │
|
|
66
|
+
│ ├─ User A's Project ──┬─ main-service │
|
|
67
|
+
│ │ (project token 1) ├─ postgres-service │
|
|
68
|
+
│ │ └─ redis-service │
|
|
69
|
+
│ │ │
|
|
70
|
+
│ ├─ User B's Project ──┬─ app-service │
|
|
71
|
+
│ │ (project token 2) └─ postgres-service │
|
|
72
|
+
│ │ │
|
|
73
|
+
│ └─ User C's Project ──┬─ api-service │
|
|
74
|
+
│ (project token 3) └─ postgres-service │
|
|
75
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 2. Railway Token 架构
|
|
81
|
+
|
|
82
|
+
### 2.1 Token 类型
|
|
83
|
+
|
|
84
|
+
| Token 类型 | 持有者 | 用途 | 权限范围 |
|
|
85
|
+
|-----------|--------|------|---------|
|
|
86
|
+
| **Master Token** (Account Token) | Clacky Backend | 创建 Project<br>创建 Project Token<br>部署中间件 | 整个 Workspace 的所有资源 |
|
|
87
|
+
| **Project Token** | OpenClacky 客户端 | 注入环境变量<br>重新部署服务 | 单个 Project 内的有限操作 |
|
|
88
|
+
|
|
89
|
+
### 2.2 Project Token 权限矩阵
|
|
90
|
+
|
|
91
|
+
> 来源: [railway-sdk README](https://github.com/crisog/railway-sdk) - Official Railway SDK
|
|
92
|
+
|
|
93
|
+
| API Category | Project Token 权限 | 说明 |
|
|
94
|
+
|--------------------|--------------------|------|
|
|
95
|
+
| **projects/** | ⚠️ Get Only | 只能读取当前 project 信息,**不能删除 project** |
|
|
96
|
+
| **services/** | ✅ Full Access | 可创建/删除/管理 services |
|
|
97
|
+
| **environments/** | ✅ Full Access | 可管理 environments |
|
|
98
|
+
| **deployments/** | ❌ Not Authorized | **不能列出或管理 deployments** |
|
|
99
|
+
| **variables/** | ✅ Full Access | 可设置/查看所有环境变量(含密码) |
|
|
100
|
+
| **domains/** | ✅ Full Access | 可创建/删除域名 |
|
|
101
|
+
| **templates/** | ⚠️ Existing Project | 可在现有 project 中部署 template(如 postgres) |
|
|
102
|
+
| **networking/** | ✅ Full Access | 可管理网络配置 |
|
|
103
|
+
| **observability/** | ⚠️ Events Only | 只能查看事件,不能访问 metrics |
|
|
104
|
+
| **workflows/** | ❌ Not Authorized | 无权限 |
|
|
105
|
+
| **integrations/** | ❌ Not Authorized | 无法管理 GitHub 集成 |
|
|
106
|
+
| **volumes/** | ⚠️ Backups Only | 只能管理备份 |
|
|
107
|
+
| **account/** | ❌ Not Authorized | 无账户访问权限 |
|
|
108
|
+
|
|
109
|
+
**关键引用**: "Has broad access within the scoped project but **cannot list deployments or access metrics**."
|
|
110
|
+
|
|
111
|
+
### 2.3 安全性分析
|
|
112
|
+
|
|
113
|
+
#### ✅ Project Token 的限制
|
|
114
|
+
|
|
115
|
+
- ❌ **不能删除 project** - 只有 Get 权限
|
|
116
|
+
- ❌ **不能查看 deployments** - 无权限访问
|
|
117
|
+
- ❌ **不能管理 GitHub 集成** - 无权限
|
|
118
|
+
|
|
119
|
+
#### ⚠️ Project Token 仍可做的危险操作
|
|
120
|
+
|
|
121
|
+
尽管有上述限制,project token 仍然可以:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# 用户拿到 project token 后可以执行:
|
|
125
|
+
export RAILWAY_TOKEN=<project_token>
|
|
126
|
+
|
|
127
|
+
# 1. 删除服务
|
|
128
|
+
railway service delete --service postgres
|
|
129
|
+
|
|
130
|
+
# 2. 查看所有密码
|
|
131
|
+
railway variables --json
|
|
132
|
+
|
|
133
|
+
# 3. 修改数据库密码
|
|
134
|
+
railway variables --set POSTGRES_PASSWORD=hacked
|
|
135
|
+
|
|
136
|
+
# 4. 修改域名配置
|
|
137
|
+
railway domain delete
|
|
138
|
+
|
|
139
|
+
# 5. 修改网络配置
|
|
140
|
+
railway networking ...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### ✅ Backend API 的安全优势
|
|
144
|
+
|
|
145
|
+
**为什么必须使用 Backend API 而不是让用户本地使用 Railway CLI:**
|
|
146
|
+
|
|
147
|
+
1. **细粒度权限控制**
|
|
148
|
+
- Backend 可限制用户只能注入变量,禁止删除服务
|
|
149
|
+
- Backend 可实施业务规则(如禁止删除 postgres)
|
|
150
|
+
|
|
151
|
+
2. **审计日志**
|
|
152
|
+
- 所有操作记录在 `deploy_task_logs` 表
|
|
153
|
+
- 可追溯谁在何时做了什么操作
|
|
154
|
+
|
|
155
|
+
3. **Token 隔离**
|
|
156
|
+
- 用户永远看不到 token
|
|
157
|
+
- Token 只在 backend 和客户端内存中短暂存在
|
|
158
|
+
|
|
159
|
+
4. **输入验证**
|
|
160
|
+
- Backend 验证参数合法性
|
|
161
|
+
- 防止注入攻击和恶意参数
|
|
162
|
+
|
|
163
|
+
5. **业务逻辑保护**
|
|
164
|
+
- Backend 可实施复杂的业务规则
|
|
165
|
+
- 例如:防止误删生产数据库、限制资源配额
|
|
166
|
+
|
|
167
|
+
**如果让用户本地使用 Railway CLI 的风险:**
|
|
168
|
+
|
|
169
|
+
- ❌ 用户拥有 services 完全访问权限(删除、重启)
|
|
170
|
+
- ❌ 用户可查看/修改所有环境变量(含密码)
|
|
171
|
+
- ❌ 用户可修改域名和网络配置
|
|
172
|
+
- ❌ 无审计日志(本地操作无法追踪)
|
|
173
|
+
- ❌ Token 存储在用户机器(泄露风险)
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 3. 部署流程详解
|
|
178
|
+
|
|
179
|
+
### 3.1 数据库创建流程(Step 4)
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
183
|
+
│ Step 4a: 客户端调用 Backend API │
|
|
184
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
185
|
+
│
|
|
186
|
+
│ POST /openclacky/v1/deploy/create-task
|
|
187
|
+
│ { project_id, backup_db, env_vars, region }
|
|
188
|
+
↓
|
|
189
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
190
|
+
│ Step 4b: Backend 处理(cloud_backend/internal/deployment) │
|
|
191
|
+
│ │
|
|
192
|
+
│ 1. 检查 PlatformProject 是否存在 │
|
|
193
|
+
│ - 不存在 → 调用 EnsurePlatformProject() │
|
|
194
|
+
│ ├─ 创建 Railway Project (使用 master token) │
|
|
195
|
+
│ ├─ 创建 Project Token │
|
|
196
|
+
│ │ token, err := cli.GetClient().CreateProjectToken( │
|
|
197
|
+
│ │ ctx, projectID, envID, tokenName) │
|
|
198
|
+
│ └─ 存储到 platform_project 表 │
|
|
199
|
+
│ │
|
|
200
|
+
│ 2. 部署 Postgres 中间件(使用 master token) │
|
|
201
|
+
│ cli.GetClient().DeployTemplateWithConfig(ctx, │
|
|
202
|
+
│ railway.TemplateDeployOptions{ │
|
|
203
|
+
│ ProjectID: pp.PlatformProjectID, │
|
|
204
|
+
│ EnvironmentID: pp.PlatformEnvironmentID, │
|
|
205
|
+
│ TemplateCode: "postgres", │
|
|
206
|
+
│ ServiceName: "Postgres-xxxx", │
|
|
207
|
+
│ Region: task.Region, │
|
|
208
|
+
│ }) │
|
|
209
|
+
│ │
|
|
210
|
+
│ 3. 返回响应给客户端 │
|
|
211
|
+
│ { │
|
|
212
|
+
│ deploy_task_id, │
|
|
213
|
+
│ deploy_service_id, │
|
|
214
|
+
│ platform_token, # ← Project Token │
|
|
215
|
+
│ platform_project_id, │
|
|
216
|
+
│ platform_environment_id │
|
|
217
|
+
│ } │
|
|
218
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
219
|
+
│
|
|
220
|
+
↓
|
|
221
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
222
|
+
│ Step 4c: 客户端轮询服务状态 │
|
|
223
|
+
│ │
|
|
224
|
+
│ loop do │
|
|
225
|
+
│ response = GET /openclacky/v1/deploy/services? │
|
|
226
|
+
│ deploy_task_id=xxx │
|
|
227
|
+
│ │
|
|
228
|
+
│ postgres = response.find { |s| s.type == 'postgres' } │
|
|
229
|
+
│ break if postgres && postgres.status == 'SUCCESS' │
|
|
230
|
+
│ │
|
|
231
|
+
│ sleep 5 # 每 5 秒轮询一次 │
|
|
232
|
+
│ end │
|
|
233
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
234
|
+
│
|
|
235
|
+
↓
|
|
236
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
237
|
+
│ Step 4d: Postgres 就绪,注入 DATABASE_URL │
|
|
238
|
+
│ │
|
|
239
|
+
│ # 使用客户端本地的 Railway CLI │
|
|
240
|
+
│ env = { "RAILWAY_TOKEN" => platform_token } # Project Token │
|
|
241
|
+
│ │
|
|
242
|
+
│ system(env, │
|
|
243
|
+
│ "railway", "variables", │
|
|
244
|
+
│ "--service", main_service_name, │
|
|
245
|
+
│ "--set", "DATABASE_URL=${{Postgres-xxxx.DATABASE_PUBLIC_URL}}",│
|
|
246
|
+
│ in: :close, out: File::NULL) │
|
|
247
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 3.2 关键设计决策
|
|
251
|
+
|
|
252
|
+
| 操作 | 使用的 Token | 执行位置 | 原因 |
|
|
253
|
+
|------|-------------|---------|------|
|
|
254
|
+
| **创建 Project** | Master Token | Backend | 需要 Workspace 级别权限 |
|
|
255
|
+
| **创建 Project Token** | Master Token | Backend | 需要 Workspace 级别权限 |
|
|
256
|
+
| **部署 Postgres** | Master Token | Backend | 统一管理,后端控制 |
|
|
257
|
+
| **注入环境变量** | Project Token | Client | 减轻后端负载,允许客户端重试 |
|
|
258
|
+
| **重新部署服务** | Project Token | Client | 允许用户直接控制 |
|
|
259
|
+
|
|
260
|
+
**为什么数据库创建用 Backend,但变量注入用 Client?**
|
|
261
|
+
|
|
262
|
+
1. **数据库创建(Backend)**:
|
|
263
|
+
- 需要 master token(project token 也可以部署 template,但 backend 统一管理更安全)
|
|
264
|
+
- 涉及资源创建和计费
|
|
265
|
+
- 需要审计和速率限制
|
|
266
|
+
- 异步操作,适合后端处理
|
|
267
|
+
|
|
268
|
+
2. **变量注入(Client)**:
|
|
269
|
+
- 操作简单,适合客户端重试
|
|
270
|
+
- 减轻后端负载(不需要每个变量都调用 backend API)
|
|
271
|
+
- 用户可以快速调整配置
|
|
272
|
+
- Project token 足够且安全(只能修改自己 project 的变量)
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 4. Railway CLI 调用方式
|
|
277
|
+
|
|
278
|
+
### 4.1 问题:`Open3.capture3` 导致挂起
|
|
279
|
+
|
|
280
|
+
**现象**:
|
|
281
|
+
```ruby
|
|
282
|
+
# ❌ 这样会挂起
|
|
283
|
+
env = { "RAILWAY_TOKEN" => token }
|
|
284
|
+
out, err, status = Open3.capture3(env, *cmd)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**原因**:
|
|
288
|
+
- `Open3.capture3` 使用管道捕获 stdout/stderr
|
|
289
|
+
- Railway CLI 可能等待管道缓冲区清空或检测到非 TTY 环境
|
|
290
|
+
- 导致死锁
|
|
291
|
+
|
|
292
|
+
### 4.2 解决方案:使用 `system()`
|
|
293
|
+
|
|
294
|
+
**正确方式**:
|
|
295
|
+
```ruby
|
|
296
|
+
# ✅ 这样不会挂起
|
|
297
|
+
env = { "RAILWAY_TOKEN" => token }
|
|
298
|
+
success = system(env, *cmd, in: :close, out: File::NULL)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**为什么有效**:
|
|
302
|
+
- `system()` 继承父进程的 stdin/stdout/stderr(无管道)
|
|
303
|
+
- `in: :close` 防止 Railway CLI 等待 stdin 输入
|
|
304
|
+
- `out: File::NULL` 抑制 stdout 输出(我们不需要)
|
|
305
|
+
- stderr 保持可见(用于调试)
|
|
306
|
+
|
|
307
|
+
**使用场景**:
|
|
308
|
+
|
|
309
|
+
| 操作 | 使用方法 | 原因 |
|
|
310
|
+
|------|---------|------|
|
|
311
|
+
| `railway variables --set` | `system()` | 避免挂起,不需要输出 |
|
|
312
|
+
| `railway status --json` | `Open3.capture3` | 需要解析 JSON 输出 |
|
|
313
|
+
| `railway up --detach` | `Open3.capture3` | 需要检查 "Build Logs:" 输出 |
|
|
314
|
+
|
|
315
|
+
**相关文件**:
|
|
316
|
+
- `lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb`
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 5. 关键代码路径
|
|
321
|
+
|
|
322
|
+
### 5.1 Backend (Go)
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
cloud_backend/
|
|
326
|
+
├── internal/deployment/service/
|
|
327
|
+
│ ├── create.go # POST /deploy/create-task
|
|
328
|
+
│ │ └─ buildStartDeploySyncResponse() # 构造响应(含 platform_token)
|
|
329
|
+
│ │
|
|
330
|
+
│ ├── middleware.go # 中间件(数据库)部署逻辑
|
|
331
|
+
│ │ ├─ ensureMiddlewares() # 确保 Postgres 存在
|
|
332
|
+
│ │ ├─ deployMiddleware() # 部署单个中间件
|
|
333
|
+
│ │ └─ deployMiddlewareWithRetry() # 重试逻辑(最多 3 次)
|
|
334
|
+
│ │
|
|
335
|
+
│ ├── ensure.go # PlatformProject 管理
|
|
336
|
+
│ │ ├─ EnsurePlatformProject() # 创建或获取 PlatformProject
|
|
337
|
+
│ │ └─ CreateProjectToken() # line 83: 创建 project token
|
|
338
|
+
│ │
|
|
339
|
+
│ └── pool_integration.go # Railway 项目池集成
|
|
340
|
+
│
|
|
341
|
+
├── common/railway-go/pkg/railway/
|
|
342
|
+
│ ├── token.go # Token 管理
|
|
343
|
+
│ │ └─ CreateProjectToken() # 封装 createProjectTokenRaw()
|
|
344
|
+
│ │
|
|
345
|
+
│ ├── graphql_raw.go # GraphQL 底层调用
|
|
346
|
+
│ │ └─ createProjectTokenRaw() # GraphQL mutation
|
|
347
|
+
│ │
|
|
348
|
+
│ └── service.go # Service 管理
|
|
349
|
+
│ ├─ CreateService()
|
|
350
|
+
│ ├─ DeleteService()
|
|
351
|
+
│ └─ DeployTemplateWithConfig() # 部署 template(如 postgres)
|
|
352
|
+
│
|
|
353
|
+
└── internal/infra/gen/model/
|
|
354
|
+
└── platform_project.gen.go # PlatformProject 数据模型
|
|
355
|
+
├─ PlatformToken # 存储 project token
|
|
356
|
+
├─ PlatformTokenName
|
|
357
|
+
└─ TokenExpiresAt
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 5.2 Client (Ruby)
|
|
361
|
+
|
|
362
|
+
```
|
|
363
|
+
openclacky/
|
|
364
|
+
├── lib/clacky/
|
|
365
|
+
│ ├── deploy_api_client.rb # Backend API 客户端
|
|
366
|
+
│ │ ├─ create_task() # POST /deploy/create-task
|
|
367
|
+
│ │ └─ services() # GET /deploy/services (轮询)
|
|
368
|
+
│ │
|
|
369
|
+
│ └── default_skills/deploy/
|
|
370
|
+
│ ├── SKILL.md # Deploy skill 入口
|
|
371
|
+
│ │
|
|
372
|
+
│ ├── templates/
|
|
373
|
+
│ │ └── rails_deploy.rb # Rails 固定脚本
|
|
374
|
+
│ │
|
|
375
|
+
│ ├── subagent/
|
|
376
|
+
│ │ └── DEPLOY_ROLE.md # Generic 部署 subagent
|
|
377
|
+
│ │
|
|
378
|
+
│ └── tools/
|
|
379
|
+
│ ├── set_deploy_variables.rb # 注入环境变量
|
|
380
|
+
│ │ ├─ set_batch() # 批量设置(使用 system())
|
|
381
|
+
│ │ └─ set_one() # 单个设置(使用 system())
|
|
382
|
+
│ │
|
|
383
|
+
│ ├── list_services.rb # 列出服务
|
|
384
|
+
│ ├── execute_deployment.rb # 执行部署
|
|
385
|
+
│ ├── fetch_runtime_logs.rb # 获取日志
|
|
386
|
+
│ └── check_health.rb # 健康检查
|
|
387
|
+
│
|
|
388
|
+
└── docs/
|
|
389
|
+
├── deploy_subagent_design.md # Deploy skill 设计文档
|
|
390
|
+
└── deploy-architecture.md # 本文档
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## 6. 数据流示例
|
|
396
|
+
|
|
397
|
+
### 6.1 首次部署(创建 Project + 数据库)
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
1. 用户: "Deploy my Rails app"
|
|
401
|
+
↓
|
|
402
|
+
2. Deploy Skill 检测到 Rails 项目
|
|
403
|
+
↓
|
|
404
|
+
3. Rails Template 执行 Step 1-3(检查服务、设置变量)
|
|
405
|
+
↓
|
|
406
|
+
4. Step 4: 客户端调用 Backend API
|
|
407
|
+
POST /deploy/create-task
|
|
408
|
+
{
|
|
409
|
+
project_id: "abc123",
|
|
410
|
+
backup_db: false,
|
|
411
|
+
env_vars: {},
|
|
412
|
+
region: "us-west2"
|
|
413
|
+
}
|
|
414
|
+
↓
|
|
415
|
+
5. Backend 处理:
|
|
416
|
+
a. EnsurePlatformProject("abc123")
|
|
417
|
+
- Railway CreateProject() [使用 master token]
|
|
418
|
+
- CreateProjectToken(projectID, envID, tokenName)
|
|
419
|
+
- 存储 platform_project 记录
|
|
420
|
+
|
|
421
|
+
b. deployMiddleware("postgres")
|
|
422
|
+
- DeployTemplateWithConfig("postgres") [使用 master token]
|
|
423
|
+
- 保存 deploy_service 记录(status: initializing)
|
|
424
|
+
|
|
425
|
+
c. 返回响应:
|
|
426
|
+
{
|
|
427
|
+
deploy_task_id: "task-xyz",
|
|
428
|
+
deploy_service_id: "svc-123",
|
|
429
|
+
platform_token: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", # ← Project Token
|
|
430
|
+
platform_project_id: "railway-proj-456",
|
|
431
|
+
platform_environment_id: "railway-env-789"
|
|
432
|
+
}
|
|
433
|
+
↓
|
|
434
|
+
6. 客户端轮询:
|
|
435
|
+
loop do
|
|
436
|
+
services = GET /deploy/services?deploy_task_id=task-xyz
|
|
437
|
+
postgres = services.find { |s| s.type == 'postgres' }
|
|
438
|
+
break if postgres.status == 'SUCCESS'
|
|
439
|
+
sleep 5
|
|
440
|
+
end
|
|
441
|
+
↓
|
|
442
|
+
7. Postgres 就绪后,客户端注入 DATABASE_URL:
|
|
443
|
+
env = { "RAILWAY_TOKEN" => platform_token }
|
|
444
|
+
system(env,
|
|
445
|
+
"railway", "variables", "--service", "main-app",
|
|
446
|
+
"--set", "DATABASE_URL=${{Postgres-abc.DATABASE_PUBLIC_URL}}",
|
|
447
|
+
in: :close, out: File::NULL)
|
|
448
|
+
↓
|
|
449
|
+
8. 继续执行 Rails Template 的 Step 5-8
|
|
450
|
+
- 执行部署
|
|
451
|
+
- 运行 db:migrate
|
|
452
|
+
- 运行 db:seed(首次)
|
|
453
|
+
- 健康检查
|
|
454
|
+
- 报告成功
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### 6.2 后续部署(Project 已存在)
|
|
458
|
+
|
|
459
|
+
```
|
|
460
|
+
1. 用户: "Deploy again"
|
|
461
|
+
↓
|
|
462
|
+
2. Rails Template 执行
|
|
463
|
+
↓
|
|
464
|
+
3. Step 4: POST /deploy/create-task (project_id 已存在)
|
|
465
|
+
↓
|
|
466
|
+
4. Backend:
|
|
467
|
+
- PlatformProject 已存在,跳过创建
|
|
468
|
+
- Postgres 已存在,跳过部署
|
|
469
|
+
- 直接返回现有 platform_token
|
|
470
|
+
↓
|
|
471
|
+
5. 客户端:
|
|
472
|
+
- services() 返回现有 Postgres(已是 SUCCESS)
|
|
473
|
+
- 跳过轮询,直接注入变量
|
|
474
|
+
- 执行部署
|
|
475
|
+
- 运行 db:migrate(跳过 db:seed)
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## 7. 优化机会(未实现)
|
|
481
|
+
|
|
482
|
+
### 7.1 Webhook 通知(已有基础设施)
|
|
483
|
+
|
|
484
|
+
**当前**: 客户端每 5 秒轮询一次 `/deploy/services`
|
|
485
|
+
|
|
486
|
+
**可优化**: 使用 Railway Webhook 主动通知
|
|
487
|
+
|
|
488
|
+
```ruby
|
|
489
|
+
# Backend 已经创建了 webhook:
|
|
490
|
+
webhook, err := cli.GetClient().CreateWebhook(ctx, pp.ID, webhookURL, nil)
|
|
491
|
+
|
|
492
|
+
# 可以实现:
|
|
493
|
+
# 1. Railway → Backend Webhook: "Postgres deployment completed"
|
|
494
|
+
# 2. Backend → SSE / WebSocket: 推送给客户端
|
|
495
|
+
# 3. 客户端无需轮询,立即收到通知
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
**优点**:
|
|
499
|
+
- 减少 API 调用量
|
|
500
|
+
- 更快的响应时间
|
|
501
|
+
- 更好的用户体验
|
|
502
|
+
|
|
503
|
+
### 7.2 缩短轮询间隔
|
|
504
|
+
|
|
505
|
+
```ruby
|
|
506
|
+
# 当前: 每 5 秒轮询
|
|
507
|
+
sleep 5
|
|
508
|
+
|
|
509
|
+
# 可改为: 每 2 秒轮询(更快反馈)
|
|
510
|
+
sleep 2
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### 7.3 并行操作
|
|
514
|
+
|
|
515
|
+
```ruby
|
|
516
|
+
# 当前: 串行执行
|
|
517
|
+
# Step 4a: 创建数据库
|
|
518
|
+
# Step 4b: 等待数据库就绪
|
|
519
|
+
# Step 4c: 注入变量
|
|
520
|
+
|
|
521
|
+
# 可优化: 部分并行
|
|
522
|
+
# Thread 1: 监控数据库状态
|
|
523
|
+
# Thread 2: 准备其他环境变量
|
|
524
|
+
# 数据库就绪后,立即注入所有变量
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## 8. 故障排查指南
|
|
530
|
+
|
|
531
|
+
### 8.1 部署挂起在 Step 3(变量注入)
|
|
532
|
+
|
|
533
|
+
**症状**: 执行 `railway variables --set` 时无响应
|
|
534
|
+
|
|
535
|
+
**原因**: 使用了 `Open3.capture3`
|
|
536
|
+
|
|
537
|
+
**解决**:
|
|
538
|
+
```ruby
|
|
539
|
+
# 修改 set_deploy_variables.rb
|
|
540
|
+
# 将 Open3.capture3 改为 system()
|
|
541
|
+
success = system(env, *cmd, in: :close, out: File::NULL)
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### 8.2 无法创建 Project Token
|
|
545
|
+
|
|
546
|
+
**症状**: Backend 返回 "failed to create project token"
|
|
547
|
+
|
|
548
|
+
**检查**:
|
|
549
|
+
1. Railway master token 是否有效
|
|
550
|
+
2. Railway API 是否可达
|
|
551
|
+
3. 是否达到 token 数量限制
|
|
552
|
+
|
|
553
|
+
**日志位置**:
|
|
554
|
+
```
|
|
555
|
+
cloud_backend/internal/deployment/service/ensure.go:85
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### 8.3 Postgres 部署失败
|
|
559
|
+
|
|
560
|
+
**症状**: 轮询超时,Postgres 状态一直是 `initializing`
|
|
561
|
+
|
|
562
|
+
**检查**:
|
|
563
|
+
1. Railway 账户余额
|
|
564
|
+
2. 区域是否支持 Postgres
|
|
565
|
+
3. Backend 日志中的重试信息
|
|
566
|
+
|
|
567
|
+
**相关代码**:
|
|
568
|
+
```
|
|
569
|
+
cloud_backend/internal/deployment/service/middleware.go
|
|
570
|
+
- deployMiddlewareWithRetry() # 最多重试 3 次
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### 8.4 DATABASE_URL 注入失败
|
|
574
|
+
|
|
575
|
+
**症状**: 主服务找不到数据库连接
|
|
576
|
+
|
|
577
|
+
**检查**:
|
|
578
|
+
1. Postgres 服务名称是否正确
|
|
579
|
+
2. Railway 变量引用语法: `${{Service-Name.VARIABLE}}`
|
|
580
|
+
3. Project token 是否有效
|
|
581
|
+
|
|
582
|
+
**调试命令**:
|
|
583
|
+
```bash
|
|
584
|
+
# 手动检查变量
|
|
585
|
+
export RAILWAY_TOKEN=<project_token>
|
|
586
|
+
railway variables --service main-app --json
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## 9. 参考资料
|
|
592
|
+
|
|
593
|
+
### 9.1 官方文档
|
|
594
|
+
|
|
595
|
+
- [Railway Public API](https://docs.railway.com/integrations/api)
|
|
596
|
+
- [Railway Project Tokens](https://docs.railway.com/integrations/api#project-tokens)
|
|
597
|
+
|
|
598
|
+
### 9.2 非官方 SDK
|
|
599
|
+
|
|
600
|
+
- [railway-sdk (TypeScript)](https://github.com/crisog/railway-sdk) - 包含详细的 token 权限矩阵
|
|
601
|
+
|
|
602
|
+
### 9.3 内部文档
|
|
603
|
+
|
|
604
|
+
- `openclacky/docs/deploy_subagent_design.md` - Deploy skill 设计
|
|
605
|
+
- `openclacky/docs/openclacky_cloud_api_reference.md` - License API 参考
|
|
606
|
+
- `~/.clacky/memories/deploy-railway-cli-system-call.md` - Railway CLI 调用问题和解决方案
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## 10. 更新历史
|
|
611
|
+
|
|
612
|
+
| 日期 | 版本 | 变更说明 |
|
|
613
|
+
|------|------|---------|
|
|
614
|
+
| 2026-04-11 | 1.0.0 | 初始版本,包含完整架构说明、token 权限矩阵、安全分析 |
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
**维护者**: OpenClacky Team
|
|
619
|
+
**反馈**: 如发现文档有误或需要补充,请提 Issue 或 PR
|
|
@@ -165,10 +165,9 @@ module Clacky
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
# Show compression info (use estimated tokens from rebuilt history)
|
|
168
|
-
@
|
|
169
|
-
"History compressed (~#{compression_context[:original_token_count]} -> ~#{@history.estimate_tokens} tokens, " \
|
|
168
|
+
compression_summary = "History compressed (~#{compression_context[:original_token_count]} -> ~#{@history.estimate_tokens} tokens, " \
|
|
170
169
|
"level #{compression_context[:compression_level]})"
|
|
171
|
-
)
|
|
170
|
+
@ui&.show_idle_status(phase: :end, message: compression_summary)
|
|
172
171
|
end
|
|
173
172
|
|
|
174
173
|
# Get recent messages while preserving tool_calls/tool_results pairs.
|