@geekbeer/minion 3.23.1 → 3.24.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/core/config.js CHANGED
@@ -77,6 +77,11 @@ const config = {
77
77
  MINION_ID: process.env.MINION_ID || '',
78
78
  MINION_EMAIL: process.env.MINION_ID ? `m-${process.env.MINION_ID}@minion-agent.com` : '',
79
79
 
80
+ // Workspace targeting (optional)
81
+ // When set, skill push/fetch operations will target this workspace.
82
+ // If not set, HQ resolves the workspace from workspace_minions.
83
+ WORKSPACE_ID: process.env.WORKSPACE_ID || '',
84
+
80
85
  // Server settings
81
86
  AGENT_PORT: parseInt(process.env.AGENT_PORT, 10) || 8080,
82
87
 
package/core/db.js CHANGED
@@ -267,7 +267,6 @@ function initSchema(db) {
267
267
  CREATE INDEX IF NOT EXISTS idx_todos_status ON todos(status);
268
268
  CREATE INDEX IF NOT EXISTS idx_todos_project ON todos(project_id);
269
269
  CREATE INDEX IF NOT EXISTS idx_todos_priority ON todos(priority);
270
- CREATE INDEX IF NOT EXISTS idx_todos_session ON todos(session_id);
271
270
 
272
271
  -- ==================== emails ====================
273
272
  CREATE TABLE IF NOT EXISTS emails (
@@ -104,10 +104,12 @@ async function writeSkillToLocal(name, { content, description, display_name, typ
104
104
  * Reusable by workflow push to auto-sync pipeline skills.
105
105
  *
106
106
  * @param {string} name - Skill slug (e.g. "execution-report")
107
+ * @param {object} [opts]
108
+ * @param {string} [opts.workspaceId] - Target workspace ID (overrides WORKSPACE_ID env var)
107
109
  * @returns {Promise<object>} HQ response
108
110
  * @throws {Error} If skill not found locally or HQ rejects
109
111
  */
110
- async function pushSkillToHQ(name) {
112
+ async function pushSkillToHQ(name, opts = {}) {
111
113
  // Use the Primary plugin's skill directory as the canonical read source.
112
114
  // Skills are written identically to every enabled plugin dir, so reading
113
115
  // from any one of them yields the same content.
@@ -140,16 +142,24 @@ async function pushSkillToHQ(name) {
140
142
  }
141
143
  }
142
144
 
145
+ const payload = {
146
+ name: metadata.name || name,
147
+ display_name: metadata.display_name || metadata.name || name,
148
+ description: metadata.description || '',
149
+ content: rawContent,
150
+ type: metadata.type || 'workflow',
151
+ files,
152
+ }
153
+
154
+ // Include workspace_id if specified or configured (for multi-workspace targeting)
155
+ const targetWorkspaceId = opts.workspaceId || config.WORKSPACE_ID
156
+ if (targetWorkspaceId) {
157
+ payload.workspace_id = targetWorkspaceId
158
+ }
159
+
143
160
  return api.request('/skills', {
144
161
  method: 'POST',
145
- body: JSON.stringify({
146
- name: metadata.name || name,
147
- display_name: metadata.display_name || metadata.name || name,
148
- description: metadata.description || '',
149
- content: rawContent,
150
- type: metadata.type || 'workflow',
151
- files,
152
- }),
162
+ body: JSON.stringify(payload),
153
163
  })
154
164
  }
155
165
 
@@ -204,6 +214,7 @@ async function skillRoutes(fastify, opts) {
204
214
  })
205
215
 
206
216
  // Push local skill to HQ
217
+ // Optional body: { workspace_id?: string } — overrides WORKSPACE_ID env var
207
218
  fastify.post('/api/skills/push/:name', async (request, reply) => {
208
219
  if (!verifyToken(request)) {
209
220
  reply.code(401)
@@ -216,16 +227,17 @@ async function skillRoutes(fastify, opts) {
216
227
  }
217
228
 
218
229
  const { name } = request.params
230
+ const { workspace_id } = request.body || {}
219
231
 
220
232
  if (!name || !/^[a-z0-9-]+$/.test(name)) {
221
233
  reply.code(400)
222
234
  return { success: false, error: 'Invalid skill name' }
223
235
  }
224
236
 
225
- console.log(`[Skills] Pushing skill to HQ: ${name}`)
237
+ console.log(`[Skills] Pushing skill to HQ: ${name}${workspace_id ? ` (workspace: ${workspace_id})` : ''}`)
226
238
 
227
239
  try {
228
- const result = await pushSkillToHQ(name)
240
+ const result = await pushSkillToHQ(name, { workspaceId: workspace_id })
229
241
  console.log(`[Skills] Skill pushed to HQ: ${name}`)
230
242
  return { success: true, ...result }
231
243
  } catch (error) {
@@ -254,13 +266,17 @@ async function skillRoutes(fastify, opts) {
254
266
  return { success: false, error: 'Invalid skill name' }
255
267
  }
256
268
 
257
- // Forward template vars query param to HQ for {{VAR_NAME}} expansion
269
+ // Forward template vars and workspace_id query params to HQ
258
270
  const vars = request.query.vars || ''
259
- const queryString = vars ? `?vars=${encodeURIComponent(vars)}` : ''
260
- console.log(`[Skills] Fetching skill from HQ: ${name}${vars ? ' (with template vars)' : ''}`)
271
+ const fetchWorkspaceId = request.query.workspace_id || config.WORKSPACE_ID || ''
272
+ const queryParams = new URLSearchParams()
273
+ if (vars) queryParams.set('vars', vars)
274
+ if (fetchWorkspaceId) queryParams.set('workspace_id', fetchWorkspaceId)
275
+ const queryString = queryParams.toString() ? `?${queryParams.toString()}` : ''
276
+ console.log(`[Skills] Fetching skill from HQ: ${name}${vars ? ' (with template vars)' : ''}${fetchWorkspaceId ? ` (workspace: ${fetchWorkspaceId})` : ''}`)
261
277
 
262
278
  try {
263
- // Fetch from HQ (with optional template variable expansion)
279
+ // Fetch from HQ (with optional template variable expansion and workspace scoping)
264
280
  const skill = await api.request(`/skills/${encodeURIComponent(name)}${queryString}`)
265
281
 
266
282
  // Write to local filesystem using shared helper
@@ -297,10 +313,12 @@ async function skillRoutes(fastify, opts) {
297
313
  return { success: false, error: 'HQ not configured' }
298
314
  }
299
315
 
300
- console.log('[Skills] Listing remote skills from HQ')
316
+ const remoteWorkspaceId = request.query.workspace_id || config.WORKSPACE_ID || ''
317
+ const remoteQuery = remoteWorkspaceId ? `?workspace_id=${encodeURIComponent(remoteWorkspaceId)}` : ''
318
+ console.log(`[Skills] Listing remote skills from HQ${remoteWorkspaceId ? ` (workspace: ${remoteWorkspaceId})` : ''}`)
301
319
 
302
320
  try {
303
- const result = await api.request('/skills')
321
+ const result = await api.request(`/skills${remoteQuery}`)
304
322
  return result
305
323
  } catch (error) {
306
324
  console.error(`[Skills] Failed to list remote skills: ${error.message}`)
@@ -27,6 +27,16 @@ Authentication: `Authorization: Bearer $API_TOKEN` header (except where noted).
27
27
  | POST | `/api/skills/fetch/:name` | Fetch skill from HQ and deploy locally |
28
28
  | GET | `/api/skills/remote` | List skills on HQ |
29
29
 
30
+ #### ワークスペーススコープ
31
+
32
+ スキルはワークスペース単位でスコープされる。`WORKSPACE_ID` 環境変数を設定するか、リクエストごとに `workspace_id` を指定することで、プッシュ先・取得元のワークスペースを制御できる。未指定の場合、HQがミニオンの所属ワークスペースから自動解決する。
33
+
34
+ | エンドポイント | パラメータ | 指定方法 |
35
+ |--------------|-----------|---------|
36
+ | `POST /api/skills/push/:name` | `workspace_id` | リクエストボディ |
37
+ | `POST /api/skills/fetch/:name` | `workspace_id` | クエリパラメータ |
38
+ | `GET /api/skills/remote` | `workspace_id` | クエリパラメータ |
39
+
30
40
  ### Workflows
31
41
 
32
42
  | Method | Endpoint | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekbeer/minion",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "AI Agent runtime for Minion - manages status and skill deployment on VPS",
5
5
  "main": "linux/server.js",
6
6
  "bin": {