@jiggai/recipes 0.4.59 → 0.4.62
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/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -655,9 +655,16 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
|
|
|
655
655
|
} else {
|
|
656
656
|
// The lock is still live and held by another worker. The cursor has
|
|
657
657
|
// already advanced past this task, so under naive logic we'd lose it.
|
|
658
|
-
// Re-enqueue — but ONLY if no equivalent task is already pending
|
|
659
|
-
//
|
|
660
|
-
//
|
|
658
|
+
// Re-enqueue — but ONLY if no equivalent task is already pending
|
|
659
|
+
// AND the run still exists. Without the run.json check, a lock-held
|
|
660
|
+
// task for a deleted run gets endlessly re-enqueued, crash-looping
|
|
661
|
+
// every tick that dequeues it.
|
|
662
|
+
const runStillExists = await fileExists(runFilePathFor(runsDir, task.runId));
|
|
663
|
+
if (!runStillExists) {
|
|
664
|
+
countedTowardLimit = false;
|
|
665
|
+
results.push({ taskId: task.id, runId: task.runId, nodeId: task.nodeId, status: 'skipped_stale' });
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
661
668
|
const alreadyPending = await hasPendingTaskFor(teamDir, agentId, {
|
|
662
669
|
runId: task.runId,
|
|
663
670
|
nodeId: task.nodeId,
|
|
@@ -678,14 +685,35 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
|
|
|
678
685
|
|
|
679
686
|
const runId = task.runId;
|
|
680
687
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
688
|
+
// loadRunFile + workflow/node lookup can throw if the run dir was
|
|
689
|
+
// removed out from under us (manual cleanup, workspace reset) or if
|
|
690
|
+
// the workflow JSON renamed/removed this node between enqueue and
|
|
691
|
+
// dequeue. The outer try has only a finally, so a throw here would
|
|
692
|
+
// escape the tick and crash the CLI — which in a 1-minute cron loop
|
|
693
|
+
// means every subsequent tick dies the same way until an operator
|
|
694
|
+
// manually drains the queue. Skip stale instead.
|
|
695
|
+
let run: RunLog;
|
|
696
|
+
let workflow: ReturnType<typeof normalizeWorkflow>;
|
|
697
|
+
let workflowFile: string;
|
|
698
|
+
let nodeIdx: number;
|
|
699
|
+
try {
|
|
700
|
+
({ run } = await loadRunFile(teamDir, runsDir, runId));
|
|
701
|
+
workflowFile = String(run.workflow.file);
|
|
702
|
+
const workflowPath = path.join(workflowsDir, workflowFile);
|
|
703
|
+
const workflowRaw = await readTextFile(workflowPath);
|
|
704
|
+
workflow = normalizeWorkflow(JSON.parse(workflowRaw));
|
|
705
|
+
nodeIdx = workflow.nodes.findIndex((n) => String(n.id) === String(task.nodeId));
|
|
706
|
+
if (nodeIdx < 0) throw new Error(`Node not found in workflow: ${task.nodeId}`);
|
|
707
|
+
} catch (err) {
|
|
708
|
+
countedTowardLimit = false;
|
|
709
|
+
// Log the reason so operators can diagnose; don't add it to the
|
|
710
|
+
// result shape (kept narrow for downstream consumers).
|
|
711
|
+
try {
|
|
712
|
+
console.warn(`[workflow-worker] skip_stale taskId=${task.id} runId=${task.runId} nodeId=${task.nodeId} reason=${(err as Error)?.message ?? String(err)}`);
|
|
713
|
+
} catch { /* log best-effort */ }
|
|
714
|
+
results.push({ taskId: task.id, runId: task.runId, nodeId: task.nodeId, status: 'skipped_stale' });
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
689
717
|
const node = workflow.nodes[nodeIdx]!;
|
|
690
718
|
|
|
691
719
|
// Now that we know the node, tighten the lock TTL based on node.config.timeoutMs.
|