@aion0/forge 0.4.16 → 0.5.1
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/README.md +27 -2
- package/RELEASE_NOTES.md +21 -14
- package/app/api/agents/route.ts +17 -0
- package/app/api/delivery/[id]/route.ts +62 -0
- package/app/api/delivery/route.ts +40 -0
- package/app/api/mobile-chat/route.ts +13 -7
- package/app/api/monitor/route.ts +10 -6
- package/app/api/pipelines/[id]/route.ts +16 -3
- package/app/api/tasks/route.ts +2 -1
- package/app/api/workspace/[id]/agents/route.ts +35 -0
- package/app/api/workspace/[id]/memory/route.ts +23 -0
- package/app/api/workspace/[id]/smith/route.ts +22 -0
- package/app/api/workspace/[id]/stream/route.ts +28 -0
- package/app/api/workspace/route.ts +100 -0
- package/app/global-error.tsx +10 -4
- package/app/icon.ico +0 -0
- package/app/layout.tsx +2 -2
- package/app/login/LoginForm.tsx +96 -0
- package/app/login/page.tsx +7 -98
- package/app/page.tsx +2 -2
- package/bin/forge-server.mjs +13 -1
- package/check-forge-status.sh +9 -0
- package/components/ConversationEditor.tsx +411 -0
- package/components/ConversationGraphView.tsx +347 -0
- package/components/ConversationTerminalView.tsx +303 -0
- package/components/Dashboard.tsx +36 -39
- package/components/DashboardWrapper.tsx +9 -0
- package/components/DeliveryFlowEditor.tsx +491 -0
- package/components/DeliveryList.tsx +230 -0
- package/components/DeliveryWorkspace.tsx +589 -0
- package/components/DocTerminal.tsx +10 -2
- package/components/DocsViewer.tsx +10 -2
- package/components/HelpTerminal.tsx +11 -6
- package/components/InlinePipelineView.tsx +111 -0
- package/components/MobileView.tsx +20 -0
- package/components/MonitorPanel.tsx +9 -4
- package/components/NewTaskModal.tsx +32 -0
- package/components/PipelineEditor.tsx +49 -6
- package/components/PipelineView.tsx +482 -64
- package/components/ProjectDetail.tsx +314 -56
- package/components/ProjectManager.tsx +49 -4
- package/components/SessionView.tsx +27 -13
- package/components/SettingsModal.tsx +790 -124
- package/components/SkillsPanel.tsx +31 -8
- package/components/TaskBoard.tsx +3 -0
- package/components/WebTerminal.tsx +257 -43
- package/components/WorkspaceTree.tsx +221 -0
- package/components/WorkspaceView.tsx +2245 -0
- package/install.sh +2 -2
- package/lib/agents/claude-adapter.ts +104 -0
- package/lib/agents/generic-adapter.ts +64 -0
- package/lib/agents/index.ts +242 -0
- package/lib/agents/types.ts +70 -0
- package/lib/artifacts.ts +106 -0
- package/lib/delivery.ts +787 -0
- package/lib/forge-skills/forge-inbox.md +37 -0
- package/lib/forge-skills/forge-send.md +40 -0
- package/lib/forge-skills/forge-status.md +32 -0
- package/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/lib/help-docs/00-overview.md +7 -1
- package/lib/help-docs/01-settings.md +159 -2
- package/lib/help-docs/05-pipelines.md +89 -0
- package/lib/help-docs/07-projects.md +35 -1
- package/lib/help-docs/11-workspace.md +254 -0
- package/lib/help-docs/CLAUDE.md +7 -2
- package/lib/init.ts +60 -10
- package/lib/pipeline.ts +537 -1
- package/lib/settings.ts +115 -22
- package/lib/skills.ts +249 -372
- package/lib/task-manager.ts +113 -33
- package/lib/telegram-bot.ts +33 -1
- package/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/lib/workspace/agent-bus.ts +416 -0
- package/lib/workspace/agent-worker.ts +667 -0
- package/lib/workspace/backends/api-backend.ts +262 -0
- package/lib/workspace/backends/cli-backend.ts +479 -0
- package/lib/workspace/index.ts +82 -0
- package/lib/workspace/manager.ts +136 -0
- package/lib/workspace/orchestrator.ts +1914 -0
- package/lib/workspace/persistence.ts +310 -0
- package/lib/workspace/presets.ts +170 -0
- package/lib/workspace/skill-installer.ts +188 -0
- package/lib/workspace/smith-memory.ts +498 -0
- package/lib/workspace/types.ts +231 -0
- package/lib/workspace/watch-manager.ts +288 -0
- package/lib/workspace-standalone.ts +814 -0
- package/middleware.ts +1 -0
- package/next-env.d.ts +1 -1
- package/package.json +4 -1
- package/src/config/index.ts +12 -1
- package/src/core/db/database.ts +1 -0
- package/start.sh +7 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Workspace (Forge Smiths)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Workspace is a multi-agent orchestration system. You define a team of **Smiths** (agents) with roles, dependencies, and steps. Smiths run as long-lived daemons, communicate via a message bus, and can be controlled manually or automatically.
|
|
6
|
+
|
|
7
|
+
## Concepts
|
|
8
|
+
|
|
9
|
+
| Term | Description |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
| **Smith** | A long-running agent instance in the workspace |
|
|
12
|
+
| **Agent Profile** | A reusable configuration (CLI type + env vars + model) that can be assigned to any smith |
|
|
13
|
+
| **Message Bus** | Inter-agent communication system with two message categories |
|
|
14
|
+
| **Input Node** | User-provided requirements node (append-only history) |
|
|
15
|
+
| **Daemon** | Background execution loop that keeps smiths alive and consuming messages |
|
|
16
|
+
|
|
17
|
+
## Three-Layer State Model
|
|
18
|
+
|
|
19
|
+
Each smith has three independent status layers displayed on the node:
|
|
20
|
+
|
|
21
|
+
| Layer | Values | Description |
|
|
22
|
+
|-------|--------|-------------|
|
|
23
|
+
| **Smith Status** | `down` / `active` | Whether the daemon process is running |
|
|
24
|
+
| **Mode** | `auto` / `manual` | `auto` = daemon-driven, `manual` = user in terminal (purple) |
|
|
25
|
+
| **Task Status** | `idle` / `running` / `done` / `failed` | Current work status |
|
|
26
|
+
|
|
27
|
+
- **Mode controls message consumption**: `manual` pauses inbox processing (same as `running`)
|
|
28
|
+
- **Smith Status controls the daemon loop**: `down` stops the message loop entirely
|
|
29
|
+
- **Task Status tracks work**: unaffected by mode changes
|
|
30
|
+
|
|
31
|
+
## Dependencies (DAG)
|
|
32
|
+
|
|
33
|
+
Dependencies must form a **directed acyclic graph** (DAG). Circular dependencies are rejected when adding or editing agents.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Input → PM → Engineer → QA → Reviewer
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Upstream agents complete first, then broadcast to downstream
|
|
40
|
+
- Each agent knows its upstream (dependsOn) and the system prevents cycles
|
|
41
|
+
|
|
42
|
+
## Message System
|
|
43
|
+
|
|
44
|
+
### Two Message Categories
|
|
45
|
+
|
|
46
|
+
| Category | Direction | Behavior | Use Case |
|
|
47
|
+
|----------|-----------|----------|----------|
|
|
48
|
+
| **Notification** | Follows DAG (upstream → downstream) | Auto-broadcast on completion, downstream discards reverse notifications | "I'm done, here's what I did" |
|
|
49
|
+
| **Ticket** | Any direction (ignores DAG) | 1-to-1, independent lifecycle, retry limits | Bug reports, fix requests |
|
|
50
|
+
|
|
51
|
+
### Notification Messages (Default)
|
|
52
|
+
|
|
53
|
+
When a smith completes:
|
|
54
|
+
- If it was processing an **upstream** message → broadcast to all downstream agents
|
|
55
|
+
- If it was processing a **downstream** message → no broadcast, just mark the message done (sender checks outbox status)
|
|
56
|
+
- Each message carries a `causedBy` field tracing which inbox message triggered it
|
|
57
|
+
|
|
58
|
+
### Ticket Messages
|
|
59
|
+
|
|
60
|
+
- Created via `create_ticket` API or forge skills
|
|
61
|
+
- Have their own lifecycle: `open → in_progress → fixed → verified → closed`
|
|
62
|
+
- Retry limit (default 3), exceeding marks ticket as failed
|
|
63
|
+
- Not affected by DAG direction — any agent can ticket any agent
|
|
64
|
+
|
|
65
|
+
### CausedBy Chain
|
|
66
|
+
|
|
67
|
+
Every outgoing message carries `causedBy` linking to the inbox message that triggered it:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"from": "qa-123",
|
|
72
|
+
"to": "reviewer-456",
|
|
73
|
+
"action": "upstream_complete",
|
|
74
|
+
"causedBy": {
|
|
75
|
+
"messageId": "msg-789",
|
|
76
|
+
"from": "engineer-111",
|
|
77
|
+
"to": "qa-123"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This enables:
|
|
83
|
+
- **Loop prevention**: Notifications from downstream are silently discarded
|
|
84
|
+
- **Outbox tracking**: Sender can verify their message was processed
|
|
85
|
+
- **Audit trail**: Every message traces back to its trigger
|
|
86
|
+
|
|
87
|
+
### Message Receive Rules
|
|
88
|
+
|
|
89
|
+
When a message arrives at an agent's inbox:
|
|
90
|
+
|
|
91
|
+
1. **Tickets** → always accepted (with retry limit check)
|
|
92
|
+
2. **Notification with causedBy matching own outbox** → accepted (response to my request)
|
|
93
|
+
3. **Notification from downstream agent** → silently discarded (prevents reverse flow)
|
|
94
|
+
4. **Notification from upstream or no causedBy** → accepted (normal DAG flow)
|
|
95
|
+
|
|
96
|
+
### Message Status Flow
|
|
97
|
+
|
|
98
|
+
`pending` → `running` → `done` / `failed`
|
|
99
|
+
|
|
100
|
+
- Only one message processed at a time per agent
|
|
101
|
+
- `currentMessageId` persisted in agent state for crash recovery
|
|
102
|
+
|
|
103
|
+
## Manual Mode
|
|
104
|
+
|
|
105
|
+
Click the **⌨️** button on any smith to open a terminal:
|
|
106
|
+
- Mode switches to `manual` (purple indicator on node)
|
|
107
|
+
- Inbox message processing pauses (messages stay pending)
|
|
108
|
+
- A tmux session opens with CLI + profile env + forge env vars (`FORGE_AGENT_ID`, `FORGE_WORKSPACE_ID`, `FORGE_PORT`)
|
|
109
|
+
- Forge Skills auto-installed for inter-agent communication
|
|
110
|
+
- Close terminal → mode returns to `auto`, pending messages resume processing
|
|
111
|
+
|
|
112
|
+
### Forge Skills in Terminal
|
|
113
|
+
|
|
114
|
+
| Skill | Description |
|
|
115
|
+
|-------|-------------|
|
|
116
|
+
| `/forge-send` | Send a message to another smith (blocked if replying to current sender — use for NEW issues only) |
|
|
117
|
+
| `/forge-inbox` | Check incoming messages |
|
|
118
|
+
| `/forge-status` | Check all smiths' status |
|
|
119
|
+
| `/forge-workspace-sync` | Sync progress back to workspace |
|
|
120
|
+
|
|
121
|
+
**Note**: When processing a message from another agent, do NOT use `/forge-send` to reply — the system auto-delivers results via `markMessageDone`. Only use `/forge-send` for new issues to other agents.
|
|
122
|
+
|
|
123
|
+
## Inbox Management
|
|
124
|
+
|
|
125
|
+
Each smith has an inbox panel with two tabs:
|
|
126
|
+
|
|
127
|
+
### Inbox Tab
|
|
128
|
+
- Messages received from other smiths
|
|
129
|
+
- Status badges: pending (yellow), running (blue), done (green), failed (red)
|
|
130
|
+
- Ticket messages have purple border + TICKET badge + lifecycle status
|
|
131
|
+
- CausedBy trace shows which agent triggered the message
|
|
132
|
+
|
|
133
|
+
### Outbox Tab
|
|
134
|
+
- Messages sent by this smith
|
|
135
|
+
- Track delivery status and responses
|
|
136
|
+
|
|
137
|
+
### Batch Operations
|
|
138
|
+
- **Select all completed** → batch delete done/failed messages
|
|
139
|
+
- **Abort all pending (N)** → cancel all pending messages at once
|
|
140
|
+
- Checkbox selection on individual done/failed messages
|
|
141
|
+
|
|
142
|
+
## Controls
|
|
143
|
+
|
|
144
|
+
| Action | Description |
|
|
145
|
+
|--------|-------------|
|
|
146
|
+
| **Start** | Launch daemon, begin executing all agents |
|
|
147
|
+
| **Stop** | Stop daemon, kill all workers |
|
|
148
|
+
| **Pause** | Pause a specific smith (stops consuming messages) |
|
|
149
|
+
| **Resume** | Resume a paused smith |
|
|
150
|
+
| **Retry** | Retry a failed smith |
|
|
151
|
+
| **Reset** | Reset smith to idle, clear history |
|
|
152
|
+
| **Open Terminal** | Switch to manual mode, open floating terminal |
|
|
153
|
+
| **Close Terminal** | Return to auto mode, resume message processing |
|
|
154
|
+
|
|
155
|
+
## Workspace API
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# List workspaces
|
|
159
|
+
curl http://localhost:8403/api/workspace
|
|
160
|
+
|
|
161
|
+
# Get workspace state
|
|
162
|
+
curl http://localhost:8403/api/workspace/<id>
|
|
163
|
+
|
|
164
|
+
# Agent operations
|
|
165
|
+
curl -X POST http://localhost:8403/api/workspace/<id>/agents \
|
|
166
|
+
-H 'Content-Type: application/json' \
|
|
167
|
+
-d '{"action":"start"}' # start daemon
|
|
168
|
+
-d '{"action":"stop"}' # stop daemon
|
|
169
|
+
-d '{"action":"run", "agentId":"engineer-123"}' # run one smith
|
|
170
|
+
-d '{"action":"open_terminal", "agentId":"engineer-123"}' # manual mode
|
|
171
|
+
-d '{"action":"close_terminal", "agentId":"engineer-123"}' # back to auto
|
|
172
|
+
-d '{"action":"reset", "agentId":"engineer-123"}' # reset to idle
|
|
173
|
+
|
|
174
|
+
# Smith API (via workspace daemon)
|
|
175
|
+
curl -X POST http://localhost:8403/api/workspace/<id>/smith \
|
|
176
|
+
-H 'Content-Type: application/json' \
|
|
177
|
+
-d '{"action":"send","agentId":"$ID","to":"QA","msgAction":"review","content":"Please check"}'
|
|
178
|
+
-d '{"action":"inbox","agentId":"$ID"}'
|
|
179
|
+
-d '{"action":"status","agentId":"$ID"}'
|
|
180
|
+
-d '{"action":"create_ticket","agentId":"$FROM","targetId":"$TO","content":"Bug found"}'
|
|
181
|
+
-d '{"action":"update_ticket","messageId":"$ID","ticketStatus":"fixed"}'
|
|
182
|
+
|
|
183
|
+
# Stream real-time events (SSE)
|
|
184
|
+
curl http://localhost:8403/api/workspace/<id>/stream
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Watch (Autonomous Monitoring)
|
|
188
|
+
|
|
189
|
+
Agents can autonomously monitor file changes, git commits, or custom commands without relying on messages.
|
|
190
|
+
|
|
191
|
+
### Configuration
|
|
192
|
+
|
|
193
|
+
In the agent config modal, enable Watch and configure:
|
|
194
|
+
- **Interval**: Check frequency in seconds (min 10, default 60)
|
|
195
|
+
- **Targets**: What to monitor
|
|
196
|
+
- `Directory` — select from project folders, detect file mtime changes
|
|
197
|
+
- `Git` — detect new commits via HEAD hash comparison
|
|
198
|
+
- `Agent Output` — monitor another agent's declared output paths
|
|
199
|
+
- `Command` — run a shell command, detect output changes
|
|
200
|
+
- **On Change**: Action when changes detected
|
|
201
|
+
- `Log` — write to agent log only (default, no token cost)
|
|
202
|
+
- `Analyze` — auto-wake agent to analyze changes (costs tokens)
|
|
203
|
+
- `Approve` — create pending approval, user decides whether to trigger
|
|
204
|
+
|
|
205
|
+
### Watch Behavior
|
|
206
|
+
|
|
207
|
+
- First check builds a baseline (no alert)
|
|
208
|
+
- Subsequent checks compare timestamps — only files modified since last check are reported
|
|
209
|
+
- No-change heartbeats log to console only (not to files)
|
|
210
|
+
- Change alerts write to `logs.jsonl` and appear in Log panel
|
|
211
|
+
- Watch never sends bus messages — report only, no auto-triggering other agents
|
|
212
|
+
|
|
213
|
+
## Agent Logs
|
|
214
|
+
|
|
215
|
+
Each agent has a persistent log file (`logs.jsonl`) that survives daemon restarts and agent re-execution.
|
|
216
|
+
|
|
217
|
+
- **Log panel**: Click the log button on any agent node to view
|
|
218
|
+
- **Persistent**: Logs are append-only, not cleared on reset or re-run
|
|
219
|
+
- **Clear**: Use the "Clear" button in the Log panel header to manually wipe logs
|
|
220
|
+
- **Content**: Execution output, watch alerts, bus message receipts, system events
|
|
221
|
+
|
|
222
|
+
## Forge Skills (Terminal Communication)
|
|
223
|
+
|
|
224
|
+
When in manual mode, agents have forge env vars injected (`FORGE_AGENT_ID`, `FORGE_WORKSPACE_ID`, `FORGE_PORT`) and can use:
|
|
225
|
+
|
|
226
|
+
| Skill | Description |
|
|
227
|
+
|-------|-------------|
|
|
228
|
+
| `/forge-send` | Send a message to another smith |
|
|
229
|
+
| `/forge-inbox` | Check incoming messages |
|
|
230
|
+
| `/forge-status` | Check all smiths' status |
|
|
231
|
+
| `/forge-workspace-sync` | Sync progress back to workspace |
|
|
232
|
+
|
|
233
|
+
**Send protection**: If an agent is currently processing a message from another agent, `/forge-send` to that agent is blocked (returns `skipped: true`). Results are delivered automatically via the message system. Only use `/forge-send` for new issues to other agents.
|
|
234
|
+
|
|
235
|
+
## Persistence
|
|
236
|
+
|
|
237
|
+
- Workspace state: `~/.forge/workspaces/<id>/state.json`
|
|
238
|
+
- Agent logs: `~/.forge/workspaces/<id>/agents/<agentId>/logs.jsonl`
|
|
239
|
+
- Auto-saved every 10 seconds
|
|
240
|
+
- Atomic writes (temp file → rename) for crash safety
|
|
241
|
+
- Synchronous save on daemon shutdown
|
|
242
|
+
- `currentMessageId` persisted per agent for crash recovery
|
|
243
|
+
|
|
244
|
+
## Tips
|
|
245
|
+
|
|
246
|
+
1. **Dependencies must be a DAG** — no circular dependencies allowed
|
|
247
|
+
2. **Start with Input nodes** — define requirements before adding agent smiths
|
|
248
|
+
3. **Use profiles** for agents that need custom API endpoints or models
|
|
249
|
+
4. **Notifications flow downstream** — upstream agents won't receive downstream broadcasts
|
|
250
|
+
5. **Use tickets for bugs** — tickets ignore DAG direction, have retry limits
|
|
251
|
+
6. **Open Terminal** for manual intervention — mode switches to manual, inbox pauses
|
|
252
|
+
7. **Use Watch for monitoring** — detect file changes without message overhead (set action to `log` to avoid token costs)
|
|
253
|
+
8. **Check Log panel** for execution history and watch alerts — logs persist across restarts
|
|
254
|
+
9. **Batch operations** — select all completed messages for bulk delete, or abort all pending at once
|
package/lib/help-docs/CLAUDE.md
CHANGED
|
@@ -25,6 +25,7 @@ Your job is to answer user questions about Forge features, configuration, and tr
|
|
|
25
25
|
| `08-rules.md` | CLAUDE.md templates and rule injection |
|
|
26
26
|
| `09-issue-autofix.md` | GitHub issue auto-fix pipeline |
|
|
27
27
|
| `10-troubleshooting.md` | Common issues and solutions |
|
|
28
|
+
| `11-workspace.md` | Workspace (Forge Smiths) — multi-agent orchestration, daemon, message bus, profiles |
|
|
28
29
|
|
|
29
30
|
## Matching questions to docs
|
|
30
31
|
|
|
@@ -33,9 +34,13 @@ Your job is to answer user questions about Forge features, configuration, and tr
|
|
|
33
34
|
- Telegram/notification → `02-telegram.md`
|
|
34
35
|
- Tunnel/remote/cloudflare → `03-tunnel.md`
|
|
35
36
|
- Task/background/queue → `04-tasks.md`
|
|
36
|
-
- Settings/config → `01-settings.md`
|
|
37
|
+
- Settings/config/agent/profile/provider → `01-settings.md`
|
|
37
38
|
- Install/start/update → `00-overview.md`
|
|
38
39
|
- Error/bug/crash → `10-troubleshooting.md`
|
|
39
40
|
- Skill/marketplace → `06-skills.md`
|
|
40
|
-
- Project/favorite → `07-projects.md`
|
|
41
|
+
- Project/favorite/terminal → `07-projects.md`
|
|
41
42
|
- Rules/CLAUDE.md/template → `08-rules.md`
|
|
43
|
+
- Workspace/smith/daemon/multi-agent/bus/message/ticket → `11-workspace.md`
|
|
44
|
+
- Watch/monitor/detect/file changes/autonomous → `11-workspace.md`
|
|
45
|
+
- Agent profile/env/model/cliType → `01-settings.md` + `11-workspace.md`
|
|
46
|
+
- Agent log/logs/history/clear logs → `11-workspace.md`
|
package/lib/init.ts
CHANGED
|
@@ -45,18 +45,25 @@ function migrateSecrets() {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/** Auto-detect
|
|
49
|
-
function
|
|
48
|
+
/** Auto-detect agent binaries */
|
|
49
|
+
function autoDetectAgents() {
|
|
50
50
|
try {
|
|
51
51
|
const settings = loadSettings();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
// Backward compat: detect claude if not configured
|
|
53
|
+
if (!settings.claudePath) {
|
|
54
|
+
const { execSync } = require('node:child_process');
|
|
55
|
+
try {
|
|
56
|
+
const path = execSync('which claude', { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
57
|
+
if (path) {
|
|
58
|
+
settings.claudePath = path;
|
|
59
|
+
saveSettings(settings);
|
|
60
|
+
console.log(`[init] Auto-detected claude: ${path}`);
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
59
63
|
}
|
|
64
|
+
// Detect all agents
|
|
65
|
+
const { autoDetectAgents: detect } = require('./agents');
|
|
66
|
+
detect();
|
|
60
67
|
} catch {}
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -80,7 +87,14 @@ export function ensureInitialized() {
|
|
|
80
87
|
} catch {}
|
|
81
88
|
|
|
82
89
|
// Auto-detect claude path if not configured
|
|
83
|
-
|
|
90
|
+
autoDetectAgents();
|
|
91
|
+
|
|
92
|
+
// Install/update forge skills to ~/.claude/skills/ on every startup
|
|
93
|
+
try {
|
|
94
|
+
const { installForgeSkills } = require('./workspace/skill-installer');
|
|
95
|
+
installForgeSkills('', '', '', Number(process.env.PORT) || 8403);
|
|
96
|
+
console.log('[init] Forge skills installed/updated');
|
|
97
|
+
} catch {}
|
|
84
98
|
|
|
85
99
|
// Sync help docs + CLAUDE.md to data dir on startup
|
|
86
100
|
try {
|
|
@@ -148,6 +162,7 @@ export function ensureInitialized() {
|
|
|
148
162
|
startTelegramBot(); // registers task event listener only
|
|
149
163
|
startTerminalProcess();
|
|
150
164
|
startTelegramProcess(); // spawns telegram-standalone
|
|
165
|
+
startWorkspaceProcess(); // spawns workspace-standalone
|
|
151
166
|
|
|
152
167
|
const settings = loadSettings();
|
|
153
168
|
if (settings.tunnelAutoStart) {
|
|
@@ -214,3 +229,38 @@ function startTerminalProcess() {
|
|
|
214
229
|
});
|
|
215
230
|
tester.listen(termPort);
|
|
216
231
|
}
|
|
232
|
+
|
|
233
|
+
let workspaceChild: ReturnType<typeof spawn> | null = null;
|
|
234
|
+
|
|
235
|
+
function startWorkspaceProcess() {
|
|
236
|
+
if (workspaceChild) return;
|
|
237
|
+
|
|
238
|
+
const wsPort = Number(process.env.WORKSPACE_PORT) || 8405;
|
|
239
|
+
|
|
240
|
+
const net = require('node:net');
|
|
241
|
+
const tester = net.createServer();
|
|
242
|
+
tester.once('error', () => {
|
|
243
|
+
// Port in use — kill stale process and retry after 2s
|
|
244
|
+
console.log(`[workspace] Port ${wsPort} in use, killing stale process...`);
|
|
245
|
+
try { require('node:child_process').execSync(`lsof -ti:${wsPort} | xargs kill -9 2>/dev/null`, { timeout: 3000 }); } catch {}
|
|
246
|
+
setTimeout(() => {
|
|
247
|
+
if (!workspaceChild) launchWorkspaceDaemon();
|
|
248
|
+
}, 2000);
|
|
249
|
+
});
|
|
250
|
+
tester.once('listening', () => {
|
|
251
|
+
tester.close();
|
|
252
|
+
launchWorkspaceDaemon();
|
|
253
|
+
});
|
|
254
|
+
tester.listen(wsPort);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function launchWorkspaceDaemon() {
|
|
258
|
+
const script = join(process.cwd(), 'lib', 'workspace-standalone.ts');
|
|
259
|
+
workspaceChild = spawn('npx', ['tsx', script], {
|
|
260
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
261
|
+
env: { ...process.env },
|
|
262
|
+
detached: false,
|
|
263
|
+
});
|
|
264
|
+
workspaceChild.on('exit', () => { workspaceChild = null; });
|
|
265
|
+
console.log('[workspace] Started daemon (pid:', workspaceChild.pid, ')');
|
|
266
|
+
}
|