@yemi33/minions 0.1.858 → 0.1.859

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/CHANGELOG.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.858 (2026-04-11)
3
+ ## 0.1.859 (2026-04-11)
4
4
 
5
5
  ### Fixes
6
+ - pipeline race condition, delete refresh, abort error logging
6
7
  - _continuePipeline now calls refresh() to update card list
7
8
  - pipeline modal refresh was silently broken — API returns array not object
8
9
  - pipeline modal now auto-updates on all action buttons
@@ -521,7 +521,8 @@ async function _deletePipelineConfirm(id) {
521
521
  showToast('cmd-toast', 'Pipeline deleted', true);
522
522
  try {
523
523
  var res = await fetch('/api/pipelines/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id }) });
524
- if (!res.ok) { showToast('cmd-toast', 'Delete failed', false); refresh(); }
524
+ if (!res.ok) { showToast('cmd-toast', 'Delete failed', false); }
525
+ refresh();
525
526
  } catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
526
527
  }
527
528
 
package/dashboard.js CHANGED
@@ -4446,8 +4446,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
4446
4446
  if (!pipeline) return jsonReply(res, 404, { error: 'Pipeline not found' });
4447
4447
  if (getActiveRun(body.id)) return jsonReply(res, 409, { error: 'Pipeline already has an active run' });
4448
4448
  const run = startRun(body.id, pipeline);
4449
+ if (!run) return jsonReply(res, 409, { error: 'Pipeline already has an active run' });
4449
4450
  invalidateStatusCache();
4450
- return jsonReply(res, 200, { ok: true, runId: run.runId });
4451
+ return jsonReply(res, 200, { ok: true, runId: run?.runId });
4451
4452
  }},
4452
4453
  { method: 'POST', path: '/api/pipelines/continue', desc: 'Continue a pipeline past a wait stage', params: 'id, stageId', handler: async (req, res) => {
4453
4454
  const body = await readBody(req);
@@ -4482,7 +4483,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
4482
4483
  }
4483
4484
  }
4484
4485
  });
4485
- } catch {}
4486
+ } catch (e) { console.error(`Pipeline abort: WI cancel error: ${e.message}`); }
4486
4487
  }
4487
4488
  const dispatchCleaned = cleanDispatchEntries(d => d.meta?.item?._pipelineRun === run.runId);
4488
4489
  invalidateStatusCache();
@@ -75,14 +75,24 @@ function startRun(pipelineId, pipeline) {
75
75
  }
76
76
  const run = { runId, pipelineId, startedAt: ts(), status: PIPELINE_STATUS.RUNNING, stages };
77
77
 
78
+ let alreadyActive = false;
78
79
  mutateJsonFileLocked(PIPELINE_RUNS_PATH, (data) => {
79
80
  if (!data[pipelineId]) data[pipelineId] = [];
81
+ // Guard: skip if there's already an active run (prevents race between ticks)
82
+ if (data[pipelineId].some(r => r.status === PIPELINE_STATUS.RUNNING || r.status === PIPELINE_STATUS.PAUSED)) {
83
+ alreadyActive = true;
84
+ return data;
85
+ }
80
86
  // Keep last 10 runs per pipeline
81
87
  if (data[pipelineId].length >= 10) data[pipelineId] = data[pipelineId].slice(-9);
82
88
  data[pipelineId].push(run);
83
89
  return data;
84
90
  }, { defaultValue: {} });
85
91
 
92
+ if (alreadyActive) {
93
+ log('info', `Pipeline ${pipelineId}: skipped startRun — active run already exists`);
94
+ return null;
95
+ }
86
96
  log('info', `Pipeline ${pipelineId}: started run ${runId}`);
87
97
  return run;
88
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.858",
3
+ "version": "0.1.859",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"