@yemi33/minions 0.1.1730 → 0.1.1732
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
|
@@ -268,6 +268,13 @@ function _buildNodeChain(stages, run, options) {
|
|
|
268
268
|
return html;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
function _renderPipelineTriggerLabel(cron) {
|
|
272
|
+
if (!cron) return 'Manual trigger';
|
|
273
|
+
var human = _cronToHuman(cron);
|
|
274
|
+
var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
275
|
+
return escHtml(human) + ' <span style="opacity:0.6">(' + escHtml(tz) + ')</span>';
|
|
276
|
+
}
|
|
277
|
+
|
|
271
278
|
function renderPipelines(pipelines) {
|
|
272
279
|
pipelines = (pipelines || []).filter(function(p) { return !isDeleted('pipeline:' + p.id); });
|
|
273
280
|
_pipelinesData = pipelines;
|
|
@@ -331,7 +338,7 @@ function openPipelineDetail(id) {
|
|
|
331
338
|
// Status + actions
|
|
332
339
|
var activeRun = _getPipelineActiveRun(p);
|
|
333
340
|
html += '<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
334
|
-
'<span style="font-size:10px;color:var(--muted)">' + (p.trigger?.cron
|
|
341
|
+
'<span style="font-size:10px;color:var(--muted)">' + _renderPipelineTriggerLabel(p.trigger?.cron) + ' · ' + escHtml(_getPipelineStageLabel(p)) + '</span>' +
|
|
335
342
|
'<div style="display:flex;gap:6px">' +
|
|
336
343
|
(activeRun
|
|
337
344
|
? '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_abortPipeline(\'' + escHtml(id) + '\',this)">Abort</button>' +
|
|
@@ -5,14 +5,50 @@
|
|
|
5
5
|
const _DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
6
6
|
const _DAY_NAMES = ['Sundays', 'Mondays', 'Tuesdays', 'Wednesdays', 'Thursdays', 'Fridays', 'Saturdays'];
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function _formatCronDowList(days, timeStr, fallback) {
|
|
9
|
+
const normalized = [...new Set(days)].sort((a, b) => a - b);
|
|
10
|
+
const key = normalized.join(',');
|
|
11
|
+
if (key === '0,1,2,3,4,5,6') return 'Daily at ' + timeStr;
|
|
12
|
+
if (key === '1,2,3,4,5') return 'Weekdays at ' + timeStr;
|
|
13
|
+
if (key === '0,6') return 'Weekends at ' + timeStr;
|
|
14
|
+
if (normalized.length === 1) return _DAY_NAMES[normalized[0]] + ' at ' + timeStr;
|
|
15
|
+
if (normalized.length > 1) return normalized.map(d => _DAY_NAMES[d]).join(', ') + ' at ' + timeStr;
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function _parseCronDowList(dow, allowSevenAsSunday) {
|
|
20
|
+
if (!dow || typeof dow !== 'string') return null;
|
|
21
|
+
const values = [];
|
|
22
|
+
const parts = dow.split(',');
|
|
23
|
+
for (const part of parts) {
|
|
24
|
+
const token = part.trim();
|
|
25
|
+
if (!/^\d+$/.test(token)) return null;
|
|
26
|
+
let day = parseInt(token, 10);
|
|
27
|
+
if (allowSevenAsSunday && day === 7) day = 0;
|
|
28
|
+
if (day < 0 || day > 6) return null;
|
|
29
|
+
values.push(day);
|
|
30
|
+
}
|
|
31
|
+
return values;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function _cronTimeString(minute, hour) {
|
|
35
|
+
const h = parseInt(hour, 10);
|
|
36
|
+
const m = parseInt(minute, 10);
|
|
37
|
+
if (isNaN(h) || isNaN(m)) return null;
|
|
38
|
+
return String(h).padStart(2, '0') + ':' + String(m).padStart(2, '0');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Convert 3-field Minions cron or common 5-field cron to human-readable text */
|
|
9
42
|
function _cronToHuman(cron) {
|
|
10
43
|
if (!cron || typeof cron !== 'string') return cron || '';
|
|
11
44
|
const parts = cron.trim().split(/\s+/);
|
|
12
|
-
if (parts.length !== 3) return cron;
|
|
13
|
-
const
|
|
45
|
+
if (parts.length !== 3 && parts.length !== 5) return cron;
|
|
46
|
+
const minute = parts[0];
|
|
47
|
+
const hour = parts[1];
|
|
48
|
+
const dow = parts.length === 3 ? parts[2] : parts[4];
|
|
14
49
|
|
|
15
|
-
if (
|
|
50
|
+
if (parts.length === 5 && (parts[2] !== '*' || parts[3] !== '*')) return cron;
|
|
51
|
+
if (parts.length === 3 && minute === '*' && hour === '*' && dow === '*') return 'Every minute';
|
|
16
52
|
|
|
17
53
|
const h = parseInt(hour, 10);
|
|
18
54
|
const m = parseInt(minute, 10);
|
|
@@ -26,13 +62,20 @@ function _cronToHuman(cron) {
|
|
|
26
62
|
const normalized = dow.split(',').map(d => d.trim()).sort().join(',');
|
|
27
63
|
if (dow === '1-5' || normalized === '1,2,3,4,5') return 'Weekdays at ' + timeStr;
|
|
28
64
|
if (normalized === '0,6' || normalized === '6,0') return 'Weekends at ' + timeStr;
|
|
65
|
+
if (parts.length === 5 && (dow === '0-6' || dow === '0-7')) return 'Daily at ' + timeStr;
|
|
29
66
|
|
|
30
67
|
// Single day
|
|
31
|
-
|
|
32
|
-
if (
|
|
68
|
+
let dayNum = parseInt(dow, 10);
|
|
69
|
+
if (parts.length === 5 && dayNum === 7 && dow === '7') dayNum = 0;
|
|
70
|
+
if (!isNaN(dayNum) && dayNum >= 0 && dayNum <= 6 && String(parseInt(dow, 10)) === dow) {
|
|
33
71
|
return _DAY_NAMES[dayNum] + ' at ' + timeStr;
|
|
34
72
|
}
|
|
35
73
|
|
|
74
|
+
if (parts.length === 5) {
|
|
75
|
+
const days = _parseCronDowList(dow, true);
|
|
76
|
+
if (days) return _formatCronDowList(days, timeStr, cron);
|
|
77
|
+
}
|
|
78
|
+
|
|
36
79
|
return cron;
|
|
37
80
|
}
|
|
38
81
|
|
|
@@ -245,23 +245,33 @@ function execGit(execFileSync, targetDir, args, timeout = 5000) {
|
|
|
245
245
|
})).trim();
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
function parseOriginHeadBranch(headRef) {
|
|
249
|
+
const branch = String(headRef || '').trim().replace(/^refs\/remotes\/origin\//, '');
|
|
250
|
+
return branch && branch !== 'HEAD' ? branch : '';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function discoverMainBranch(execFileSync, targetDir) {
|
|
254
|
+
try {
|
|
255
|
+
return parseOriginHeadBranch(execGit(execFileSync, targetDir, ['symbolic-ref', 'refs/remotes/origin/HEAD']));
|
|
256
|
+
} catch {}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
execGit(execFileSync, targetDir, ['remote', 'set-head', 'origin', '-a'], 10000);
|
|
260
|
+
return parseOriginHeadBranch(execGit(execFileSync, targetDir, ['symbolic-ref', 'refs/remotes/origin/HEAD']));
|
|
261
|
+
} catch {}
|
|
262
|
+
|
|
263
|
+
return '';
|
|
264
|
+
}
|
|
265
|
+
|
|
248
266
|
function discoverProjectMetadata(targetDir, options = {}) {
|
|
249
267
|
const execFileSync = options.execFileSync || defaultExecFileSync;
|
|
250
268
|
const result = { _found: [] };
|
|
251
269
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
head = execGit(execFileSync, targetDir, ['symbolic-ref', 'HEAD']);
|
|
258
|
-
}
|
|
259
|
-
const branch = head.replace('refs/remotes/origin/', '').replace('refs/heads/', '');
|
|
260
|
-
if (branch) {
|
|
261
|
-
result.mainBranch = branch;
|
|
262
|
-
result._found.push('main branch');
|
|
263
|
-
}
|
|
264
|
-
} catch {}
|
|
270
|
+
const branch = discoverMainBranch(execFileSync, targetDir);
|
|
271
|
+
if (branch) {
|
|
272
|
+
result.mainBranch = branch;
|
|
273
|
+
result._found.push('main branch');
|
|
274
|
+
}
|
|
265
275
|
|
|
266
276
|
try {
|
|
267
277
|
const remoteUrl = execGit(execFileSync, targetDir, ['remote', 'get-url', 'origin']);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1732",
|
|
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"
|