@geekbeer/minion 3.16.1 → 3.17.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/lib/dag-step-poller.js +147 -4
- package/package.json +1 -1
|
@@ -98,13 +98,24 @@ async function pollOnce() {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
* Execute a single pending DAG node
|
|
101
|
+
* Execute a single pending DAG node.
|
|
102
|
+
* Routes to skill or transform execution based on node_type.
|
|
103
|
+
*/
|
|
104
|
+
async function executeNode(node) {
|
|
105
|
+
if (node.node_type === 'transform') {
|
|
106
|
+
return executeTransformNode(node)
|
|
107
|
+
}
|
|
108
|
+
return executeSkillNode(node)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Execute a skill node:
|
|
102
113
|
* 1. Claim the node
|
|
103
114
|
* 2. Fetch the skill from HQ
|
|
104
115
|
* 3. Run the skill locally (with input_data injected)
|
|
105
116
|
* 4. Report completion with output_data
|
|
106
117
|
*/
|
|
107
|
-
async function
|
|
118
|
+
async function executeSkillNode(node) {
|
|
108
119
|
const {
|
|
109
120
|
node_execution_id,
|
|
110
121
|
execution_id,
|
|
@@ -119,7 +130,7 @@ async function executeNode(node) {
|
|
|
119
130
|
} = node
|
|
120
131
|
|
|
121
132
|
console.log(
|
|
122
|
-
`[DagPoller] Executing node "${node_id}" of DAG "${dag_workflow_name}" ` +
|
|
133
|
+
`[DagPoller] Executing skill node "${node_id}" of DAG "${dag_workflow_name}" ` +
|
|
123
134
|
`(skill: ${resolvedSkillName || skill_version_id}, scope: "${scope_path || 'root'}", role: ${assigned_role})`
|
|
124
135
|
)
|
|
125
136
|
|
|
@@ -212,15 +223,147 @@ async function executeNode(node) {
|
|
|
212
223
|
// This will be handled by dag-node-executor.js which watches the session.
|
|
213
224
|
|
|
214
225
|
} catch (err) {
|
|
215
|
-
console.error(`[DagPoller] Failed to execute node ${node_id}: ${err.message}`)
|
|
226
|
+
console.error(`[DagPoller] Failed to execute skill node ${node_id}: ${err.message}`)
|
|
227
|
+
try {
|
|
228
|
+
await reportNodeComplete(node_execution_id, 'failed', null, err.message)
|
|
229
|
+
} catch {
|
|
230
|
+
// best-effort
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Execute a transform node:
|
|
237
|
+
* 1. Claim the node
|
|
238
|
+
* 2. Create an ephemeral skill from the transform_instruction
|
|
239
|
+
* 3. Run the ephemeral skill via existing /api/skills/run
|
|
240
|
+
* 4. Clean up ephemeral skill directory
|
|
241
|
+
*/
|
|
242
|
+
async function executeTransformNode(node) {
|
|
243
|
+
const {
|
|
244
|
+
node_execution_id,
|
|
245
|
+
execution_id,
|
|
246
|
+
dag_workflow_name,
|
|
247
|
+
node_id,
|
|
248
|
+
scope_path,
|
|
249
|
+
assigned_role,
|
|
250
|
+
input_data,
|
|
251
|
+
transform_instruction,
|
|
252
|
+
} = node
|
|
253
|
+
|
|
254
|
+
console.log(
|
|
255
|
+
`[DagPoller] Executing transform node "${node_id}" of DAG "${dag_workflow_name}" ` +
|
|
256
|
+
`(scope: "${scope_path || 'root'}", role: ${assigned_role})`
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
const ephemeralName = `_dag_transform_${node_execution_id.substring(0, 8)}`
|
|
260
|
+
const fs = require('fs').promises
|
|
261
|
+
const path = require('path')
|
|
262
|
+
const skillDir = path.join(config.HOME_DIR || process.env.HOME, '.claude', 'skills', ephemeralName)
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
// 1. Claim the node
|
|
266
|
+
try {
|
|
267
|
+
await dagRequest('/claim-node', {
|
|
268
|
+
method: 'POST',
|
|
269
|
+
body: JSON.stringify({ node_execution_id }),
|
|
270
|
+
})
|
|
271
|
+
} catch (claimErr) {
|
|
272
|
+
if (claimErr.statusCode === 409) {
|
|
273
|
+
console.log(`[DagPoller] Transform node ${node_id} already claimed, skipping`)
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
throw claimErr
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// 2. Create ephemeral skill from transform_instruction
|
|
280
|
+
const skillContent = buildTransformSkillContent(transform_instruction, input_data)
|
|
281
|
+
await fs.mkdir(skillDir, { recursive: true })
|
|
282
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent, 'utf-8')
|
|
283
|
+
|
|
284
|
+
// 3. Run the ephemeral skill
|
|
285
|
+
const runPayload = {
|
|
286
|
+
skill_name: ephemeralName,
|
|
287
|
+
execution_id,
|
|
288
|
+
workflow_name: dag_workflow_name,
|
|
289
|
+
role: assigned_role,
|
|
290
|
+
dag_node_id: node_id,
|
|
291
|
+
dag_input_data: input_data,
|
|
292
|
+
dag_node_execution_id: node_execution_id,
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const runUrl = `http://localhost:${config.AGENT_PORT || 8080}/api/skills/run`
|
|
296
|
+
const runResp = await fetch(runUrl, {
|
|
297
|
+
method: 'POST',
|
|
298
|
+
headers: {
|
|
299
|
+
'Content-Type': 'application/json',
|
|
300
|
+
'Authorization': `Bearer ${config.API_TOKEN}`,
|
|
301
|
+
},
|
|
302
|
+
body: JSON.stringify(runPayload),
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
if (!runResp.ok) {
|
|
306
|
+
const errData = await runResp.json().catch(() => ({}))
|
|
307
|
+
console.error(`[DagPoller] Transform run failed: ${errData.error || runResp.status}`)
|
|
308
|
+
await reportNodeComplete(
|
|
309
|
+
node_execution_id,
|
|
310
|
+
'failed',
|
|
311
|
+
null,
|
|
312
|
+
`Failed to start transform: ${errData.error || 'unknown error'}`
|
|
313
|
+
)
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const runData = await runResp.json()
|
|
318
|
+
console.log(
|
|
319
|
+
`[DagPoller] Transform started for node "${node_id}" ` +
|
|
320
|
+
`(session: ${runData.session_name}). Completion reported by post-execution hook.`
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
} catch (err) {
|
|
324
|
+
console.error(`[DagPoller] Failed to execute transform node ${node_id}: ${err.message}`)
|
|
216
325
|
try {
|
|
217
326
|
await reportNodeComplete(node_execution_id, 'failed', null, err.message)
|
|
218
327
|
} catch {
|
|
219
328
|
// best-effort
|
|
220
329
|
}
|
|
330
|
+
} finally {
|
|
331
|
+
// 4. Clean up ephemeral skill directory
|
|
332
|
+
try {
|
|
333
|
+
await fs.rm(skillDir, { recursive: true, force: true })
|
|
334
|
+
} catch {
|
|
335
|
+
// best-effort cleanup
|
|
336
|
+
}
|
|
221
337
|
}
|
|
222
338
|
}
|
|
223
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Build SKILL.md content for a transform node's ephemeral skill.
|
|
342
|
+
*/
|
|
343
|
+
function buildTransformSkillContent(instruction, inputData) {
|
|
344
|
+
return [
|
|
345
|
+
'---',
|
|
346
|
+
'name: dag-transform',
|
|
347
|
+
'description: DAG Transform Node',
|
|
348
|
+
'---',
|
|
349
|
+
'',
|
|
350
|
+
'You are a data transformation step in a DAG workflow.',
|
|
351
|
+
'',
|
|
352
|
+
'## Input Data',
|
|
353
|
+
'```json',
|
|
354
|
+
JSON.stringify(inputData, null, 2),
|
|
355
|
+
'```',
|
|
356
|
+
'',
|
|
357
|
+
'## Transform Instruction',
|
|
358
|
+
instruction,
|
|
359
|
+
'',
|
|
360
|
+
'## Task',
|
|
361
|
+
'Apply the transform instruction to the input data above.',
|
|
362
|
+
'Output the result as a JSON object in an "## Output Data" section with a json code block.',
|
|
363
|
+
'Do NOT output anything other than the Output Data section.',
|
|
364
|
+
].join('\n')
|
|
365
|
+
}
|
|
366
|
+
|
|
224
367
|
/**
|
|
225
368
|
* Resolve skill_version_id to skill name via HQ API.
|
|
226
369
|
*/
|