@aion0/forge 0.1.0
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.
- package/CLAUDE.md +4 -0
- package/README.md +264 -0
- package/app/api/auth/[...nextauth]/route.ts +3 -0
- package/app/api/claude/[id]/route.ts +31 -0
- package/app/api/claude/[id]/stream/route.ts +63 -0
- package/app/api/claude/route.ts +28 -0
- package/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/app/api/claude-sessions/sync/route.ts +17 -0
- package/app/api/flows/route.ts +6 -0
- package/app/api/flows/run/route.ts +19 -0
- package/app/api/notify/test/route.ts +33 -0
- package/app/api/projects/route.ts +7 -0
- package/app/api/sessions/[id]/chat/route.ts +64 -0
- package/app/api/sessions/[id]/messages/route.ts +9 -0
- package/app/api/sessions/[id]/route.ts +17 -0
- package/app/api/sessions/route.ts +20 -0
- package/app/api/settings/route.ts +15 -0
- package/app/api/status/route.ts +12 -0
- package/app/api/tasks/[id]/route.ts +36 -0
- package/app/api/tasks/[id]/stream/route.ts +77 -0
- package/app/api/tasks/link/route.ts +37 -0
- package/app/api/tasks/route.ts +43 -0
- package/app/api/tasks/session/route.ts +14 -0
- package/app/api/templates/route.ts +6 -0
- package/app/api/tunnel/route.ts +20 -0
- package/app/api/watchers/route.ts +33 -0
- package/app/globals.css +26 -0
- package/app/icon.svg +26 -0
- package/app/layout.tsx +17 -0
- package/app/login/page.tsx +61 -0
- package/app/page.tsx +9 -0
- package/cli/mw.ts +377 -0
- package/components/ChatPanel.tsx +191 -0
- package/components/ClaudeTerminal.tsx +267 -0
- package/components/Dashboard.tsx +270 -0
- package/components/MarkdownContent.tsx +57 -0
- package/components/NewSessionModal.tsx +93 -0
- package/components/NewTaskModal.tsx +456 -0
- package/components/ProjectList.tsx +108 -0
- package/components/SessionList.tsx +74 -0
- package/components/SessionView.tsx +655 -0
- package/components/SettingsModal.tsx +366 -0
- package/components/StatusBar.tsx +99 -0
- package/components/TaskBoard.tsx +110 -0
- package/components/TaskDetail.tsx +351 -0
- package/components/TunnelToggle.tsx +163 -0
- package/components/WebTerminal.tsx +1069 -0
- package/docs/LOCAL-DEPLOY.md +144 -0
- package/docs/roadmap-multi-agent-workflow.md +330 -0
- package/instrumentation.ts +14 -0
- package/lib/auth.ts +47 -0
- package/lib/claude-process.ts +352 -0
- package/lib/claude-sessions.ts +267 -0
- package/lib/cloudflared.ts +218 -0
- package/lib/flows.ts +86 -0
- package/lib/init.ts +82 -0
- package/lib/notify.ts +75 -0
- package/lib/password.ts +77 -0
- package/lib/projects.ts +86 -0
- package/lib/session-manager.ts +156 -0
- package/lib/session-watcher.ts +345 -0
- package/lib/settings.ts +44 -0
- package/lib/task-manager.ts +668 -0
- package/lib/telegram-bot.ts +912 -0
- package/lib/terminal-server.ts +70 -0
- package/lib/terminal-standalone.ts +363 -0
- package/middleware.ts +33 -0
- package/next-env.d.ts +6 -0
- package/next.config.ts +16 -0
- package/package.json +66 -0
- package/postcss.config.mjs +7 -0
- package/src/config/index.ts +119 -0
- package/src/core/db/database.ts +133 -0
- package/src/core/memory/strategy.ts +32 -0
- package/src/core/providers/chat.ts +65 -0
- package/src/core/providers/registry.ts +60 -0
- package/src/core/session/manager.ts +190 -0
- package/src/types/index.ts +128 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# 本地部署 — Mac 上运行,手机监控
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## 1. 手机访问本地 Mac 的方案
|
|
6
|
+
|
|
7
|
+
核心问题:你的 Mac 在局域网内,手机怎么访问?
|
|
8
|
+
|
|
9
|
+
### 方案对比
|
|
10
|
+
|
|
11
|
+
| 方案 | 原理 | 优点 | 缺点 |
|
|
12
|
+
|------|------|------|------|
|
|
13
|
+
| **同一 WiFi 局域网** | Mac 开 Web 服务,手机直接访问 `192.168.x.x:3000` | 零配置、零成本 | 只能在家用 |
|
|
14
|
+
| **Tailscale** | 虚拟局域网,任何网络下设备互通 | 免费、安全、无需公网 IP | 需要装客户端 |
|
|
15
|
+
| **Cloudflare Tunnel** | 免费内网穿透,给你一个公网域名 | 免费、HTTPS、不开端口 | 依赖 Cloudflare |
|
|
16
|
+
| **ngrok** | 临时隧道 | 一行命令搞定 | 免费版地址每次变 |
|
|
17
|
+
| **frp** | 自建内网穿透 | 完全自控 | 需要一台有公网 IP 的服务器 |
|
|
18
|
+
|
|
19
|
+
### 推荐组合:Tailscale(推荐) + 局域网(备选)
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────┐
|
|
23
|
+
│ 你的 Mac │
|
|
24
|
+
│ │
|
|
25
|
+
│ my-workflow server │
|
|
26
|
+
│ ├── REST API :3000 │
|
|
27
|
+
│ ├── WebSocket :3000/ws │
|
|
28
|
+
│ └── Dashboard :3000 │
|
|
29
|
+
│ │
|
|
30
|
+
│ Tailscale IP: 100.x.x.x │
|
|
31
|
+
│ 局域网 IP: 192.168.x.x │
|
|
32
|
+
└──────────┬──────────────────────┘
|
|
33
|
+
│
|
|
34
|
+
│ Tailscale 虚拟网络(加密)
|
|
35
|
+
│
|
|
36
|
+
┌──────────┴──────────────────────┐
|
|
37
|
+
│ 你的手机 │
|
|
38
|
+
│ │
|
|
39
|
+
│ 浏览器 → 100.x.x.x:3000 │ ← 任何网络下都能访问
|
|
40
|
+
│ 或 Safari → 192.168.x.x:3000 │ ← 同一 WiFi 下
|
|
41
|
+
│ │
|
|
42
|
+
└─────────────────────────────────┘
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Tailscale 的好处:**
|
|
46
|
+
- 免费(个人用户 100 台设备)
|
|
47
|
+
- Mac 和手机都装一个客户端就完了
|
|
48
|
+
- 在公司、咖啡厅、4G 网络下都能访问你家里的 Mac
|
|
49
|
+
- 加密传输,不暴露任何端口到公网
|
|
50
|
+
- 以后迁移到云服务器,只需要在服务器上装 Tailscale,手机地址改一下就行
|
|
51
|
+
|
|
52
|
+
### 快速开始
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 1. Mac 安装 Tailscale
|
|
56
|
+
brew install tailscale
|
|
57
|
+
# 或从 Mac App Store 安装
|
|
58
|
+
|
|
59
|
+
# 2. 登录
|
|
60
|
+
tailscale up
|
|
61
|
+
|
|
62
|
+
# 3. 查看你的 Tailscale IP
|
|
63
|
+
tailscale ip -4
|
|
64
|
+
# 输出: 100.64.x.x
|
|
65
|
+
|
|
66
|
+
# 4. 手机安装 Tailscale App,同一账号登录
|
|
67
|
+
|
|
68
|
+
# 5. 启动 my-workflow
|
|
69
|
+
mw server start
|
|
70
|
+
# → Dashboard: http://100.64.x.x:3000
|
|
71
|
+
|
|
72
|
+
# 手机浏览器打开这个地址即可
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Mac 不休眠配置
|
|
76
|
+
|
|
77
|
+
如果你想离开电脑后 agent 继续跑:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# 方法 1: 系统设置 → 电池 → 防止自动休眠(接电源时)
|
|
81
|
+
|
|
82
|
+
# 方法 2: 命令行临时阻止休眠
|
|
83
|
+
caffeinate -d -i -s &
|
|
84
|
+
|
|
85
|
+
# 方法 3: 用 launchd 注册为系统服务(推荐)
|
|
86
|
+
# 即使合盖,接着电源也不休眠
|
|
87
|
+
# 见下方 "注册为系统服务" 部分
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 注册为系统服务(开机自启 + 崩溃自重启)
|
|
91
|
+
|
|
92
|
+
```xml
|
|
93
|
+
<!-- ~/Library/LaunchAgents/com.zliu.my-workflow.plist -->
|
|
94
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
95
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
96
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
97
|
+
<plist version="1.0">
|
|
98
|
+
<dict>
|
|
99
|
+
<key>Label</key>
|
|
100
|
+
<string>com.zliu.my-workflow</string>
|
|
101
|
+
<key>ProgramArguments</key>
|
|
102
|
+
<array>
|
|
103
|
+
<string>/usr/local/bin/mw</string>
|
|
104
|
+
<string>server</string>
|
|
105
|
+
<string>start</string>
|
|
106
|
+
<string>--foreground</string>
|
|
107
|
+
</array>
|
|
108
|
+
<key>RunAtLoad</key>
|
|
109
|
+
<true/>
|
|
110
|
+
<key>KeepAlive</key>
|
|
111
|
+
<true/>
|
|
112
|
+
<key>StandardOutPath</key>
|
|
113
|
+
<string>/tmp/my-workflow.log</string>
|
|
114
|
+
<key>StandardErrorPath</key>
|
|
115
|
+
<string>/tmp/my-workflow.err</string>
|
|
116
|
+
</dict>
|
|
117
|
+
</plist>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 注册服务
|
|
122
|
+
launchctl load ~/Library/LaunchAgents/com.zliu.my-workflow.plist
|
|
123
|
+
|
|
124
|
+
# 查看状态
|
|
125
|
+
launchctl list | grep my-workflow
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 2. 未来迁移到云端
|
|
131
|
+
|
|
132
|
+
本地跑通后,迁移非常简单:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
本地开发阶段:
|
|
136
|
+
手机 → Tailscale → Mac:3000
|
|
137
|
+
|
|
138
|
+
迁移到云端:
|
|
139
|
+
手机 → Tailscale → VPS:3000 (只是 IP 变了)
|
|
140
|
+
或
|
|
141
|
+
手机 → https://workflow.yourdomain.com (Cloudflare Tunnel)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
不需要改任何代码,只是服务跑在不同的机器上。
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Multi-Agent Workflow (Roadmap)
|
|
2
|
+
|
|
3
|
+
> 让多个 Claude Code 实例协作完成复杂任务,相互传递信息和中间产物。
|
|
4
|
+
|
|
5
|
+
## 动机
|
|
6
|
+
|
|
7
|
+
当前的 Flow 系统是线性的:每个 step 独立运行一个 Claude Code,step 之间没有数据传递。真实的复杂工程场景需要:
|
|
8
|
+
|
|
9
|
+
- **Agent A** 做架构设计 → 把设计文档传给 **Agent B** 写代码 → **Agent C** review + 测试
|
|
10
|
+
- 多个 Agent 并行工作在不同模块,通过共享 artifact 协调
|
|
11
|
+
- Agent 完成后触发下游 Agent,形成 DAG(有向无环图)执行流
|
|
12
|
+
|
|
13
|
+
## 设计分析
|
|
14
|
+
|
|
15
|
+
### 核心概念
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Pipeline (一次执行实例)
|
|
19
|
+
└─ Workflow (YAML 定义)
|
|
20
|
+
└─ Node (一个 Agent 节点)
|
|
21
|
+
├─ inputs: 从上游节点或用户获取
|
|
22
|
+
├─ action: 运行 Claude Code prompt
|
|
23
|
+
├─ outputs: 产出 artifact(文本、文件、diff)
|
|
24
|
+
└─ routes: 根据条件决定下游节点
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**与现有系统的关系:**
|
|
28
|
+
- `Node` 对应现有的 `Task`(一次 Claude Code 执行)
|
|
29
|
+
- `Workflow` 扩展现有的 `Flow`(从线性 steps → DAG nodes)
|
|
30
|
+
- `Pipeline` 是新增的,表示一次 workflow 运行实例
|
|
31
|
+
|
|
32
|
+
### YAML 定义格式
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
name: feature-implementation
|
|
36
|
+
description: "从需求到 PR 的完整流程"
|
|
37
|
+
|
|
38
|
+
# 全局变量,所有节点可用
|
|
39
|
+
vars:
|
|
40
|
+
project: my-app
|
|
41
|
+
|
|
42
|
+
nodes:
|
|
43
|
+
architect:
|
|
44
|
+
project: "{{vars.project}}"
|
|
45
|
+
prompt: |
|
|
46
|
+
分析以下需求并输出技术方案文档(markdown),包括:
|
|
47
|
+
1. 架构设计
|
|
48
|
+
2. 需要修改的文件列表
|
|
49
|
+
3. 接口定义
|
|
50
|
+
需求:{{input.requirement}}
|
|
51
|
+
outputs:
|
|
52
|
+
- name: design_doc
|
|
53
|
+
extract: result # 从 Claude 的 result 中提取
|
|
54
|
+
|
|
55
|
+
implement:
|
|
56
|
+
project: "{{vars.project}}"
|
|
57
|
+
depends_on: [architect]
|
|
58
|
+
prompt: |
|
|
59
|
+
按照以下技术方案实现代码:
|
|
60
|
+
{{nodes.architect.outputs.design_doc}}
|
|
61
|
+
outputs:
|
|
62
|
+
- name: diff
|
|
63
|
+
extract: git_diff
|
|
64
|
+
|
|
65
|
+
review:
|
|
66
|
+
project: "{{vars.project}}"
|
|
67
|
+
depends_on: [implement]
|
|
68
|
+
prompt: |
|
|
69
|
+
Review 以下代码改动,检查:
|
|
70
|
+
1. 是否符合设计方案
|
|
71
|
+
2. 是否有 bug 或安全问题
|
|
72
|
+
3. 测试覆盖
|
|
73
|
+
|
|
74
|
+
设计方案:
|
|
75
|
+
{{nodes.architect.outputs.design_doc}}
|
|
76
|
+
|
|
77
|
+
代码改动:
|
|
78
|
+
{{nodes.implement.outputs.diff}}
|
|
79
|
+
outputs:
|
|
80
|
+
- name: review_result
|
|
81
|
+
extract: result
|
|
82
|
+
routes:
|
|
83
|
+
- condition: "{{outputs.review_result contains 'LGTM'}}"
|
|
84
|
+
next: create_pr
|
|
85
|
+
- condition: default
|
|
86
|
+
next: fix
|
|
87
|
+
|
|
88
|
+
fix:
|
|
89
|
+
project: "{{vars.project}}"
|
|
90
|
+
depends_on: [review]
|
|
91
|
+
prompt: |
|
|
92
|
+
根据 Review 意见修复代码:
|
|
93
|
+
{{nodes.review.outputs.review_result}}
|
|
94
|
+
routes:
|
|
95
|
+
- next: review # 循环回 review(需要设定最大循环次数)
|
|
96
|
+
max_iterations: 3
|
|
97
|
+
|
|
98
|
+
create_pr:
|
|
99
|
+
project: "{{vars.project}}"
|
|
100
|
+
depends_on: [review]
|
|
101
|
+
prompt: |
|
|
102
|
+
为当前改动创建 Pull Request,标题和描述基于:
|
|
103
|
+
{{nodes.architect.outputs.design_doc}}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 并行执行
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
nodes:
|
|
110
|
+
frontend:
|
|
111
|
+
project: my-app-web
|
|
112
|
+
prompt: "实现前端登录页面..."
|
|
113
|
+
|
|
114
|
+
backend:
|
|
115
|
+
project: my-app-api
|
|
116
|
+
prompt: "实现后端 auth API..."
|
|
117
|
+
|
|
118
|
+
integration:
|
|
119
|
+
depends_on: [frontend, backend] # 等两个都完成
|
|
120
|
+
project: my-app-web
|
|
121
|
+
prompt: |
|
|
122
|
+
前端和后端都已完成,进行集成:
|
|
123
|
+
前端改动:{{nodes.frontend.outputs.diff}}
|
|
124
|
+
后端改动:{{nodes.backend.outputs.diff}}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 数据传递机制
|
|
128
|
+
|
|
129
|
+
节点之间传递信息有三种方式:
|
|
130
|
+
|
|
131
|
+
| 方式 | 说明 | 适用场景 |
|
|
132
|
+
|------|------|----------|
|
|
133
|
+
| **Output Extraction** | 从 Claude 的 result/diff 中自动提取 | 文本结果、设计文档 |
|
|
134
|
+
| **File Artifact** | Claude 生成的文件保存到共享目录 | 代码文件、配置 |
|
|
135
|
+
| **Git State** | 通过 git branch/commit 传递代码改动 | 同一 repo 的代码接力 |
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
nodes:
|
|
139
|
+
generate_config:
|
|
140
|
+
prompt: "生成 API schema 文件到 shared/api-schema.json"
|
|
141
|
+
outputs:
|
|
142
|
+
- name: schema_file
|
|
143
|
+
type: file
|
|
144
|
+
path: "shared/api-schema.json"
|
|
145
|
+
|
|
146
|
+
use_config:
|
|
147
|
+
depends_on: [generate_config]
|
|
148
|
+
prompt: |
|
|
149
|
+
根据这个 schema 生成客户端代码:
|
|
150
|
+
{{read_file(nodes.generate_config.outputs.schema_file)}}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 实现方案(基于现有架构)
|
|
154
|
+
|
|
155
|
+
#### Phase 1:扩展 Flow → DAG
|
|
156
|
+
|
|
157
|
+
**改动点:**
|
|
158
|
+
|
|
159
|
+
1. **`src/types/index.ts`** — 新增类型:
|
|
160
|
+
```typescript
|
|
161
|
+
export interface WorkflowNode {
|
|
162
|
+
id: string;
|
|
163
|
+
project: string;
|
|
164
|
+
prompt: string; // 支持模板语法 {{...}}
|
|
165
|
+
dependsOn?: string[]; // 上游节点 ID
|
|
166
|
+
outputs?: NodeOutput[];
|
|
167
|
+
routes?: NodeRoute[];
|
|
168
|
+
maxIterations?: number; // 防止无限循环
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface NodeOutput {
|
|
172
|
+
name: string;
|
|
173
|
+
extract: 'result' | 'git_diff' | 'file';
|
|
174
|
+
path?: string; // for file type
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface NodeRoute {
|
|
178
|
+
condition: string; // 模板表达式
|
|
179
|
+
next: string; // 目标节点 ID
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface Workflow {
|
|
183
|
+
name: string;
|
|
184
|
+
description?: string;
|
|
185
|
+
vars?: Record<string, string>;
|
|
186
|
+
nodes: Record<string, WorkflowNode>;
|
|
187
|
+
input?: Record<string, string>; // 启动时需要的输入
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface Pipeline {
|
|
191
|
+
id: string;
|
|
192
|
+
workflowName: string;
|
|
193
|
+
status: 'running' | 'done' | 'failed' | 'cancelled';
|
|
194
|
+
input: Record<string, any>;
|
|
195
|
+
nodeStates: Record<string, PipelineNodeState>;
|
|
196
|
+
createdAt: string;
|
|
197
|
+
completedAt?: string;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface PipelineNodeState {
|
|
201
|
+
status: TaskStatus;
|
|
202
|
+
taskId?: string; // 关联的 Task ID
|
|
203
|
+
outputs: Record<string, any>;
|
|
204
|
+
iterations: number;
|
|
205
|
+
startedAt?: string;
|
|
206
|
+
completedAt?: string;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
2. **`lib/pipeline-engine.ts`** — 新增 Pipeline 执行引擎:
|
|
211
|
+
- `startPipeline(workflowName, input)` → 创建 Pipeline,解析 DAG,启动无依赖节点
|
|
212
|
+
- `onTaskComplete(taskId)` → 检查下游节点是否 ready,触发执行
|
|
213
|
+
- `resolveTemplate(template, context)` → 解析 `{{...}}` 模板
|
|
214
|
+
- `evaluateRoute(routes, outputs)` → 条件路由
|
|
215
|
+
- `extractOutput(task, outputDef)` → 从 Task 结果中提取 output
|
|
216
|
+
- 状态持久化到 SQLite `pipelines` 表
|
|
217
|
+
|
|
218
|
+
3. **`lib/task-manager.ts`** — 小改动:
|
|
219
|
+
- `createTask` 增加 `pipelineId` 和 `nodeId` 字段
|
|
220
|
+
- Task 完成时触发 `pipeline-engine.onTaskComplete`
|
|
221
|
+
|
|
222
|
+
4. **`lib/flows.ts`** — 兼容升级:
|
|
223
|
+
- 旧格式(线性 steps)自动转换为 DAG(每个 step depends_on 上一个)
|
|
224
|
+
- 新格式支持 `nodes` 字段
|
|
225
|
+
|
|
226
|
+
5. **UI 改动:**
|
|
227
|
+
- Pipeline 视图:DAG 可视化,显示节点状态和数据流
|
|
228
|
+
- 节点详情:点击查看 prompt(渲染后)、outputs、关联 task
|
|
229
|
+
- 启动 workflow 时可填入 input 参数
|
|
230
|
+
|
|
231
|
+
6. **API 路由:**
|
|
232
|
+
- `POST /api/pipelines` — 启动 pipeline
|
|
233
|
+
- `GET /api/pipelines` — 列表
|
|
234
|
+
- `GET /api/pipelines/[id]` — 详情(含 DAG 状态)
|
|
235
|
+
- `POST /api/pipelines/[id]/cancel` — 取消
|
|
236
|
+
|
|
237
|
+
7. **CLI:**
|
|
238
|
+
- `forge run <workflow> --input requirement="实现用户注册功能"`
|
|
239
|
+
- `forge pipeline <id>` — 查看 pipeline 状态
|
|
240
|
+
- `forge pipelines` — 列表
|
|
241
|
+
|
|
242
|
+
#### Phase 2:实时协作(高级)
|
|
243
|
+
|
|
244
|
+
更高级的场景——Agent 不是等上游完全结束才开始,而是通过消息通道实时交互:
|
|
245
|
+
|
|
246
|
+
```yaml
|
|
247
|
+
nodes:
|
|
248
|
+
coder:
|
|
249
|
+
prompt: "实现功能..."
|
|
250
|
+
channels:
|
|
251
|
+
- name: questions
|
|
252
|
+
direction: out
|
|
253
|
+
target: architect
|
|
254
|
+
|
|
255
|
+
architect:
|
|
256
|
+
prompt: "你是架构师,回答 coder 的问题..."
|
|
257
|
+
channels:
|
|
258
|
+
- name: questions
|
|
259
|
+
direction: in
|
|
260
|
+
source: coder
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
这需要:
|
|
264
|
+
- 两个 Claude Code 进程同时运行
|
|
265
|
+
- 中间消息总线(可基于 SQLite + polling 或 WebSocket)
|
|
266
|
+
- 一个 Agent 的 output 实时注入另一个 Agent 的 stdin(通过 `--resume` + append message)
|
|
267
|
+
|
|
268
|
+
**复杂度高,建议 Phase 1 稳定后再考虑。**
|
|
269
|
+
|
|
270
|
+
### 执行流程示意
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
User: forge run feature-implementation --input requirement="Add OAuth login"
|
|
274
|
+
│
|
|
275
|
+
▼
|
|
276
|
+
Pipeline created (id: pip-abc123)
|
|
277
|
+
│
|
|
278
|
+
▼ (resolve input template)
|
|
279
|
+
[architect] ──prompt──▶ Claude Code ──result──▶ design_doc
|
|
280
|
+
│
|
|
281
|
+
▼ (depends_on satisfied)
|
|
282
|
+
[implement] ──prompt(with design_doc)──▶ Claude Code ──git_diff──▶ diff
|
|
283
|
+
│
|
|
284
|
+
▼
|
|
285
|
+
[review] ──prompt(with design_doc + diff)──▶ Claude Code ──result──▶ review_result
|
|
286
|
+
│
|
|
287
|
+
├── "LGTM" ──▶ [create_pr]
|
|
288
|
+
│
|
|
289
|
+
└── else ──▶ [fix] ──▶ [review] (loop, max 3)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### SQLite Schema
|
|
293
|
+
|
|
294
|
+
```sql
|
|
295
|
+
CREATE TABLE pipelines (
|
|
296
|
+
id TEXT PRIMARY KEY,
|
|
297
|
+
workflow_name TEXT NOT NULL,
|
|
298
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
299
|
+
input TEXT, -- JSON
|
|
300
|
+
node_states TEXT, -- JSON: Record<nodeId, PipelineNodeState>
|
|
301
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
302
|
+
completed_at DATETIME
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
-- tasks 表增加:
|
|
306
|
+
ALTER TABLE tasks ADD COLUMN pipeline_id TEXT;
|
|
307
|
+
ALTER TABLE tasks ADD COLUMN node_id TEXT;
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 工作量评估
|
|
311
|
+
|
|
312
|
+
| 模块 | 工作量 | 说明 |
|
|
313
|
+
|------|--------|------|
|
|
314
|
+
| 类型定义 | 小 | 新增 Workflow/Pipeline/Node 类型 |
|
|
315
|
+
| YAML 解析 | 小 | 扩展现有 flows.ts |
|
|
316
|
+
| 模板引擎 | 中 | `{{...}}` 解析 + output 引用 |
|
|
317
|
+
| Pipeline 引擎 | 大 | DAG 调度、状态管理、条件路由 |
|
|
318
|
+
| Task Manager 集成 | 小 | 增加 pipeline/node 关联 |
|
|
319
|
+
| Pipeline UI | 大 | DAG 可视化、实时状态 |
|
|
320
|
+
| CLI 扩展 | 小 | 新增 pipeline 命令 |
|
|
321
|
+
| **总计** | **~3-5 天** | Phase 1 完整可用 |
|
|
322
|
+
|
|
323
|
+
### 关键设计决策
|
|
324
|
+
|
|
325
|
+
1. **模板语法**:用 `{{...}}` Mustache 风格,简单够用,不引入 Handlebars 依赖
|
|
326
|
+
2. **Output 提取**:默认取 Claude 的 `result` 字段;`git_diff` 取 task 的 diff;`file` 读指定路径
|
|
327
|
+
3. **循环保护**:`max_iterations` 默认 3,防止 review → fix 无限循环
|
|
328
|
+
4. **错误处理**:单节点失败 → 标记该节点 failed → 下游节点 skip → Pipeline 标记 failed
|
|
329
|
+
5. **并发**:同 project 的节点串行(现有 `runningProjects` 锁),不同 project 的节点并行
|
|
330
|
+
6. **向后兼容**:现有线性 Flow YAML 无需修改,自动适配
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js instrumentation — runs once when the server starts.
|
|
3
|
+
* Sets MW_PASSWORD before any request is handled.
|
|
4
|
+
*/
|
|
5
|
+
export async function register() {
|
|
6
|
+
// Only run on server, not Edge
|
|
7
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
8
|
+
const { getPassword } = await import('./lib/password');
|
|
9
|
+
const password = getPassword();
|
|
10
|
+
process.env.MW_PASSWORD = password;
|
|
11
|
+
console.log(`[init] Login password: ${password}`);
|
|
12
|
+
console.log('[init] Forgot password? Run: mw password');
|
|
13
|
+
}
|
|
14
|
+
}
|
package/lib/auth.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import NextAuth from 'next-auth';
|
|
2
|
+
import Google from 'next-auth/providers/google';
|
|
3
|
+
import Credentials from 'next-auth/providers/credentials';
|
|
4
|
+
|
|
5
|
+
export const { handlers, signIn, signOut, auth } = NextAuth({
|
|
6
|
+
providers: [
|
|
7
|
+
// Google OAuth — for production use
|
|
8
|
+
Google({
|
|
9
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
10
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
11
|
+
}),
|
|
12
|
+
// Local password — auto-generated, rotates daily
|
|
13
|
+
Credentials({
|
|
14
|
+
name: 'Local',
|
|
15
|
+
credentials: {
|
|
16
|
+
password: { label: 'Password', type: 'password' },
|
|
17
|
+
},
|
|
18
|
+
async authorize(credentials) {
|
|
19
|
+
// Dynamic import to avoid Edge Runtime pulling in node:path/node:fs
|
|
20
|
+
const { getPassword } = await import('./password');
|
|
21
|
+
const localPassword = getPassword();
|
|
22
|
+
if (credentials?.password === localPassword) {
|
|
23
|
+
return { id: 'local', name: 'zliu', email: 'local@my-workflow' };
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
},
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
pages: {
|
|
30
|
+
signIn: '/login',
|
|
31
|
+
},
|
|
32
|
+
callbacks: {
|
|
33
|
+
authorized({ auth }) {
|
|
34
|
+
return !!auth;
|
|
35
|
+
},
|
|
36
|
+
// Allow redirects to tunnel URLs (*.trycloudflare.com) after login
|
|
37
|
+
redirect({ url, baseUrl }) {
|
|
38
|
+
// Same origin — always allow
|
|
39
|
+
if (url.startsWith(baseUrl)) return url;
|
|
40
|
+
// Relative path — prepend base
|
|
41
|
+
if (url.startsWith('/')) return `${baseUrl}${url}`;
|
|
42
|
+
// Cloudflare tunnel URLs — allow
|
|
43
|
+
if (url.includes('.trycloudflare.com')) return url;
|
|
44
|
+
return baseUrl;
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|