@aion0/forge 0.2.10 → 0.2.11
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 +42 -38
- package/README.md +170 -92
- package/cli/mw.ts +154 -26
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -1,44 +1,43 @@
|
|
|
1
1
|
## Project: Forge (@aion0/forge)
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### Scripts
|
|
4
4
|
```bash
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
forge
|
|
5
|
+
# ── Start ──
|
|
6
|
+
./start.sh # production (kill old processes → build → start)
|
|
7
|
+
./start.sh dev # development (hot-reload)
|
|
8
|
+
forge-server # production via npm link/install
|
|
9
|
+
forge-server --dev # dev mode
|
|
10
|
+
forge-server --background # background, logs to ~/.forge/forge.log
|
|
11
|
+
forge-server --stop # stop background server
|
|
12
|
+
forge-server --restart # stop + start (safe for remote)
|
|
13
|
+
forge-server --rebuild # force rebuild
|
|
14
|
+
forge-server --port 4000 --terminal-port 4001 --dir ~/.forge-staging
|
|
15
|
+
forge-server --reset-terminal # kill terminal server (loses tmux attach)
|
|
16
|
+
forge-server --version # show version
|
|
17
|
+
|
|
18
|
+
# ── Test ──
|
|
19
|
+
./dev-test.sh # test instance (port 4000, data ~/.forge-test)
|
|
20
|
+
|
|
21
|
+
# ── Install ──
|
|
22
|
+
./install.sh # install from npm
|
|
23
|
+
./install.sh --local # install from local source (npm link + build)
|
|
24
|
+
|
|
25
|
+
# ── Publish ──
|
|
26
|
+
./publish.sh # bump patch version, commit, tag
|
|
27
|
+
./publish.sh minor # bump minor
|
|
28
|
+
./publish.sh 1.0.0 # explicit version
|
|
29
|
+
npm login && npm publish --access public --otp=<code>
|
|
30
|
+
|
|
31
|
+
# ── Monitor ──
|
|
32
|
+
./check-forge-status.sh # show process status + tmux sessions
|
|
33
|
+
|
|
34
|
+
# ── CLI ──
|
|
35
|
+
forge # help
|
|
36
|
+
forge --version # show version
|
|
37
|
+
forge password # show today's login password
|
|
38
|
+
forge tasks # list tasks
|
|
36
39
|
forge task <project> "prompt" # submit task
|
|
37
|
-
|
|
38
|
-
# Terminal server runs on port 3001 (auto-started by Next.js)
|
|
39
|
-
# Data directory: ~/.forge/
|
|
40
|
-
# Config: ~/.forge/settings.yaml
|
|
41
|
-
# Env: ~/.forge/.env.local
|
|
40
|
+
forge watch <id> # live stream task output
|
|
42
41
|
```
|
|
43
42
|
|
|
44
43
|
### Key Paths
|
|
@@ -46,7 +45,12 @@ forge task <project> "prompt" # submit task
|
|
|
46
45
|
- npm package: `@aion0/forge`
|
|
47
46
|
- GitHub: `github.com/aiwatching/forge`
|
|
48
47
|
|
|
48
|
+
### Architecture
|
|
49
|
+
- `forge-server.mjs` starts: Next.js + terminal-standalone + telegram-standalone
|
|
50
|
+
- `pnpm dev` / `start.sh dev` starts: Next.js (init.ts spawns terminal + telegram)
|
|
51
|
+
- `FORGE_EXTERNAL_SERVICES=1` → init.ts skips spawning (forge-server manages them)
|
|
52
|
+
|
|
49
53
|
## Obsidian Vault
|
|
50
54
|
Location: /Users/zliu/MyDocuments/obsidian-project/Projects/Bastion
|
|
51
55
|
When I ask about my notes, use bash to search and read files from this directory.
|
|
52
|
-
Example: find /Users/zliu/MyDocuments/obsidian-project -name "*.md" | head -20
|
|
56
|
+
Example: find /Users/zliu/MyDocuments/obsidian-project -name "*.md" | head -20
|
package/README.md
CHANGED
|
@@ -1,73 +1,72 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="app/icon.svg" width="80" height="80" alt="Forge">
|
|
2
|
+
<img src="app/icon.svg" width="80" height="80" alt="Forge - Self-hosted Vibe Coding Platform">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Forge</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>Self-hosted Vibe Coding platform — browser terminal, task orchestration, remote access</strong>
|
|
8
|
+
<strong>Self-hosted Vibe Coding platform for Claude Code — browser terminal, AI task orchestration, remote access from any device</strong>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="https://www.npmjs.com/package/@aion0/forge"><img src="https://img.shields.io/npm/v/@aion0/forge" alt="npm"></a>
|
|
13
|
-
<a href="https://github.com/aiwatching/forge/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@aion0/forge" alt="license"></a>
|
|
14
|
-
<a href="https://github.com/aiwatching/forge"><img src="https://img.shields.io/github/stars/aiwatching/forge?style=social" alt="stars"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@aion0/forge"><img src="https://img.shields.io/npm/v/@aion0/forge" alt="npm version"></a>
|
|
13
|
+
<a href="https://github.com/aiwatching/forge/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@aion0/forge" alt="MIT license"></a>
|
|
14
|
+
<a href="https://github.com/aiwatching/forge"><img src="https://img.shields.io/github/stars/aiwatching/forge?style=social" alt="GitHub stars"></a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
|
-
<a href="#installation">Install</a> · <a href="#features">Features</a> · <a href="#quick-start">Quick Start</a> · <a href="#telegram-bot">Telegram</a> · <a href="#configuration">Config</a> · <a href="#roadmap">Roadmap</a>
|
|
18
|
+
<a href="#installation">Install</a> · <a href="#features">Features</a> · <a href="#quick-start">Quick Start</a> · <a href="#telegram-bot">Telegram</a> · <a href="#scripts">Scripts</a> · <a href="#configuration">Config</a> · <a href="#roadmap">Roadmap</a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
## What is Forge?
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Forge is a self-hosted web platform that turns [Claude Code](https://docs.anthropic.com/en/docs/claude-code) into a remote-accessible vibe coding environment. It provides a persistent browser-based terminal (powered by tmux), background AI task orchestration, one-click Cloudflare Tunnel for remote access, and a Telegram bot for mobile control.
|
|
26
|
+
|
|
27
|
+
**Use cases:**
|
|
28
|
+
- Vibe code from your iPad, phone, or any browser — Claude Code runs on your machine, you access it from anywhere
|
|
29
|
+
- Submit AI coding tasks that run in the background while you sleep
|
|
30
|
+
- Chain multiple Claude Code instances into automated pipelines (design → implement → review → deploy)
|
|
31
|
+
- Browse and search your Obsidian notes with an AI assistant
|
|
32
|
+
|
|
33
|
+
**No API keys required.** Forge uses your existing Claude Code CLI subscription. Your code never leaves your machine.
|
|
26
34
|
|
|
27
35
|
## Features
|
|
28
36
|
|
|
29
|
-
| Feature |
|
|
37
|
+
| Feature | What it does |
|
|
30
38
|
|---------|-------------|
|
|
31
|
-
| **Vibe Coding** | Browser-based tmux terminal
|
|
32
|
-
| **
|
|
33
|
-
| **
|
|
34
|
-
| **
|
|
35
|
-
| **
|
|
36
|
-
| **
|
|
37
|
-
| **
|
|
38
|
-
| **
|
|
39
|
-
| **
|
|
40
|
-
| **
|
|
39
|
+
| **Vibe Coding** | Browser-based tmux terminal with multiple tabs. Sessions persist across page refresh, browser close, and server restart. Access from any device via Cloudflare Tunnel. |
|
|
40
|
+
| **AI Task Queue** | Submit prompts to Claude Code that run in the background. Live streaming output, cost tracking, session continuity across tasks. Supports `--dangerously-skip-permissions` for fully autonomous operation. |
|
|
41
|
+
| **Pipeline Engine** | Define multi-step DAG workflows in YAML. Chain Claude Code tasks with dependencies, output passing between steps, conditional routing, and parallel execution. Visual drag-and-drop editor included. |
|
|
42
|
+
| **Remote Access** | One-click Cloudflare Tunnel generates a secure public URL. Zero config, no Cloudflare account needed. Auto-health-check with reconnection. |
|
|
43
|
+
| **Docs Viewer** | Render Obsidian vaults and markdown directories in the browser. Built-in Claude Console for AI-assisted note-taking and research. Image support. |
|
|
44
|
+
| **Project Manager** | Browse project files, view code with syntax highlighting, git status/commit/push/pull, commit history — all from the browser. Multi-repo support. |
|
|
45
|
+
| **Demo Preview** | Preview local dev servers (Vite, Next.js, etc.) through dedicated Cloudflare Tunnel URLs. Multiple simultaneous previews supported. |
|
|
46
|
+
| **Telegram Bot** | Create tasks, check status, control tunnel, take notes, get AI session summaries — all from your phone. Whitelist-protected. |
|
|
47
|
+
| **CLI** | Full command-line interface: `forge task`, `forge watch`, `forge status`, `forge password`, and more. |
|
|
48
|
+
| **Monitor** | Real-time dashboard showing process status (Next.js, Terminal, Telegram, Tunnel), tmux sessions, and system uptime. |
|
|
41
49
|
|
|
42
50
|
## Installation
|
|
43
51
|
|
|
44
|
-
### npm (recommended)
|
|
45
|
-
|
|
46
52
|
```bash
|
|
47
53
|
npm install -g @aion0/forge
|
|
48
54
|
forge-server
|
|
49
55
|
```
|
|
50
56
|
|
|
57
|
+
Open `http://localhost:3000` — a login password is printed in the console.
|
|
58
|
+
|
|
51
59
|
### From source
|
|
52
60
|
|
|
53
61
|
```bash
|
|
54
62
|
git clone https://github.com/aiwatching/forge.git
|
|
55
63
|
cd forge
|
|
56
64
|
pnpm install
|
|
57
|
-
|
|
65
|
+
./start.sh # production
|
|
66
|
+
./start.sh dev # development with hot-reload
|
|
58
67
|
```
|
|
59
68
|
|
|
60
|
-
###
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
forge-server # Production (auto-builds if needed)
|
|
64
|
-
forge-server --dev # Development with hot-reload
|
|
65
|
-
forge-server --background # Run in background, logs to ~/.forge/forge.log
|
|
66
|
-
forge-server --stop # Stop background server
|
|
67
|
-
forge-server --rebuild # Force rebuild
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Prerequisites
|
|
69
|
+
### Prerequisites
|
|
71
70
|
|
|
72
71
|
- **Node.js** >= 20
|
|
73
72
|
- **tmux** — `brew install tmux` (macOS) / `apt install tmux` (Linux)
|
|
@@ -75,68 +74,133 @@ forge-server --rebuild # Force rebuild
|
|
|
75
74
|
|
|
76
75
|
## Quick Start
|
|
77
76
|
|
|
78
|
-
1. **Start Forge**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
2. **Open browser** → `http://localhost:3000`
|
|
85
|
-
|
|
86
|
-
3. **Log in** — password is auto-generated and printed in the console:
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
[init] Login password: a7x9k2 (valid today)
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Forgot it? Run `forge password`
|
|
93
|
-
|
|
94
|
-
4. **Configure projects** — Settings → add your project directories
|
|
95
|
-
|
|
96
|
-
5. **Start vibe coding** — open a terminal tab, run `claude`, and go
|
|
77
|
+
1. **Start Forge** — `forge-server` or `./start.sh`
|
|
78
|
+
2. **Open browser** — `http://localhost:3000`
|
|
79
|
+
3. **Log in** — password is in the console output, rotates daily. Run `forge password` if you forget.
|
|
80
|
+
4. **Configure** — Settings → add project directories and (optionally) Telegram bot token
|
|
81
|
+
5. **Start coding** — Open a terminal tab, run `claude`, and vibe
|
|
97
82
|
|
|
98
83
|
## Remote Access
|
|
99
84
|
|
|
100
|
-
Access Forge from anywhere —
|
|
85
|
+
Access Forge from anywhere — iPad, phone, coffee shop:
|
|
101
86
|
|
|
102
87
|
1. Click the **tunnel button** in the header
|
|
103
|
-
2.
|
|
104
|
-
3. Open
|
|
88
|
+
2. A temporary Cloudflare URL is generated (no account needed)
|
|
89
|
+
3. Open it on any device — protected by your daily rotating password
|
|
105
90
|
|
|
106
|
-
|
|
91
|
+
Health checks run every 60 seconds. If the tunnel drops, it auto-restarts and notifies you via Telegram.
|
|
107
92
|
|
|
108
93
|
## Telegram Bot
|
|
109
94
|
|
|
110
|
-
|
|
95
|
+
Mobile-first control for Forge. Create a bot via [@BotFather](https://t.me/botfather), add the token in Settings.
|
|
111
96
|
|
|
112
97
|
| Command | Description |
|
|
113
98
|
|---------|-------------|
|
|
114
99
|
| `/task` | Create a task (interactive project picker) |
|
|
115
100
|
| `/tasks` | List tasks with quick-action numbers |
|
|
116
|
-
| `/
|
|
101
|
+
| `/sessions` | AI summary of Claude Code sessions |
|
|
117
102
|
| `/docs` | Docs session summary or file search |
|
|
118
|
-
| `/note` | Quick note — sent to Docs Claude |
|
|
103
|
+
| `/note` | Quick note — sent to Docs Claude for filing |
|
|
119
104
|
| `/tunnel_start` | Start Cloudflare Tunnel |
|
|
120
105
|
| `/tunnel_stop` | Stop tunnel |
|
|
121
106
|
| `/tunnel_password <pw>` | Get login password + tunnel URL |
|
|
107
|
+
| `/watch` | Monitor session / list watchers |
|
|
108
|
+
|
|
109
|
+
Whitelist-protected — only configured Chat IDs can interact. Supports multiple users (comma-separated IDs).
|
|
110
|
+
|
|
111
|
+
## Pipeline Engine
|
|
112
|
+
|
|
113
|
+
Define multi-step AI workflows in YAML. Each step runs Claude Code autonomously, with outputs passed to downstream steps.
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
name: feature-build
|
|
117
|
+
description: "Design → Implement → Review"
|
|
118
|
+
input:
|
|
119
|
+
requirement: "Feature description"
|
|
120
|
+
vars:
|
|
121
|
+
project: my-app
|
|
122
|
+
nodes:
|
|
123
|
+
architect:
|
|
124
|
+
project: "{{vars.project}}"
|
|
125
|
+
prompt: "Analyze: {{input.requirement}}. Output a design doc."
|
|
126
|
+
outputs:
|
|
127
|
+
- name: design
|
|
128
|
+
extract: result
|
|
129
|
+
implement:
|
|
130
|
+
project: "{{vars.project}}"
|
|
131
|
+
depends_on: [architect]
|
|
132
|
+
prompt: "Implement: {{nodes.architect.outputs.design}}"
|
|
133
|
+
outputs:
|
|
134
|
+
- name: diff
|
|
135
|
+
extract: git_diff
|
|
136
|
+
review:
|
|
137
|
+
depends_on: [implement]
|
|
138
|
+
project: "{{vars.project}}"
|
|
139
|
+
prompt: "Review: {{nodes.implement.outputs.diff}}"
|
|
140
|
+
```
|
|
122
141
|
|
|
123
|
-
|
|
142
|
+
Features: DAG execution, parallel nodes, conditional routing, loop protection, Telegram notifications per step, visual editor.
|
|
124
143
|
|
|
125
144
|
## CLI
|
|
126
145
|
|
|
146
|
+
All commands are unified under `forge`:
|
|
147
|
+
|
|
127
148
|
```bash
|
|
149
|
+
# Server management
|
|
150
|
+
forge server start # Start server (foreground)
|
|
151
|
+
forge server start --background # Start in background
|
|
152
|
+
forge server start --dev # Development mode with hot-reload
|
|
153
|
+
forge server start --port 4000 # Custom port
|
|
154
|
+
forge server stop # Stop server
|
|
155
|
+
forge server restart # Restart (safe for remote use)
|
|
156
|
+
forge server rebuild # Force rebuild
|
|
157
|
+
|
|
158
|
+
# Tasks
|
|
128
159
|
forge task <project> <prompt> # Submit a task
|
|
129
|
-
forge tasks [status] # List tasks
|
|
130
|
-
forge watch <id> # Live stream output
|
|
131
|
-
forge status <id> # Task details + result
|
|
160
|
+
forge tasks [status] # List tasks (running|queued|done|failed)
|
|
161
|
+
forge watch <id> # Live stream task output
|
|
132
162
|
forge cancel <id> # Cancel a task
|
|
133
163
|
forge retry <id> # Retry a failed task
|
|
164
|
+
|
|
165
|
+
# Workflows
|
|
134
166
|
forge run <flow-name> # Run a YAML workflow
|
|
135
|
-
forge
|
|
167
|
+
forge flows # List available workflows
|
|
168
|
+
|
|
169
|
+
# Status & Info
|
|
170
|
+
forge status # Show process status + tmux sessions
|
|
171
|
+
forge status <id> # Show task details
|
|
136
172
|
forge password # Show login password
|
|
173
|
+
forge projects # List configured projects
|
|
174
|
+
forge -v # Show version
|
|
175
|
+
|
|
176
|
+
# Package management
|
|
177
|
+
forge upgrade # Update to latest npm version
|
|
178
|
+
forge uninstall # Stop server + uninstall (data preserved in ~/.forge)
|
|
137
179
|
```
|
|
138
180
|
|
|
139
|
-
Shortcuts: `t`=task, `ls`=tasks, `w`=watch, `s`=status, `f`=flows, `p`=projects, `pw`=password
|
|
181
|
+
Shortcuts: `t`=task, `ls`=tasks, `w`=watch, `s`=status, `l`=log, `f`=flows, `p`=projects, `pw`=password
|
|
182
|
+
|
|
183
|
+
### Server start options
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
forge server start --port 4000 # Custom web port (default: 3000)
|
|
187
|
+
forge server start --terminal-port 4001 # Custom terminal port (default: 3001)
|
|
188
|
+
forge server start --dir ~/.forge-test # Custom data directory
|
|
189
|
+
forge server start --background # Run in background
|
|
190
|
+
forge server start --reset-terminal # Kill terminal server on start
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Development scripts
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
./start.sh # kill old processes → build → start (production)
|
|
197
|
+
./start.sh dev # development with hot-reload
|
|
198
|
+
./dev-test.sh # test instance on port 4000 (separate data dir)
|
|
199
|
+
./install.sh # install from npm
|
|
200
|
+
./install.sh --local # install from local source
|
|
201
|
+
./publish.sh # bump version → commit → tag → ready to publish
|
|
202
|
+
./check-forge-status.sh # show all forge processes + tmux sessions
|
|
203
|
+
```
|
|
140
204
|
|
|
141
205
|
## Configuration
|
|
142
206
|
|
|
@@ -144,14 +208,16 @@ All data lives in `~/.forge/`:
|
|
|
144
208
|
|
|
145
209
|
```
|
|
146
210
|
~/.forge/
|
|
147
|
-
├── .env.local # Environment variables (
|
|
211
|
+
├── .env.local # Environment variables (AUTH_SECRET, API keys)
|
|
148
212
|
├── settings.yaml # Main configuration
|
|
149
|
-
├── password.json # Daily auto-generated password
|
|
150
|
-
├── data.db # SQLite database
|
|
213
|
+
├── password.json # Daily auto-generated login password
|
|
214
|
+
├── data.db # SQLite database (tasks, sessions)
|
|
151
215
|
├── terminal-state.json # Terminal tab layout
|
|
216
|
+
├── tunnel-state.json # Tunnel process state
|
|
152
217
|
├── preview.json # Demo preview config
|
|
218
|
+
├── pipelines/ # Pipeline execution state
|
|
153
219
|
├── flows/ # YAML workflow definitions
|
|
154
|
-
└── bin/ # Auto-downloaded binaries
|
|
220
|
+
└── bin/ # Auto-downloaded binaries (cloudflared)
|
|
155
221
|
```
|
|
156
222
|
|
|
157
223
|
<details>
|
|
@@ -169,6 +235,10 @@ telegramChatId: "" # Comma-separated for multiple users
|
|
|
169
235
|
telegramTunnelPassword: ""
|
|
170
236
|
notifyOnComplete: true
|
|
171
237
|
notifyOnFailure: true
|
|
238
|
+
taskModel: default # default / sonnet / opus / haiku
|
|
239
|
+
pipelineModel: default
|
|
240
|
+
telegramModel: sonnet
|
|
241
|
+
skipPermissions: false # Add --dangerously-skip-permissions to terminal claude invocations
|
|
172
242
|
```
|
|
173
243
|
|
|
174
244
|
</details>
|
|
@@ -190,45 +260,41 @@ AUTH_SECRET=<random-string>
|
|
|
190
260
|
## Architecture
|
|
191
261
|
|
|
192
262
|
```
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
│
|
|
196
|
-
│
|
|
197
|
-
│
|
|
198
|
-
│
|
|
199
|
-
|
|
200
|
-
│
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
├───────────┴───────────┴──────────────────────────┤
|
|
206
|
-
│ SQLite · Terminal Server · Cloudflare Tunnel │
|
|
207
|
-
└──────────────────────────────────────────────────┘
|
|
263
|
+
forge-server.mjs (single process)
|
|
264
|
+
├── Next.js (web dashboard + API)
|
|
265
|
+
│ ├── Vibe Coding (xterm.js + tmux)
|
|
266
|
+
│ ├── Docs Viewer (markdown + Claude Console)
|
|
267
|
+
│ ├── Project Manager (files + git)
|
|
268
|
+
│ ├── Task Queue (background Claude Code)
|
|
269
|
+
│ ├── Pipeline Engine (DAG workflows)
|
|
270
|
+
│ ├── Demo Preview (tunnel proxy)
|
|
271
|
+
│ └── Monitor (process status)
|
|
272
|
+
├── terminal-standalone.ts (WebSocket → tmux)
|
|
273
|
+
├── telegram-standalone.ts (Telegram Bot API polling)
|
|
274
|
+
└── cloudflared (Cloudflare Tunnel, on demand)
|
|
208
275
|
```
|
|
209
276
|
|
|
210
277
|
## Tech Stack
|
|
211
278
|
|
|
212
279
|
| Layer | Technology |
|
|
213
280
|
|-------|-----------|
|
|
214
|
-
| Frontend | Next.js 16, React 19, Tailwind CSS 4, xterm.js |
|
|
281
|
+
| Frontend | Next.js 16, React 19, Tailwind CSS 4, xterm.js, ReactFlow |
|
|
215
282
|
| Backend | Next.js Route Handlers, SQLite (better-sqlite3) |
|
|
216
283
|
| Terminal | node-pty, tmux, WebSocket |
|
|
217
284
|
| Auth | NextAuth v5 (daily rotating password + OAuth) |
|
|
218
285
|
| Tunnel | Cloudflare cloudflared (zero-config) |
|
|
219
286
|
| Bot | Telegram Bot API |
|
|
287
|
+
| Pipeline | YAML-based DAG engine with visual editor |
|
|
220
288
|
|
|
221
289
|
## Troubleshooting
|
|
222
290
|
|
|
223
291
|
<details>
|
|
224
292
|
<summary><strong>macOS: "fork failed: Device not configured"</strong></summary>
|
|
225
293
|
|
|
226
|
-
PTY device limit exhausted
|
|
294
|
+
PTY device limit exhausted:
|
|
227
295
|
|
|
228
296
|
```bash
|
|
229
297
|
sudo sysctl kern.tty.ptmx_max=2048
|
|
230
|
-
|
|
231
|
-
# Permanent
|
|
232
298
|
echo 'kern.tty.ptmx_max=2048' | sudo tee -a /etc/sysctl.conf
|
|
233
299
|
```
|
|
234
300
|
|
|
@@ -237,7 +303,7 @@ echo 'kern.tty.ptmx_max=2048' | sudo tee -a /etc/sysctl.conf
|
|
|
237
303
|
<details>
|
|
238
304
|
<summary><strong>Session cookie invalid after restart</strong></summary>
|
|
239
305
|
|
|
240
|
-
Fix
|
|
306
|
+
Fix AUTH_SECRET so it persists across restarts:
|
|
241
307
|
|
|
242
308
|
```bash
|
|
243
309
|
echo "AUTH_SECRET=$(openssl rand -hex 32)" >> ~/.forge/.env.local
|
|
@@ -245,13 +311,25 @@ echo "AUTH_SECRET=$(openssl rand -hex 32)" >> ~/.forge/.env.local
|
|
|
245
311
|
|
|
246
312
|
</details>
|
|
247
313
|
|
|
314
|
+
<details>
|
|
315
|
+
<summary><strong>Orphan processes after Ctrl+C</strong></summary>
|
|
316
|
+
|
|
317
|
+
Use `./start.sh` or `forge-server` which clean up old processes on start. Or manually:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
./check-forge-status.sh # see what's running
|
|
321
|
+
pkill -f 'telegram-standalone|terminal-standalone|next-server|cloudflared'
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
</details>
|
|
325
|
+
|
|
248
326
|
## Roadmap
|
|
249
327
|
|
|
250
|
-
- [ ] **Multi-Agent
|
|
251
|
-
- [ ] Pipeline UI — DAG visualization with real-time node status
|
|
328
|
+
- [ ] **Multi-Agent Collaboration** — Real-time message channels between concurrent Claude Code instances ([design doc](docs/roadmap-multi-agent-workflow.md))
|
|
252
329
|
- [ ] Additional bot platforms — Discord, Slack
|
|
253
330
|
- [ ] Excalidraw rendering in Docs viewer
|
|
254
|
-
- [ ] Multi-model chat (Anthropic, OpenAI, Google, xAI)
|
|
331
|
+
- [ ] Multi-model chat with API keys (Anthropic, OpenAI, Google, xAI)
|
|
332
|
+
- [ ] Plugin system for custom integrations
|
|
255
333
|
|
|
256
334
|
## Contributing
|
|
257
335
|
|
package/cli/mw.ts
CHANGED
|
@@ -112,10 +112,9 @@ async function main() {
|
|
|
112
112
|
break;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
case 'status':
|
|
116
115
|
case 's': {
|
|
117
116
|
const id = args[0];
|
|
118
|
-
if (!id) { console.log('Usage:
|
|
117
|
+
if (!id) { console.log('Usage: forge status <id>'); process.exit(1); }
|
|
119
118
|
const task = await api(`/api/tasks/${id}`);
|
|
120
119
|
console.log(`Task: ${task.id}`);
|
|
121
120
|
console.log(`Project: ${task.projectName} (${task.projectPath})`);
|
|
@@ -353,34 +352,163 @@ async function main() {
|
|
|
353
352
|
break;
|
|
354
353
|
}
|
|
355
354
|
|
|
355
|
+
case 'server': {
|
|
356
|
+
// Delegate to forge-server.mjs
|
|
357
|
+
const { execSync } = await import('node:child_process');
|
|
358
|
+
const { join, dirname } = await import('node:path');
|
|
359
|
+
const { fileURLToPath } = await import('node:url');
|
|
360
|
+
const serverScript = join(dirname(fileURLToPath(import.meta.url)), '..', 'bin', 'forge-server.mjs');
|
|
361
|
+
const sub = args[0] || 'start';
|
|
362
|
+
const serverArgs = args.slice(1);
|
|
363
|
+
|
|
364
|
+
const flagMap: Record<string, string[]> = {
|
|
365
|
+
'start': [],
|
|
366
|
+
'stop': ['--stop'],
|
|
367
|
+
'restart': ['--restart'],
|
|
368
|
+
'rebuild': ['--rebuild'],
|
|
369
|
+
'dev': ['--dev'],
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const flags = flagMap[sub] || [];
|
|
373
|
+
const allArgs = [...flags, ...serverArgs];
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
execSync(`node ${serverScript} ${allArgs.join(' ')}`, { stdio: 'inherit' });
|
|
377
|
+
} catch {}
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
case 'status': {
|
|
382
|
+
// If arg provided, show task details
|
|
383
|
+
if (args[0]) {
|
|
384
|
+
const task = await api(`/api/tasks/${args[0]}`);
|
|
385
|
+
console.log(`Task: ${task.id}`);
|
|
386
|
+
console.log(`Project: ${task.projectName} (${task.projectPath})`);
|
|
387
|
+
console.log(`Status: ${task.status}`);
|
|
388
|
+
console.log(`Prompt: ${task.prompt}`);
|
|
389
|
+
if (task.startedAt) console.log(`Started: ${task.startedAt}`);
|
|
390
|
+
if (task.completedAt) console.log(`Completed: ${task.completedAt}`);
|
|
391
|
+
if (task.costUSD != null) console.log(`Cost: $${task.costUSD.toFixed(4)}`);
|
|
392
|
+
if (task.error) console.log(`Error: ${task.error}`);
|
|
393
|
+
if (task.resultSummary) console.log(`\nResult:\n${task.resultSummary}`);
|
|
394
|
+
if (task.gitDiff) console.log(`\nGit Diff:\n${task.gitDiff.slice(0, 2000)}`);
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// No arg — show process status
|
|
399
|
+
const { execSync } = await import('node:child_process');
|
|
400
|
+
|
|
401
|
+
const check = (pattern: string) => {
|
|
402
|
+
try {
|
|
403
|
+
const out = execSync(`ps aux | grep '${pattern}' | grep -v grep | head -1`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
404
|
+
return out ? out.split(/\s+/)[1] : null;
|
|
405
|
+
} catch { return null; }
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const nextPid = check('next-server');
|
|
409
|
+
const termPid = check('terminal-standalone');
|
|
410
|
+
const telePid = check('telegram-standalone');
|
|
411
|
+
const tunnPid = check('cloudflared tunnel');
|
|
412
|
+
|
|
413
|
+
console.log('');
|
|
414
|
+
console.log(` ${nextPid ? '●' : '○'} Next.js ${nextPid ? `running (pid: ${nextPid})` : 'stopped'}`);
|
|
415
|
+
console.log(` ${termPid ? '●' : '○'} Terminal ${termPid ? `running (pid: ${termPid})` : 'stopped'}`);
|
|
416
|
+
console.log(` ${telePid ? '●' : '○'} Telegram ${telePid ? `running (pid: ${telePid})` : 'stopped'}`);
|
|
417
|
+
console.log(` ${tunnPid ? '●' : '○'} Tunnel ${tunnPid ? `running (pid: ${tunnPid})` : 'stopped'}`);
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
const { execSync: ex } = await import('node:child_process');
|
|
421
|
+
const sessions = ex("tmux list-sessions -F '#{session_name} #{session_attached}' 2>/dev/null", { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] })
|
|
422
|
+
.trim().split('\n').filter(l => l.startsWith('mw-'));
|
|
423
|
+
console.log(`\n Sessions: ${sessions.length}`);
|
|
424
|
+
for (const s of sessions) {
|
|
425
|
+
const [name, att] = s.split(' ');
|
|
426
|
+
console.log(` ${att !== '0' ? '●' : '○'} ${name}`);
|
|
427
|
+
}
|
|
428
|
+
} catch {
|
|
429
|
+
console.log('\n Sessions: 0');
|
|
430
|
+
}
|
|
431
|
+
console.log('');
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
case 'upgrade': {
|
|
436
|
+
const { execSync } = await import('node:child_process');
|
|
437
|
+
const { lstatSync } = await import('node:fs');
|
|
438
|
+
const { join, dirname } = await import('node:path');
|
|
439
|
+
const { fileURLToPath } = await import('node:url');
|
|
440
|
+
|
|
441
|
+
// Check if installed via npm link (symlink)
|
|
442
|
+
const cliDir = dirname(fileURLToPath(import.meta.url));
|
|
443
|
+
let isLinked = false;
|
|
444
|
+
try { isLinked = lstatSync(join(cliDir, '..')).isSymbolicLink(); } catch {}
|
|
445
|
+
|
|
446
|
+
if (isLinked) {
|
|
447
|
+
console.log('[forge] Installed via npm link (local source)');
|
|
448
|
+
console.log('[forge] Pull latest and rebuild:');
|
|
449
|
+
console.log(' cd ' + join(cliDir, '..'));
|
|
450
|
+
console.log(' git pull && pnpm install && pnpm build');
|
|
451
|
+
} else {
|
|
452
|
+
console.log('[forge] Upgrading from npm...');
|
|
453
|
+
try {
|
|
454
|
+
execSync('cd /tmp && npm install -g @aion0/forge', { stdio: 'inherit' });
|
|
455
|
+
console.log('[forge] Upgraded. Run: forge server restart');
|
|
456
|
+
} catch {
|
|
457
|
+
console.log('[forge] Upgrade failed');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
case 'uninstall': {
|
|
464
|
+
const { execSync } = await import('node:child_process');
|
|
465
|
+
console.log('[forge] Stopping server...');
|
|
466
|
+
try { execSync('forge server stop', { stdio: 'inherit' }); } catch {}
|
|
467
|
+
console.log('[forge] Uninstalling...');
|
|
468
|
+
try {
|
|
469
|
+
execSync('npm uninstall -g @aion0/forge', { stdio: 'inherit' });
|
|
470
|
+
console.log('[forge] Uninstalled. Data remains in ~/.forge/');
|
|
471
|
+
} catch {
|
|
472
|
+
console.log('[forge] Uninstall failed');
|
|
473
|
+
}
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
|
|
356
477
|
default:
|
|
357
478
|
console.log(`forge — Forge CLI (@aion0/forge)
|
|
358
479
|
|
|
359
480
|
Usage:
|
|
360
|
-
forge
|
|
361
|
-
forge
|
|
362
|
-
forge
|
|
363
|
-
forge
|
|
364
|
-
forge
|
|
365
|
-
|
|
366
|
-
forge
|
|
367
|
-
forge
|
|
368
|
-
forge
|
|
369
|
-
forge
|
|
370
|
-
forge
|
|
371
|
-
forge
|
|
372
|
-
forge
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
forge
|
|
379
|
-
|
|
380
|
-
forge
|
|
381
|
-
forge
|
|
382
|
-
|
|
383
|
-
|
|
481
|
+
forge server start [options] Start server (default: foreground)
|
|
482
|
+
forge server stop Stop server
|
|
483
|
+
forge server restart Restart server (safe for remote)
|
|
484
|
+
forge server dev Start in dev mode
|
|
485
|
+
forge server rebuild Force rebuild
|
|
486
|
+
|
|
487
|
+
forge task <project> <prompt> Submit a task
|
|
488
|
+
forge tasks [status] List tasks
|
|
489
|
+
forge watch <id> Live stream output
|
|
490
|
+
forge status [<id>] Process status / task details
|
|
491
|
+
forge log <id> Show execution log
|
|
492
|
+
forge cancel <id> Cancel a task
|
|
493
|
+
forge retry <id> Retry a failed task
|
|
494
|
+
|
|
495
|
+
forge run <flow> Run a workflow
|
|
496
|
+
forge flows List workflows
|
|
497
|
+
forge projects List projects
|
|
498
|
+
forge session [project] Show session info
|
|
499
|
+
forge password Show login password
|
|
500
|
+
|
|
501
|
+
forge upgrade Update to latest version
|
|
502
|
+
forge uninstall Remove forge
|
|
503
|
+
|
|
504
|
+
Options for 'forge server start':
|
|
505
|
+
--port 4000 Custom web port (default: 3000)
|
|
506
|
+
--terminal-port 4001 Custom terminal port (default: 3001)
|
|
507
|
+
--dir ~/.forge-staging Custom data directory
|
|
508
|
+
--background Run in background
|
|
509
|
+
--reset-terminal Kill terminal server on start
|
|
510
|
+
|
|
511
|
+
Shortcuts: t=task, ls=tasks, w=watch, s=status, l=log, f=flows, p=projects, pw=password`);
|
|
384
512
|
}
|
|
385
513
|
}
|
|
386
514
|
|
package/package.json
CHANGED