@jiggai/recipes 0.4.25 → 0.4.27
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/index.ts
CHANGED
|
@@ -654,10 +654,12 @@ const recipesPlugin = {
|
|
|
654
654
|
.description("Claim and execute a single queued workflow run (intended for cron-driven runner)")
|
|
655
655
|
.requiredOption("--team-id <teamId>", "Team id (workspace-<teamId>)")
|
|
656
656
|
.option("--lease-seconds <n>", "Lease duration in seconds", (v: string) => Number(v))
|
|
657
|
-
.
|
|
657
|
+
.option("--run-id <runId>", "Only claim this specific run id")
|
|
658
|
+
.action(async (options: { teamId?: string; leaseSeconds?: number; runId?: string }) => {
|
|
658
659
|
const res = await handleWorkflowsRunnerOnce(api, {
|
|
659
660
|
teamId: String(options.teamId ?? ""),
|
|
660
661
|
leaseSeconds: typeof options.leaseSeconds === "number" ? options.leaseSeconds : undefined,
|
|
662
|
+
runId: options.runId,
|
|
661
663
|
});
|
|
662
664
|
console.log(JSON.stringify(res, null, 2));
|
|
663
665
|
});
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -17,9 +17,10 @@ export async function handleWorkflowsRun(api: OpenClawPluginApi, opts: {
|
|
|
17
17
|
export async function handleWorkflowsRunnerOnce(api: OpenClawPluginApi, opts: {
|
|
18
18
|
teamId: string;
|
|
19
19
|
leaseSeconds?: number;
|
|
20
|
+
runId?: string;
|
|
20
21
|
}) {
|
|
21
22
|
if (!opts.teamId) throw new Error('--team-id is required');
|
|
22
|
-
return runWorkflowRunnerOnce(api, { teamId: opts.teamId, leaseSeconds: opts.leaseSeconds });
|
|
23
|
+
return runWorkflowRunnerOnce(api, { teamId: opts.teamId, leaseSeconds: opts.leaseSeconds, runId: opts.runId });
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
|
|
@@ -124,6 +124,7 @@ export async function enqueueWorkflowRun(api: OpenClawPluginApi, opts: {
|
|
|
124
124
|
export async function runWorkflowRunnerOnce(api: OpenClawPluginApi, opts: {
|
|
125
125
|
teamId: string;
|
|
126
126
|
leaseSeconds?: number;
|
|
127
|
+
runId?: string;
|
|
127
128
|
}) {
|
|
128
129
|
const teamId = String(opts.teamId);
|
|
129
130
|
const teamDir = resolveTeamDir(api, teamId);
|
|
@@ -169,6 +170,14 @@ export async function runWorkflowRunnerOnce(api: OpenClawPluginApi, opts: {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
// If a specific runId was requested, only consider that run.
|
|
174
|
+
const targetRunId = opts.runId?.trim();
|
|
175
|
+
if (targetRunId) {
|
|
176
|
+
const match = candidates.filter((c) => path.basename(path.dirname(c.file)) === targetRunId);
|
|
177
|
+
candidates.length = 0;
|
|
178
|
+
candidates.push(...match);
|
|
179
|
+
}
|
|
180
|
+
|
|
172
181
|
if (!candidates.length) {
|
|
173
182
|
return { ok: true as const, teamId, claimed: 0, message: 'No queued runs available.' };
|
|
174
183
|
}
|
|
@@ -19,15 +19,21 @@ import {
|
|
|
19
19
|
sanitizeDraftOnlyText, templateReplace,
|
|
20
20
|
} from './workflow-utils';
|
|
21
21
|
|
|
22
|
+
// Max depth for event-driven chaining to prevent runaway recursion.
|
|
23
|
+
const maxChainDepth = 20;
|
|
24
|
+
|
|
22
25
|
// eslint-disable-next-line complexity, max-lines-per-function
|
|
23
26
|
export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
|
|
24
27
|
teamId: string;
|
|
25
28
|
agentId: string;
|
|
26
29
|
limit?: number;
|
|
27
30
|
workerId?: string;
|
|
28
|
-
|
|
31
|
+
/** Disable event-driven chaining (used in tests to control execution order). */
|
|
32
|
+
noChain?: boolean;
|
|
33
|
+
}, chainDepth = 0) {
|
|
29
34
|
const teamId = String(opts.teamId);
|
|
30
35
|
const agentId = String(opts.agentId);
|
|
36
|
+
const noChain = !!opts.noChain;
|
|
31
37
|
if (!teamId) throw new Error('--team-id is required');
|
|
32
38
|
if (!agentId) throw new Error('--agent-id is required');
|
|
33
39
|
|
|
@@ -415,6 +421,44 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
|
|
|
415
421
|
await fs.writeFile(artifactPath, JSON.stringify({ ok: true, tool: toolName, args: toolArgs, result }, null, 2) + '\n', 'utf8');
|
|
416
422
|
|
|
417
423
|
|
|
424
|
+
} else if (toolName === 'fs.write') {
|
|
425
|
+
const relPathRaw = String(toolArgs.path ?? '').trim();
|
|
426
|
+
const contentRaw = String(toolArgs.content ?? '');
|
|
427
|
+
if (!relPathRaw) throw new Error('fs.write requires args.path');
|
|
428
|
+
|
|
429
|
+
const vars = {
|
|
430
|
+
date: new Date().toISOString(),
|
|
431
|
+
'run.id': runId,
|
|
432
|
+
'run.timestamp': runId,
|
|
433
|
+
'workflow.id': String(workflow.id ?? ''),
|
|
434
|
+
'workflow.name': String(workflow.name ?? workflow.id ?? workflowFile),
|
|
435
|
+
};
|
|
436
|
+
// Also inject node outputs so templates like {{brand_review.output}} resolve
|
|
437
|
+
const { run: runSnap } = await loadRunFile(teamDir, runsDir, task.runId);
|
|
438
|
+
for (const nr of (runSnap.nodeResults ?? [])) {
|
|
439
|
+
const nid = String((nr as Record<string, unknown>).nodeId ?? '');
|
|
440
|
+
const nrOutPath = String((nr as Record<string, unknown>).nodeOutputPath ?? '');
|
|
441
|
+
if (nid && nrOutPath) {
|
|
442
|
+
try {
|
|
443
|
+
const outAbs = path.resolve(teamDir, nrOutPath);
|
|
444
|
+
vars[`${nid}.output`] = await fs.readFile(outAbs, 'utf8');
|
|
445
|
+
} catch { /* node output may not exist */ }
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const relPath = templateReplace(relPathRaw, vars);
|
|
449
|
+
const content = templateReplace(contentRaw, vars);
|
|
450
|
+
|
|
451
|
+
const abs = path.resolve(teamDir, relPath);
|
|
452
|
+
if (!abs.startsWith(teamDir + path.sep) && abs !== teamDir) {
|
|
453
|
+
throw new Error('fs.write path must be within the team workspace');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
await ensureDir(path.dirname(abs));
|
|
457
|
+
await fs.writeFile(abs, content, 'utf8');
|
|
458
|
+
|
|
459
|
+
const result = { writtenTo: path.relative(teamDir, abs), bytes: Buffer.byteLength(content, 'utf8') };
|
|
460
|
+
await fs.writeFile(artifactPath, JSON.stringify({ ok: true, tool: toolName, args: toolArgs, result }, null, 2) + '\n', 'utf8');
|
|
461
|
+
|
|
418
462
|
} else if (toolName === 'marketing.post_all') {
|
|
419
463
|
// Disabled by default: do not ship plugins that spawn local processes for posting.
|
|
420
464
|
// Use an approval-gated workflow node that calls a dedicated posting tool/plugin instead.
|
|
@@ -540,6 +584,12 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
|
|
|
540
584
|
events: [...cur.events, { ts: new Date().toISOString(), type: 'node.enqueued', nodeId: nextNode.id, agentId: approvalAgentId }],
|
|
541
585
|
}));
|
|
542
586
|
|
|
587
|
+
// Event-driven chaining: immediately kick the next agent's worker
|
|
588
|
+
if (!noChain && approvalAgentId !== agentId && chainDepth < maxChainDepth) {
|
|
589
|
+
void runWorkflowWorkerTick(api, { teamId, agentId: approvalAgentId, limit: 1, workerId: `${workerId}:chain` }, chainDepth + 1)
|
|
590
|
+
.catch(() => { /* best-effort — cron workers are the safety net */ });
|
|
591
|
+
}
|
|
592
|
+
|
|
543
593
|
results.push({ taskId: task.id, runId: task.runId, nodeId: task.nodeId, status: 'ok' });
|
|
544
594
|
continue;
|
|
545
595
|
}
|
|
@@ -562,6 +612,12 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
|
|
|
562
612
|
events: [...cur.events, { ts: new Date().toISOString(), type: 'node.enqueued', nodeId: nextNode.id, agentId: nextAgentId }],
|
|
563
613
|
}));
|
|
564
614
|
|
|
615
|
+
// Event-driven chaining: immediately kick the next agent's worker
|
|
616
|
+
if (!noChain && nextAgentId !== agentId && chainDepth < maxChainDepth) {
|
|
617
|
+
void runWorkflowWorkerTick(api, { teamId, agentId: nextAgentId, limit: 1, workerId: `${workerId}:chain` }, chainDepth + 1)
|
|
618
|
+
.catch(() => { /* best-effort — cron workers are the safety net */ });
|
|
619
|
+
}
|
|
620
|
+
|
|
565
621
|
results.push({ taskId: task.id, runId: task.runId, nodeId: task.nodeId, status: 'ok' });
|
|
566
622
|
} finally {
|
|
567
623
|
if (lockHeld) {
|