@yemi33/minions 0.1.592 → 0.1.594
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 +5 -1
- package/dashboard/js/refresh.js +10 -1
- package/dashboard/js/render-pipelines.js +71 -0
- package/dashboard/pages/engine.html +3 -0
- package/dashboard.js +20 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.594 (2026-04-08)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- show worktree count and engine quick stats on engine page
|
|
4
7
|
|
|
5
8
|
### Fixes
|
|
9
|
+
- Pipeline UI show monitored resources in pipeline card (closes #523) (#542)
|
|
6
10
|
- move 'When to Stop' after Build/Test in implement playbook
|
|
7
11
|
- per-type maxTurns bypassed when config has default maxTurns=100
|
|
8
12
|
|
package/dashboard/js/refresh.js
CHANGED
|
@@ -64,7 +64,16 @@ function _processStatusUpdate(data) {
|
|
|
64
64
|
if (_changed('prd', [data.prd, data.prdProgress])) renderPrd(data.prd, data.prdProgress);
|
|
65
65
|
if (_changed('prs', data.pullRequests)) renderPrs(data.pullRequests || []);
|
|
66
66
|
if (_changed('archivedPrds', data.archivedPrds)) renderArchiveButtons(data.archivedPrds || []);
|
|
67
|
-
if (_changed('engine', data.engine))
|
|
67
|
+
if (_changed('engine', data.engine)) {
|
|
68
|
+
renderEngineStatus(data.engine);
|
|
69
|
+
var qs = document.getElementById('engine-quick-stats');
|
|
70
|
+
if (qs && data.engine) {
|
|
71
|
+
var wt = data.engine.worktreeCount != null ? data.engine.worktreeCount : '-';
|
|
72
|
+
var tick = data.engine.tick || '-';
|
|
73
|
+
var pid = data.engine.pid || '-';
|
|
74
|
+
qs.innerHTML = '<span>PID: <b>' + pid + '</b></span><span>Tick: <b>' + tick + '</b></span><span>Worktrees: <b>' + wt + '</b></span>';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
68
77
|
if (_changed('version', data.version)) renderVersionBanner(data.version);
|
|
69
78
|
if (_changed('dispatch', data.dispatch)) renderDispatch(data.dispatch);
|
|
70
79
|
window._lastDispatch = data.dispatch;
|
|
@@ -5,6 +5,62 @@ let _pipelinePollId = null;
|
|
|
5
5
|
let _pipelinePollInterval = null;
|
|
6
6
|
function _stopPipelinePoll() { if (_pipelinePollInterval) { clearInterval(_pipelinePollInterval); _pipelinePollInterval = null; } _pipelinePollId = null; }
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Collect all monitoredResources from a pipeline (pipeline-level + all stages).
|
|
10
|
+
* Returns a deduplicated array of resource objects.
|
|
11
|
+
*/
|
|
12
|
+
function _collectMonitoredResources(pipeline) {
|
|
13
|
+
var seen = new Set();
|
|
14
|
+
var result = [];
|
|
15
|
+
function add(r) {
|
|
16
|
+
var key = typeof r === 'string' ? r : (r.url || r.label || JSON.stringify(r));
|
|
17
|
+
if (seen.has(key)) return;
|
|
18
|
+
seen.add(key);
|
|
19
|
+
result.push(typeof r === 'string' ? { label: r, url: r } : r);
|
|
20
|
+
}
|
|
21
|
+
(pipeline.monitoredResources || []).forEach(add);
|
|
22
|
+
(pipeline.stages || []).forEach(function(s) { (s.monitoredResources || []).forEach(add); });
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Render monitored resources as compact pills on a pipeline card or stage detail.
|
|
28
|
+
* Supports both string resources (URLs/IDs) and objects with {type, label, url}.
|
|
29
|
+
* @param {Array} resources - array of resource strings or {type?, label, url?} objects
|
|
30
|
+
* @param {Object} [options] - { compact: true } limits display and shows "+N more"
|
|
31
|
+
* @returns {string} HTML string
|
|
32
|
+
*/
|
|
33
|
+
function _renderMonitoredResources(resources, options) {
|
|
34
|
+
if (!resources || resources.length === 0) return '';
|
|
35
|
+
var compact = options && options.compact;
|
|
36
|
+
var maxShow = compact ? 4 : resources.length;
|
|
37
|
+
var shown = resources.slice(0, maxShow);
|
|
38
|
+
var overflow = resources.length - maxShow;
|
|
39
|
+
|
|
40
|
+
var iconMap = { pr: '🔀', workitem: '⚙', url: '🔗', issue: '🐛' };
|
|
41
|
+
var pillStyle = 'display:inline-flex;align-items:center;gap:2px;padding:1px 6px;border-radius:10px;font-size:10px;text-decoration:none;' +
|
|
42
|
+
'color:var(--text);background:color-mix(in srgb, var(--muted) 10%, transparent);border:1px solid color-mix(in srgb, var(--muted) 20%, transparent);max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
43
|
+
|
|
44
|
+
var pills = shown.map(function(r) {
|
|
45
|
+
var res = typeof r === 'string' ? { label: r, url: r.startsWith('http') ? r : '' } : r;
|
|
46
|
+
var icon = iconMap[res.type] || (res.url ? '🔗' : '📌');
|
|
47
|
+
var label = res.label || res.url || '(resource)';
|
|
48
|
+
// Truncate label for compact view
|
|
49
|
+
var displayLabel = compact && label.length > 28 ? label.slice(0, 26) + '…' : label;
|
|
50
|
+
if (res.url) {
|
|
51
|
+
return '<a href="' + escHtml(res.url) + '" target="_blank" rel="noopener" style="' + pillStyle + ';cursor:pointer" onclick="event.stopPropagation()" title="' + escHtml(label) + '">' + icon + ' ' + escHtml(displayLabel) + '</a>';
|
|
52
|
+
}
|
|
53
|
+
return '<span style="' + pillStyle + ';cursor:default" title="' + escHtml(label) + '">' + icon + ' ' + escHtml(displayLabel) + '</span>';
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (overflow > 0) {
|
|
57
|
+
pills.push('<span style="' + pillStyle + ';cursor:default;opacity:0.7" title="' + overflow + ' more resource' + (overflow !== 1 ? 's' : '') + '">+' + overflow + ' more</span>');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
var heading = compact ? '' : '<span style="font-size:10px;color:var(--muted);margin-right:4px">Monitoring:</span>';
|
|
61
|
+
return '<div style="margin-top:4px;display:flex;flex-wrap:wrap;gap:3px;align-items:center">' + heading + pills.join('') + '</div>';
|
|
62
|
+
}
|
|
63
|
+
|
|
8
64
|
/**
|
|
9
65
|
* Render clickable artifact links for a pipeline stage.
|
|
10
66
|
* Each artifact type gets an icon and navigates to the relevant detail view.
|
|
@@ -154,6 +210,10 @@ function renderPipelines(pipelines) {
|
|
|
154
210
|
progressHtml = _buildProgressBar(p.stages || [], displayRun);
|
|
155
211
|
}
|
|
156
212
|
|
|
213
|
+
// Monitored resources (pipeline-level + stage-level, compact on card)
|
|
214
|
+
var allResources = _collectMonitoredResources(p);
|
|
215
|
+
var resourcesHtml = _renderMonitoredResources(allResources, { compact: true });
|
|
216
|
+
|
|
157
217
|
return '<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 16px;margin-bottom:8px;cursor:pointer" onclick="openPipelineDetail(\'' + escHtml(p.id) + '\')">' +
|
|
158
218
|
'<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
159
219
|
'<strong style="font-size:13px">' + escHtml(p.title) + '</strong>' +
|
|
@@ -164,6 +224,7 @@ function renderPipelines(pipelines) {
|
|
|
164
224
|
'</div>' +
|
|
165
225
|
'</div>' +
|
|
166
226
|
'<div style="margin-top:6px;display:flex;gap:4px;align-items:center;flex-wrap:wrap">' + stageFlow + '</div>' +
|
|
227
|
+
resourcesHtml +
|
|
167
228
|
progressHtml +
|
|
168
229
|
'</div>';
|
|
169
230
|
}).join('');
|
|
@@ -196,6 +257,15 @@ function openPipelineDetail(id) {
|
|
|
196
257
|
if (detailRun && (p.stages || []).length > 0) {
|
|
197
258
|
html += _buildProgressBar(p.stages || [], detailRun, { height: '8px', detailLabel: true });
|
|
198
259
|
}
|
|
260
|
+
// Pipeline-level monitored resources (full view in detail)
|
|
261
|
+
var pipelineResources = _collectMonitoredResources(p);
|
|
262
|
+
if (pipelineResources.length > 0) {
|
|
263
|
+
html += '<div style="border:1px solid color-mix(in srgb, var(--blue) 20%, transparent);border-radius:6px;padding:6px 10px;background:color-mix(in srgb, var(--blue) 4%, transparent)">' +
|
|
264
|
+
'<span style="font-size:10px;font-weight:600;color:var(--blue)">📡 Monitored Resources</span>' +
|
|
265
|
+
_renderMonitoredResources(pipelineResources) +
|
|
266
|
+
'</div>';
|
|
267
|
+
}
|
|
268
|
+
|
|
199
269
|
html += '<h4 style="font-size:12px;color:var(--blue);margin:0">Stages</h4>';
|
|
200
270
|
(p.stages || []).forEach(function(s, i) {
|
|
201
271
|
var stageRun = activeRun?.stages?.[s.id] || {};
|
|
@@ -209,6 +279,7 @@ function openPipelineDetail(id) {
|
|
|
209
279
|
'<span style="color:' + statusColor + ';font-size:10px;font-weight:600">' + stageStatus.toUpperCase() + '</span>' +
|
|
210
280
|
'</div>' +
|
|
211
281
|
'<div style="font-size:10px;color:var(--muted);margin-top:4px">Type: ' + escHtml(s.type) + ' | Depends on: ' + escHtml(deps) + (s.agent ? ' | Agent: ' + escHtml(s.agent) : '') + '</div>' +
|
|
282
|
+
_renderMonitoredResources(s.monitoredResources || []) +
|
|
212
283
|
_renderArtifactLinks(stageRun.artifacts) +
|
|
213
284
|
(stageRun.output ? '<div style="margin-top:6px;font-size:11px;max-height:150px;overflow-y:auto">' + renderMd(stageRun.output.slice(0, 500)) + '</div>' : '') +
|
|
214
285
|
(stageStatus === 'waiting-human' ? '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--green);border-color:var(--green);margin-top:6px" onclick="_continuePipeline(\'' + escHtml(id) + '\',\'' + escHtml(s.id) + '\',this)">Continue</button>' : '') +
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
<section>
|
|
2
|
+
<div id="engine-quick-stats" style="display:flex;gap:16px;margin-bottom:12px;font-size:11px;color:var(--muted)"></div>
|
|
3
|
+
</section>
|
|
1
4
|
<section>
|
|
2
5
|
<h2>Engine Log <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">tick-by-tick audit trail of engine operations</span></h2>
|
|
3
6
|
<div class="log-list" id="engine-log">No log entries yet.</div>
|
package/dashboard.js
CHANGED
|
@@ -167,6 +167,21 @@ function getVerifyGuides() {
|
|
|
167
167
|
function getArchivedPrds() { return []; }
|
|
168
168
|
function getEngineState() { return queries.getControl(); }
|
|
169
169
|
|
|
170
|
+
function _countWorktrees() {
|
|
171
|
+
try {
|
|
172
|
+
const config = queries.getConfig();
|
|
173
|
+
const projects = queries.getProjects(config);
|
|
174
|
+
let count = 0;
|
|
175
|
+
for (const p of projects) {
|
|
176
|
+
const root = p.localPath ? path.resolve(p.localPath) : null;
|
|
177
|
+
if (!root) continue;
|
|
178
|
+
const wtRoot = path.resolve(root, config.engine?.worktreeRoot || shared.ENGINE_DEFAULTS.worktreeRoot);
|
|
179
|
+
try { count += fs.readdirSync(wtRoot).filter(f => fs.statSync(path.join(wtRoot, f)).isDirectory()).length; } catch {}
|
|
180
|
+
}
|
|
181
|
+
return count;
|
|
182
|
+
} catch { return 0; }
|
|
183
|
+
}
|
|
184
|
+
|
|
170
185
|
// ── npm update check ────────────────────────────────────────────────────────
|
|
171
186
|
let _npmVersionCache = null;
|
|
172
187
|
let _npmVersionCacheTs = 0;
|
|
@@ -347,7 +362,7 @@ function getStatus() {
|
|
|
347
362
|
pullRequests: getPullRequests(),
|
|
348
363
|
verifyGuides: getVerifyGuides(),
|
|
349
364
|
archivedPrds: getArchivedPrds(),
|
|
350
|
-
engine: getEngineState(),
|
|
365
|
+
engine: { ...getEngineState(), worktreeCount: _countWorktrees() },
|
|
351
366
|
dispatch: getDispatchQueue(),
|
|
352
367
|
engineLog: getEngineLog(),
|
|
353
368
|
metrics: getMetrics(),
|
|
@@ -3937,17 +3952,18 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3937
3952
|
const result = pipelines.map(p => ({ ...p, runs: (runs[p.id] || []).slice(-5) }));
|
|
3938
3953
|
return jsonReply(res, 200, result);
|
|
3939
3954
|
}},
|
|
3940
|
-
{ method: 'POST', path: '/api/pipelines', desc: 'Create a pipeline', params: 'id, title, stages[], trigger?', handler: async (req, res) => {
|
|
3955
|
+
{ method: 'POST', path: '/api/pipelines', desc: 'Create a pipeline', params: 'id, title, stages[], trigger?, monitoredResources?', handler: async (req, res) => {
|
|
3941
3956
|
const body = await readBody(req);
|
|
3942
3957
|
if (!body.id || !body.title || !body.stages) return jsonReply(res, 400, { error: 'id, title, and stages required' });
|
|
3943
3958
|
const { savePipeline, getPipeline } = require('./engine/pipeline');
|
|
3944
3959
|
if (getPipeline(body.id)) return jsonReply(res, 409, { error: 'Pipeline already exists' });
|
|
3945
3960
|
const pipeline = { id: body.id, title: body.title, stages: body.stages, trigger: body.trigger || {}, enabled: body.enabled !== false };
|
|
3961
|
+
if (Array.isArray(body.monitoredResources) && body.monitoredResources.length > 0) pipeline.monitoredResources = body.monitoredResources;
|
|
3946
3962
|
savePipeline(pipeline);
|
|
3947
3963
|
invalidateStatusCache();
|
|
3948
3964
|
return jsonReply(res, 200, { ok: true, id: pipeline.id });
|
|
3949
3965
|
}},
|
|
3950
|
-
{ method: 'POST', path: '/api/pipelines/update', desc: 'Update a pipeline', params: 'id, title?, stages?, trigger?, enabled?', handler: async (req, res) => {
|
|
3966
|
+
{ method: 'POST', path: '/api/pipelines/update', desc: 'Update a pipeline', params: 'id, title?, stages?, trigger?, enabled?, monitoredResources?', handler: async (req, res) => {
|
|
3951
3967
|
const body = await readBody(req);
|
|
3952
3968
|
if (!body.id) return jsonReply(res, 400, { error: 'id required' });
|
|
3953
3969
|
const { getPipeline, savePipeline } = require('./engine/pipeline');
|
|
@@ -3957,6 +3973,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3957
3973
|
if (body.stages !== undefined) pipeline.stages = body.stages;
|
|
3958
3974
|
if (body.trigger !== undefined) pipeline.trigger = body.trigger;
|
|
3959
3975
|
if (body.enabled !== undefined) pipeline.enabled = body.enabled;
|
|
3976
|
+
if (body.monitoredResources !== undefined) pipeline.monitoredResources = body.monitoredResources;
|
|
3960
3977
|
savePipeline(pipeline);
|
|
3961
3978
|
invalidateStatusCache();
|
|
3962
3979
|
return jsonReply(res, 200, { ok: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.594",
|
|
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"
|