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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/docs/deploy-architecture.md +619 -0
  4. data/lib/clacky/agent/message_compressor_helper.rb +2 -3
  5. data/lib/clacky/agent/skill_manager.rb +2 -2
  6. data/lib/clacky/brand_config.rb +4 -3
  7. data/lib/clacky/clacky_auth_client.rb +152 -0
  8. data/lib/clacky/clacky_cloud_config.rb +123 -0
  9. data/lib/clacky/client.rb +5 -4
  10. data/lib/clacky/cloud_project_client.rb +169 -0
  11. data/lib/clacky/default_agents/base_prompt.md +1 -0
  12. data/lib/clacky/default_parsers/doc_parser.rb +9 -9
  13. data/lib/clacky/default_skills/browser-setup/SKILL.md +9 -0
  14. data/lib/clacky/default_skills/channel-setup/SKILL.md +21 -4
  15. data/lib/clacky/default_skills/channel-setup/feishu_setup.rb +8 -2
  16. data/lib/clacky/default_skills/deploy/SKILL.md +96 -5
  17. data/lib/clacky/default_skills/deploy/scripts/rails_deploy.rb +1268 -274
  18. data/lib/clacky/default_skills/deploy/tools/create_database_service.rb +341 -0
  19. data/lib/clacky/default_skills/deploy/tools/execute_deployment.rb +72 -147
  20. data/lib/clacky/default_skills/deploy/tools/fetch_runtime_logs.rb +60 -50
  21. data/lib/clacky/default_skills/deploy/tools/list_services.rb +47 -60
  22. data/lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb +147 -96
  23. data/lib/clacky/default_skills/new/SKILL.md +117 -5
  24. data/lib/clacky/default_skills/new/scripts/cloud_project_init.sh +74 -0
  25. data/lib/clacky/default_skills/new/scripts/create_rails_project.sh +32 -0
  26. data/lib/clacky/deploy_api_client.rb +484 -0
  27. data/lib/clacky/message_format/bedrock.rb +3 -2
  28. data/lib/clacky/server/browser_manager.rb +3 -1
  29. data/lib/clacky/server/channel/adapters/feishu/ws_client.rb +2 -1
  30. data/lib/clacky/server/http_server.rb +12 -2
  31. data/lib/clacky/server/server_master.rb +43 -7
  32. data/lib/clacky/skill.rb +6 -2
  33. data/lib/clacky/tools/run_project.rb +4 -1
  34. data/lib/clacky/tools/shell.rb +7 -1
  35. data/lib/clacky/utils/arguments_parser.rb +22 -5
  36. data/lib/clacky/version.rb +1 -1
  37. data/lib/clacky/web/app.css +37 -5
  38. data/lib/clacky/web/app.js +99 -10
  39. data/lib/clacky/web/sessions.js +48 -8
  40. data/lib/clacky/web/skills.js +8 -2
  41. data/lib/clacky.rb +3 -0
  42. metadata +8 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e9d5f6bf142facc899ab2087b5f245e1e3b7aa5135548a42455fea73e685def
4
- data.tar.gz: f3d3251db1a6245ff9bdbe37433c9e70409c1975c718f18a68b7af82d17a38a4
3
+ metadata.gz: de10c61ed7d2c530c9bdacae8ddc625cfb969d98316f51f45e1833e87ed9b0cb
4
+ data.tar.gz: 42787ba0901cfc544fde90b46e1a2b4ffe07ce6636bcccbf364141dce3fe0515
5
5
  SHA512:
6
- metadata.gz: a300098516c8081374fa6d55a21f4884f8d70b43c12b1283643a86e03fdb81d96f765a81bc14e703b6e4584bbe3fec1a7b08eadb5488ff5de2223fda922ca9e8
7
- data.tar.gz: 64d73189f3852cf6fc064a61d065e762c6ba2e32d4d254736aebdcaff2106be2b55ea3ad349a1898778460e17142a06977250d8053f7db6bfe5525093f701b55
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
- @ui&.show_info(
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.