@geekbeer/minion 2.5.0 → 2.6.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/minion-cli.sh +142 -4
- package/package.json +3 -1
- package/routes/files.js +21 -7
- package/routes/index.js +10 -0
- package/routes/routines.js +260 -0
- package/routes/workflows.js +14 -0
- package/routine-runner.js +404 -0
- package/routine-store.js +116 -0
- package/rules/minion.md +194 -52
- package/server.js +15 -1
- package/workflow-runner.js +1 -0
- package/workflow-store.js +5 -1
package/rules/minion.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
You are running on a Minion VPS managed by the @geekbeer/minion package.
|
|
4
4
|
A local Agent API server runs at `http://localhost:3001` and a CLI tool `minion-cli` is available.
|
|
5
5
|
|
|
6
|
+
## Architecture: Project / Workflow / Routine
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Project (組織・課金単位)
|
|
10
|
+
├── Context (markdown, PMが更新)
|
|
11
|
+
├── Members (minion + role)
|
|
12
|
+
└── Workflows (何をするか, スケジュールなし)
|
|
13
|
+
|
|
14
|
+
Minion
|
|
15
|
+
└── Routines (いつ・何を, cron付き)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- **Workflow** はプロジェクトスコープ。「何をするか」を定義。スケジュールは持たない。
|
|
19
|
+
- **Routine** はミニオンスコープ。「いつ・何を」を定義。cron_expression を持ち、自律トリガーする。
|
|
20
|
+
- ミニオンは複数プロジェクトに `pm` または `engineer` として参加できる。
|
|
21
|
+
- ワークフローは直接トリガーされず、ルーティン経由で実行される(`project-workflow-check` スキル)。
|
|
22
|
+
|
|
6
23
|
## CLI Commands
|
|
7
24
|
|
|
8
25
|
```bash
|
|
@@ -35,7 +52,7 @@ Authentication: `Authorization: Bearer $API_TOKEN` header (except where noted).
|
|
|
35
52
|
| Method | Endpoint | Description |
|
|
36
53
|
|--------|----------|-------------|
|
|
37
54
|
| GET | `/api/list-skills` | List local deployed skills |
|
|
38
|
-
| DELETE | `/api/skills/:name` | Delete local skill |
|
|
55
|
+
| DELETE | `/api/skills/:name` | Delete a local skill |
|
|
39
56
|
| POST | `/api/skills/push/:name` | Push local skill to HQ |
|
|
40
57
|
| POST | `/api/skills/fetch/:name` | Fetch skill from HQ and deploy locally |
|
|
41
58
|
| GET | `/api/skills/remote` | List skills on HQ |
|
|
@@ -44,23 +61,26 @@ Authentication: `Authorization: Bearer $API_TOKEN` header (except where noted).
|
|
|
44
61
|
|
|
45
62
|
| Method | Endpoint | Description |
|
|
46
63
|
|--------|----------|-------------|
|
|
47
|
-
| GET | `/api/workflows` | List all workflows with next_run |
|
|
48
|
-
| POST | `/api/workflows` | Receive/upsert workflows from HQ |
|
|
64
|
+
| GET | `/api/workflows` | List all local workflows with next_run |
|
|
65
|
+
| POST | `/api/workflows` | Receive/upsert workflows (from HQ or local) |
|
|
49
66
|
| POST | `/api/workflows/push/:name` | Push local workflow to HQ |
|
|
50
67
|
| POST | `/api/workflows/fetch/:name` | Fetch workflow from HQ and deploy locally (+ pipeline skills) |
|
|
51
68
|
| GET | `/api/workflows/remote` | List workflows on HQ |
|
|
52
|
-
| PUT | `/api/workflows/:id/schedule` | Update
|
|
53
|
-
| DELETE | `/api/workflows/:id` | Remove workflow |
|
|
69
|
+
| PUT | `/api/workflows/:id/schedule` | **Legacy.** Update cron_expression/is_active. 新アーキではRoutine側で管理 |
|
|
70
|
+
| DELETE | `/api/workflows/:id` | Remove a local workflow |
|
|
54
71
|
| POST | `/api/workflows/trigger` | Manual trigger. Body: `{workflow_id}` |
|
|
55
72
|
|
|
73
|
+
> **Note:** 新アーキテクチャでは Workflow にスケジュールがなく、Routine の cron で自律トリガーする設計。
|
|
74
|
+
> `PUT /api/workflows/:id/schedule` はレガシー互換で残っているが、新規利用は非推奨。
|
|
75
|
+
|
|
56
76
|
### Executions
|
|
57
77
|
|
|
58
78
|
| Method | Endpoint | Description |
|
|
59
79
|
|--------|----------|-------------|
|
|
60
|
-
| GET | `/api/executions` | List execution history (
|
|
80
|
+
| GET | `/api/executions` | List execution history (`?limit=50`, `?workflow_id=`) |
|
|
61
81
|
| GET | `/api/executions/:id` | Get single execution |
|
|
62
|
-
| GET | `/api/executions/:id/log` | Get execution log content |
|
|
63
|
-
| POST | `/api/executions/:id/outcome` | Report outcome (no auth). Body: `{outcome}` |
|
|
82
|
+
| GET | `/api/executions/:id/log` | Get execution log content (`?tail=N`) |
|
|
83
|
+
| POST | `/api/executions/:id/outcome` | Report outcome (no auth). Body: `{outcome, summary?, details?}` |
|
|
64
84
|
|
|
65
85
|
Outcome values: `success`, `failure`, `partial`
|
|
66
86
|
|
|
@@ -69,13 +89,25 @@ Outcome values: `success`, `failure`, `partial`
|
|
|
69
89
|
| Method | Endpoint | Description |
|
|
70
90
|
|--------|----------|-------------|
|
|
71
91
|
| GET | `/api/terminal/sessions` | List tmux sessions |
|
|
72
|
-
| POST | `/api/terminal/send` | Send keys. Body: `{session,
|
|
73
|
-
| POST | `/api/terminal/create` | Create session. Body: `{name
|
|
92
|
+
| POST | `/api/terminal/send` | Send keys. Body: `{session, input?, enter?, special?}` |
|
|
93
|
+
| POST | `/api/terminal/create` | Create session. Body: `{name?, command?}` |
|
|
74
94
|
| POST | `/api/terminal/kill` | Kill session. Body: `{session}` |
|
|
75
|
-
| GET | `/api/terminal/capture` | Capture pane content. Query: `?session
|
|
95
|
+
| GET | `/api/terminal/capture` | Capture pane content. Query: `?session=&lines=100` |
|
|
76
96
|
| GET | `/api/terminal/ttyd/status` | ttyd process status |
|
|
77
|
-
| POST | `/api/terminal/ttyd/start` | Start ttyd. Body: `{session}` |
|
|
78
|
-
| POST | `/api/terminal/ttyd/stop` | Stop ttyd. Body: `{session}` |
|
|
97
|
+
| POST | `/api/terminal/ttyd/start` | Start ttyd for session. Body: `{session}` |
|
|
98
|
+
| POST | `/api/terminal/ttyd/stop` | Stop ttyd for session. Body: `{session}` |
|
|
99
|
+
| POST | `/api/terminal/ttyd/stop-all` | Stop all ttyd processes |
|
|
100
|
+
|
|
101
|
+
### Files
|
|
102
|
+
|
|
103
|
+
Files are stored in `~/files/`. Max upload size: 50MB.
|
|
104
|
+
|
|
105
|
+
| Method | Endpoint | Description |
|
|
106
|
+
|--------|----------|-------------|
|
|
107
|
+
| GET | `/api/files` | List files in directory. Query: `?path=subdir` |
|
|
108
|
+
| GET | `/api/files/*` | Download a file |
|
|
109
|
+
| POST | `/api/files/*` | Upload a file (Content-Type: `application/octet-stream`) |
|
|
110
|
+
| DELETE | `/api/files/*` | Delete a file or directory |
|
|
79
111
|
|
|
80
112
|
### Commands
|
|
81
113
|
|
|
@@ -86,6 +118,103 @@ Outcome values: `success`, `failure`, `partial`
|
|
|
86
118
|
|
|
87
119
|
Available commands: `restart-agent`, `update-agent`, `restart-display`, `status-services`
|
|
88
120
|
|
|
121
|
+
## HQ API Endpoints (https://<HQ_URL>)
|
|
122
|
+
|
|
123
|
+
Authentication: `Authorization: Bearer $API_TOKEN` header.
|
|
124
|
+
Base path: All endpoints are prefixed with `/api/minion` by the agent's API client.
|
|
125
|
+
|
|
126
|
+
### Projects
|
|
127
|
+
|
|
128
|
+
| Method | Endpoint | Description |
|
|
129
|
+
|--------|----------|-------------|
|
|
130
|
+
| GET | `/api/minion/me/projects` | 参加プロジェクト一覧(role 含む) |
|
|
131
|
+
|
|
132
|
+
Response:
|
|
133
|
+
```json
|
|
134
|
+
[
|
|
135
|
+
{
|
|
136
|
+
"id": "uuid",
|
|
137
|
+
"name": "project-name",
|
|
138
|
+
"description": "...",
|
|
139
|
+
"github_owner": "org",
|
|
140
|
+
"github_repo": "repo",
|
|
141
|
+
"role": "pm",
|
|
142
|
+
"created_at": "...",
|
|
143
|
+
"updated_at": "..."
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`role` is either `"pm"` (project manager) or `"engineer"`.
|
|
149
|
+
|
|
150
|
+
### Project Context
|
|
151
|
+
|
|
152
|
+
| Method | Endpoint | Description |
|
|
153
|
+
|--------|----------|-------------|
|
|
154
|
+
| GET | `/api/minion/me/project/[id]/context` | プロジェクトコンテキスト取得 |
|
|
155
|
+
| PUT | `/api/minion/me/project/[id]/context` | コンテキスト更新(PM のみ) |
|
|
156
|
+
|
|
157
|
+
PUT body: `{ "content": "markdown string" }`
|
|
158
|
+
|
|
159
|
+
### Workflows (project-scoped)
|
|
160
|
+
|
|
161
|
+
| Method | Endpoint | Description |
|
|
162
|
+
|--------|----------|-------------|
|
|
163
|
+
| GET | `/api/minion/workflows` | 参加プロジェクトのワークフロー一覧 |
|
|
164
|
+
|
|
165
|
+
Response:
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"workflows": [
|
|
169
|
+
{
|
|
170
|
+
"name": "workflow-name",
|
|
171
|
+
"pipeline_skill_names": ["skill-1", "skill-2"],
|
|
172
|
+
"content": "...",
|
|
173
|
+
"project_id": "uuid",
|
|
174
|
+
"created_at": "..."
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Routines (minion-scoped)
|
|
181
|
+
|
|
182
|
+
| Method | Endpoint | Description |
|
|
183
|
+
|--------|----------|-------------|
|
|
184
|
+
| GET | `/api/minion/routines` | 自分のルーティン一覧 |
|
|
185
|
+
|
|
186
|
+
Response:
|
|
187
|
+
```json
|
|
188
|
+
[
|
|
189
|
+
{
|
|
190
|
+
"id": "uuid",
|
|
191
|
+
"name": "routine-name",
|
|
192
|
+
"pipeline_skill_names": ["skill-1", "execution-report"],
|
|
193
|
+
"content": "...",
|
|
194
|
+
"is_active": true,
|
|
195
|
+
"cron_expression": "0 9 * * 1-5",
|
|
196
|
+
"last_run": "...",
|
|
197
|
+
"next_run": "...",
|
|
198
|
+
"created_at": "...",
|
|
199
|
+
"updated_at": "..."
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Skills
|
|
205
|
+
|
|
206
|
+
| Method | Endpoint | Description |
|
|
207
|
+
|--------|----------|-------------|
|
|
208
|
+
| GET | `/api/minion/skills` | HQ に登録されたスキル一覧 |
|
|
209
|
+
| GET | `/api/minion/skills/[name]` | スキル詳細取得(content, references 含む) |
|
|
210
|
+
| POST | `/api/minion/skills` | スキル登録/更新 |
|
|
211
|
+
|
|
212
|
+
### Execution Reporting
|
|
213
|
+
|
|
214
|
+
| Method | Endpoint | Description |
|
|
215
|
+
|--------|----------|-------------|
|
|
216
|
+
| POST | `/api/minion/execution` | 実行状況を HQ に報告 |
|
|
217
|
+
|
|
89
218
|
## Environment Variables
|
|
90
219
|
|
|
91
220
|
| Variable | Description |
|
|
@@ -125,7 +254,8 @@ When not configured, the agent runs in standalone mode and HQ-dependent features
|
|
|
125
254
|
|
|
126
255
|
## Workflow Structure
|
|
127
256
|
|
|
128
|
-
Workflows are
|
|
257
|
+
Workflows are project-scoped and fetched from HQ via `GET /api/minion/workflows`.
|
|
258
|
+
They are also stored locally in `workflows.json`. Each workflow object:
|
|
129
259
|
|
|
130
260
|
```json
|
|
131
261
|
{
|
|
@@ -133,9 +263,7 @@ Workflows are stored locally in `workflows.json`. Each workflow object:
|
|
|
133
263
|
"name": "my-workflow",
|
|
134
264
|
"pipeline_skill_names": ["skill-1", "skill-2", "execution-report"],
|
|
135
265
|
"content": "Markdown description of the workflow",
|
|
136
|
-
"
|
|
137
|
-
"is_active": true,
|
|
138
|
-
"last_run": "2026-02-10T00:00:00.000Z"
|
|
266
|
+
"project_id": "uuid"
|
|
139
267
|
}
|
|
140
268
|
```
|
|
141
269
|
|
|
@@ -145,38 +273,7 @@ Workflows are stored locally in `workflows.json`. Each workflow object:
|
|
|
145
273
|
| `name` | string | Slug identifier (`/^[a-z0-9-]+$/`) |
|
|
146
274
|
| `pipeline_skill_names` | string[] | Ordered skill names to execute |
|
|
147
275
|
| `content` | string | Markdown body describing the workflow |
|
|
148
|
-
| `
|
|
149
|
-
| `is_active` | boolean | Whether cron scheduling is enabled |
|
|
150
|
-
| `last_run` | string\|null | ISO timestamp of last execution |
|
|
151
|
-
|
|
152
|
-
### Creating a workflow locally
|
|
153
|
-
|
|
154
|
-
Use `POST /api/workflows` to create/update workflows:
|
|
155
|
-
|
|
156
|
-
```bash
|
|
157
|
-
curl -X POST http://localhost:3001/api/workflows \
|
|
158
|
-
-H "Authorization: Bearer $API_TOKEN" \
|
|
159
|
-
-H "Content-Type: application/json" \
|
|
160
|
-
-d '{
|
|
161
|
-
"workflows": [{
|
|
162
|
-
"id": "'$(uuidgen)'",
|
|
163
|
-
"name": "my-workflow",
|
|
164
|
-
"pipeline_skill_names": ["my-skill", "execution-report"],
|
|
165
|
-
"content": "Description of what this workflow does",
|
|
166
|
-
"cron_expression": "",
|
|
167
|
-
"is_active": false
|
|
168
|
-
}]
|
|
169
|
-
}'
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Then activate with a schedule:
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
curl -X PUT http://localhost:3001/api/workflows/<id>/schedule \
|
|
176
|
-
-H "Authorization: Bearer $API_TOKEN" \
|
|
177
|
-
-H "Content-Type: application/json" \
|
|
178
|
-
-d '{"cron_expression": "0 9 * * 1-5", "is_active": true}'
|
|
179
|
-
```
|
|
276
|
+
| `project_id` | string | UUID of the parent project |
|
|
180
277
|
|
|
181
278
|
### Syncing workflows with HQ
|
|
182
279
|
|
|
@@ -186,10 +283,55 @@ curl -X PUT http://localhost:3001/api/workflows/<id>/schedule \
|
|
|
186
283
|
|
|
187
284
|
Pipeline skills must be deployed locally (in `~/.claude/skills/`) before pushing a workflow.
|
|
188
285
|
|
|
189
|
-
##
|
|
286
|
+
## Routine Structure
|
|
190
287
|
|
|
191
|
-
|
|
288
|
+
Routines are minion-scoped and fetched from HQ via `GET /api/minion/routines`.
|
|
289
|
+
They are also stored locally in `routines.json`. Each routine object:
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"id": "uuid",
|
|
294
|
+
"name": "morning-work",
|
|
295
|
+
"pipeline_skill_names": ["project-workflow-check", "execution-report"],
|
|
296
|
+
"content": "Markdown description of the routine",
|
|
297
|
+
"cron_expression": "0 9 * * 1-5",
|
|
298
|
+
"is_active": true,
|
|
299
|
+
"last_run": "2026-02-10T09:00:00.000Z",
|
|
300
|
+
"next_run": "2026-02-11T09:00:00.000Z"
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
| Field | Type | Description |
|
|
305
|
+
|-------|------|-------------|
|
|
306
|
+
| `id` | string | UUID (auto-generated) |
|
|
307
|
+
| `name` | string | Slug identifier (`/^[a-z0-9-]+$/`) |
|
|
308
|
+
| `pipeline_skill_names` | string[] | Ordered skill names to execute |
|
|
309
|
+
| `content` | string | Markdown body describing the routine |
|
|
310
|
+
| `cron_expression` | string | Cron schedule (empty = manual only) |
|
|
311
|
+
| `is_active` | boolean | Whether cron scheduling is enabled |
|
|
312
|
+
| `last_run` | string\|null | ISO timestamp of last execution |
|
|
313
|
+
| `next_run` | string\|null | ISO timestamp of next scheduled run |
|
|
314
|
+
|
|
315
|
+
## Routine Execution
|
|
316
|
+
|
|
317
|
+
Routines run on cron schedules. Each execution:
|
|
192
318
|
1. Creates a tmux session
|
|
193
319
|
2. Runs skills in pipeline order via `claude --dangerously-skip-permissions`
|
|
194
320
|
3. Appends `execution-report` skill to report outcome
|
|
195
|
-
4. Environment variables `MINION_EXECUTION_ID`, `
|
|
321
|
+
4. Environment variables `MINION_EXECUTION_ID`, `MINION_ROUTINE_ID`, `MINION_ROUTINE_NAME` are available during execution
|
|
322
|
+
|
|
323
|
+
### Workflow Execution via Routine
|
|
324
|
+
|
|
325
|
+
ワークフローは `project-workflow-check` システムスキルを通じてルーティンから実行される:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
ルーティン: "morning-work" (cron: 0 9 * * 1-5)
|
|
329
|
+
pipeline: [project-workflow-check, execution-report]
|
|
330
|
+
↓
|
|
331
|
+
project-workflow-check:
|
|
332
|
+
1. GET /api/minion/me/projects → 参加プロジェクト一覧
|
|
333
|
+
2. GET /api/minion/workflows → active なワークフロー一覧
|
|
334
|
+
3. 各ワークフローの pipeline を順に実行
|
|
335
|
+
↓
|
|
336
|
+
execution-report → HQ に結果報告
|
|
337
|
+
```
|
package/server.js
CHANGED
|
@@ -13,6 +13,8 @@ const fastify = require('fastify')({ logger: true })
|
|
|
13
13
|
const { config, validate, isHqConfigured } = require('./config')
|
|
14
14
|
const workflowRunner = require('./workflow-runner')
|
|
15
15
|
const workflowStore = require('./workflow-store')
|
|
16
|
+
const routineRunner = require('./routine-runner')
|
|
17
|
+
const routineStore = require('./routine-store')
|
|
16
18
|
|
|
17
19
|
const { registerRoutes, setOffline, getProcessManager, getAllowedCommands } = require('./routes')
|
|
18
20
|
const { cleanupTtyd, killStaleTtydProcesses } = require('./routes/terminal')
|
|
@@ -31,8 +33,9 @@ async function shutdown(signal) {
|
|
|
31
33
|
|
|
32
34
|
setOffline()
|
|
33
35
|
|
|
34
|
-
// Stop workflow
|
|
36
|
+
// Stop workflow and routine runners
|
|
35
37
|
workflowRunner.stopAll()
|
|
38
|
+
routineRunner.stopAll()
|
|
36
39
|
|
|
37
40
|
// Stop terminal proxy and all ttyd processes
|
|
38
41
|
stopTerminalProxy()
|
|
@@ -164,6 +167,17 @@ async function start() {
|
|
|
164
167
|
console.error('[Server] Failed to load cached workflows:', err.message)
|
|
165
168
|
}
|
|
166
169
|
|
|
170
|
+
// Load cached routines and start routine runner
|
|
171
|
+
try {
|
|
172
|
+
const cachedRoutines = await routineStore.load()
|
|
173
|
+
if (cachedRoutines.length > 0) {
|
|
174
|
+
console.log(`[Server] Loading ${cachedRoutines.length} cached routines`)
|
|
175
|
+
routineRunner.loadRoutines(cachedRoutines)
|
|
176
|
+
}
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.error('[Server] Failed to load cached routines:', err.message)
|
|
179
|
+
}
|
|
180
|
+
|
|
167
181
|
if (isHqConfigured()) {
|
|
168
182
|
console.log(`[Server] HQ URL: ${config.HQ_URL}`)
|
|
169
183
|
} else {
|
package/workflow-runner.js
CHANGED
|
@@ -100,6 +100,7 @@ async function executeWorkflowSession(workflow, executionId, skillNames) {
|
|
|
100
100
|
|
|
101
101
|
// Extend PATH to include common CLI installation locations
|
|
102
102
|
const additionalPaths = [
|
|
103
|
+
path.join(homeDir, 'bin'),
|
|
103
104
|
path.join(homeDir, '.npm-global', 'bin'),
|
|
104
105
|
path.join(homeDir, '.local', 'bin'),
|
|
105
106
|
path.join(homeDir, '.claude', 'bin'),
|
package/workflow-store.js
CHANGED
|
@@ -82,7 +82,7 @@ async function findByName(name) {
|
|
|
82
82
|
* Upsert a workflow by name.
|
|
83
83
|
* If exists: updates definition only (preserves schedule/local state).
|
|
84
84
|
* If new: creates with inactive schedule.
|
|
85
|
-
* @param {object} workflowData - { name, pipeline_skill_names, content }
|
|
85
|
+
* @param {object} workflowData - { name, pipeline_skill_names, content, project_id }
|
|
86
86
|
* @returns {Promise<Array>} Updated workflows array
|
|
87
87
|
*/
|
|
88
88
|
async function upsertByName(workflowData) {
|
|
@@ -96,12 +96,16 @@ async function upsertByName(workflowData) {
|
|
|
96
96
|
if (workflowData.content !== undefined) {
|
|
97
97
|
workflows[index].content = workflowData.content
|
|
98
98
|
}
|
|
99
|
+
if (workflowData.project_id !== undefined) {
|
|
100
|
+
workflows[index].project_id = workflowData.project_id
|
|
101
|
+
}
|
|
99
102
|
} else {
|
|
100
103
|
workflows.push({
|
|
101
104
|
id: crypto.randomUUID(),
|
|
102
105
|
name: workflowData.name,
|
|
103
106
|
pipeline_skill_names: workflowData.pipeline_skill_names,
|
|
104
107
|
content: workflowData.content || '',
|
|
108
|
+
project_id: workflowData.project_id || null,
|
|
105
109
|
cron_expression: '',
|
|
106
110
|
is_active: false,
|
|
107
111
|
last_run: null,
|