clementine-agent 1.18.110 → 1.18.111
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/dist/cli/dashboard.js +124 -21
- package/package.json +1 -1
package/dist/cli/dashboard.js
CHANGED
|
@@ -24459,9 +24459,17 @@ function operationUsageBadge(usage) {
|
|
|
24459
24459
|
return '<span class="badge badge-blue" title="' + esc(formatTokens(usage.totalInput || 0)) + ' input, ' + esc(formatTokens(usage.totalOutput || 0)) + ' output">' + esc(formatTokens(usage.totalTokens || 0)) + ' tok 7d</span>';
|
|
24460
24460
|
}
|
|
24461
24461
|
|
|
24462
|
+
// PRD §pretty-cron / 1.18.111: pretty primary, raw cron in a hover
|
|
24463
|
+
// tooltip. Power users still get the literal expression on hover; casual
|
|
24464
|
+
// users never have to read "0 8-18 * * 1-5". Falls back to raw inline
|
|
24465
|
+
// (with a help-cursor hint) when describeCron can't summarize.
|
|
24462
24466
|
function operationScheduleHtml(schedule) {
|
|
24463
|
-
var
|
|
24464
|
-
|
|
24467
|
+
var raw = schedule || '';
|
|
24468
|
+
var desc = describeCron(raw);
|
|
24469
|
+
if (desc) {
|
|
24470
|
+
return '<span title="' + esc(raw) + '" style="cursor:help">' + esc(desc) + '</span>';
|
|
24471
|
+
}
|
|
24472
|
+
return '<code title="' + esc(raw) + '" style="color:var(--accent);cursor:help">' + esc(raw) + '</code>';
|
|
24465
24473
|
}
|
|
24466
24474
|
|
|
24467
24475
|
function operationSectionHeader(title, subtitle, badgeClass, badgeText, marginTop) {
|
|
@@ -29577,44 +29585,139 @@ function updateScheduleHint() {
|
|
|
29577
29585
|
|
|
29578
29586
|
const monthNames = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
|
29579
29587
|
|
|
29588
|
+
// PRD §pretty-cron / 1.18.111 — describe a cron expression in casual
|
|
29589
|
+
// human-readable form. Output style:
|
|
29590
|
+
// - "Mondays at 8 AM" not "Every Monday at 8:00 AM"
|
|
29591
|
+
// - "5 PM" not "5:00 PM" when minutes=0
|
|
29592
|
+
// - "8 AM–6 PM" (en-dash range) for hour ranges
|
|
29593
|
+
// - "Every minute" / "Every 2 minutes" / "Every 4 hours"
|
|
29594
|
+
// - "Hourly weekdays 8 AM–6 PM" for the business-hours pattern
|
|
29595
|
+
// - Falls back to '' when the expression is too exotic to summarize;
|
|
29596
|
+
// the renderer then shows the raw cron only as a last resort.
|
|
29580
29597
|
function describeCron(expr) {
|
|
29581
|
-
|
|
29598
|
+
if (!expr || typeof expr !== 'string') return '';
|
|
29599
|
+
// @aliases first — common shortcuts.
|
|
29600
|
+
var aliases = {
|
|
29601
|
+
'@yearly': 'Once a year (Jan 1, midnight)',
|
|
29602
|
+
'@annually': 'Once a year (Jan 1, midnight)',
|
|
29603
|
+
'@monthly': 'First of every month, midnight',
|
|
29604
|
+
'@weekly': 'Sundays at midnight',
|
|
29605
|
+
'@daily': 'Every day at midnight',
|
|
29606
|
+
'@hourly': 'Every hour',
|
|
29607
|
+
'@reboot': 'On daemon start',
|
|
29608
|
+
};
|
|
29609
|
+
if (aliases[expr]) return aliases[expr];
|
|
29610
|
+
|
|
29611
|
+
var parts = expr.trim().split(/\\s+/);
|
|
29582
29612
|
if (parts.length !== 5) return '';
|
|
29583
|
-
|
|
29613
|
+
var min = parts[0], hour = parts[1], dom = parts[2], month = parts[3], dow = parts[4];
|
|
29584
29614
|
|
|
29585
|
-
//
|
|
29586
|
-
if (min
|
|
29587
|
-
|
|
29588
|
-
|
|
29615
|
+
// ── Sub-hour cadence ────────────────────────────────────────────────
|
|
29616
|
+
if (min === '*' && hour === '*' && dom === '*' && month === '*' && dow === '*') return 'Every minute';
|
|
29617
|
+
if (min.startsWith('*/')) {
|
|
29618
|
+
var n = parseInt(min.slice(2), 10);
|
|
29619
|
+
if (Number.isFinite(n)) return n === 1 ? 'Every minute' : 'Every ' + n + ' minutes';
|
|
29620
|
+
}
|
|
29621
|
+
// Every N hours (e.g. "0 */2 * * *")
|
|
29622
|
+
if (hour.startsWith('*/') && (min === '0' || min === '*')) {
|
|
29623
|
+
var nh = parseInt(hour.slice(2), 10);
|
|
29624
|
+
if (Number.isFinite(nh)) return nh === 1 ? 'Every hour' : 'Every ' + nh + ' hours';
|
|
29625
|
+
}
|
|
29589
29626
|
|
|
29590
|
-
|
|
29627
|
+
// ── Hour ranges (e.g. "0 8-18 * * 1-5" — hourly during business hours) ──
|
|
29628
|
+
var rangeMatch = /^(\d{1,2})-(\d{1,2})$/.exec(hour);
|
|
29629
|
+
if (rangeMatch && (min === '0' || min === '*')) {
|
|
29630
|
+
var startH = parseInt(rangeMatch[1], 10);
|
|
29631
|
+
var endH = parseInt(rangeMatch[2], 10);
|
|
29632
|
+
if (Number.isFinite(startH) && Number.isFinite(endH)) {
|
|
29633
|
+
var span = formatHour(startH) + '–' + formatHour(endH);
|
|
29634
|
+
if (dow === '1-5') return 'Hourly weekdays ' + span;
|
|
29635
|
+
if (dow === '*' && dom === '*' && month === '*') return 'Hourly ' + span;
|
|
29636
|
+
if (/^[0-6]$/.test(dow)) return 'Hourly ' + plural(dayNames[+dow]) + ' ' + span;
|
|
29637
|
+
}
|
|
29638
|
+
}
|
|
29639
|
+
|
|
29640
|
+
// ── Comma-list of hours at fixed minute (e.g. "0 8,12,16 * * *") ────
|
|
29641
|
+
// Check this BEFORE parseInt — parseInt is lenient and would parse
|
|
29642
|
+
// "8,12,16" as 8, missing the multi-hour case entirely.
|
|
29643
|
+
if (hour.indexOf(',') !== -1 && min !== '*' && !min.startsWith('*/')) {
|
|
29644
|
+
var minN = parseInt(min, 10);
|
|
29645
|
+
if (Number.isFinite(minN)) {
|
|
29646
|
+
return 'Daily at ' + hour.split(',').map(function(h) { return formatTimePretty(+h, minN); }).join(', ');
|
|
29647
|
+
}
|
|
29648
|
+
}
|
|
29649
|
+
|
|
29650
|
+
// ── Single-time-of-day patterns ─────────────────────────────────────
|
|
29651
|
+
var hourNum = parseInt(hour, 10);
|
|
29652
|
+
var minNum = parseInt(min, 10);
|
|
29653
|
+
if (!Number.isFinite(hourNum) || !Number.isFinite(minNum)) return '';
|
|
29654
|
+
// Reject parseInt's lenient mode: a hour like "8,12" would parse to 8.
|
|
29655
|
+
if (String(hourNum) !== hour || String(minNum) !== min) return '';
|
|
29656
|
+
var time = formatTimePretty(hourNum, minNum);
|
|
29591
29657
|
|
|
29592
29658
|
// Specific date: day + month set (e.g. "10 16 1 3 *" = Mar 1 at 4:10 PM)
|
|
29593
29659
|
if (dom !== '*' && month !== '*') {
|
|
29594
|
-
|
|
29660
|
+
var monthStr = monthNames[+month] || ('Month ' + month);
|
|
29595
29661
|
return monthStr + ' ' + dom + ' at ' + time;
|
|
29596
29662
|
}
|
|
29597
29663
|
|
|
29598
29664
|
// Day of month only (e.g. "0 9 15 * *" = 15th of every month)
|
|
29599
29665
|
if (dom !== '*' && month === '*' && dow === '*') {
|
|
29600
|
-
|
|
29601
|
-
|
|
29666
|
+
return ordinal(+dom) + ' of every month at ' + time;
|
|
29667
|
+
}
|
|
29668
|
+
|
|
29669
|
+
// Weekdays (Mon-Fri)
|
|
29670
|
+
if (dow === '1-5') return 'Weekdays at ' + time;
|
|
29671
|
+
|
|
29672
|
+
// Specific weekday → pluralize ("Mondays at 8 AM" not "Every Monday at 8 AM")
|
|
29673
|
+
if (/^[0-6]$/.test(dow)) return plural(dayNames[+dow]) + ' at ' + time;
|
|
29674
|
+
|
|
29675
|
+
// Multiple specific weekdays (e.g. "0 9 * * 1,3,5")
|
|
29676
|
+
if (/^[0-6](,[0-6])+$/.test(dow)) {
|
|
29677
|
+
return dow.split(',').map(function(d) { return shortDay(+d); }).join(', ') + ' at ' + time;
|
|
29602
29678
|
}
|
|
29603
29679
|
|
|
29604
|
-
// Weekdays
|
|
29605
|
-
if (dow === '1-5' && !hour.includes(',')) return 'Weekdays at ' + time;
|
|
29606
29680
|
// Every day
|
|
29607
|
-
if (dow === '*' && dom === '*' && month === '*'
|
|
29608
|
-
// Specific weekday
|
|
29609
|
-
if (/^[0-6]$/.test(dow) && !hour.includes(',')) return 'Every ' + dayNames[+dow] + ' at ' + time;
|
|
29610
|
-
// Multiple weekdays (e.g. "0 9 * * 1,3,5")
|
|
29611
|
-
if (/^[0-6](,[0-6])+$/.test(dow)) return dow.split(',').map(d => dayNames[+d]).join(', ') + ' at ' + time;
|
|
29612
|
-
// Multiple hours
|
|
29613
|
-
if (hour.includes(',')) return 'Daily at ' + hour.split(',').map(h => formatTime(+h, +min)).join(', ');
|
|
29681
|
+
if (dow === '*' && dom === '*' && month === '*') return 'Every day at ' + time;
|
|
29614
29682
|
|
|
29615
29683
|
return '';
|
|
29616
29684
|
}
|
|
29617
29685
|
|
|
29686
|
+
function plural(day) {
|
|
29687
|
+
// "Monday" → "Mondays". Days end in y already so just append 's'.
|
|
29688
|
+
return day + 's';
|
|
29689
|
+
}
|
|
29690
|
+
|
|
29691
|
+
function shortDay(d) {
|
|
29692
|
+
// Compact form for multi-day lists: "Mon, Wed, Fri".
|
|
29693
|
+
return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][d] || ('day ' + d);
|
|
29694
|
+
}
|
|
29695
|
+
|
|
29696
|
+
function ordinal(n) {
|
|
29697
|
+
// 1st / 2nd / 3rd / 4th… for the day-of-month phrasing.
|
|
29698
|
+
if (n >= 11 && n <= 13) return n + 'th';
|
|
29699
|
+
var lastDigit = n % 10;
|
|
29700
|
+
if (lastDigit === 1) return n + 'st';
|
|
29701
|
+
if (lastDigit === 2) return n + 'nd';
|
|
29702
|
+
if (lastDigit === 3) return n + 'rd';
|
|
29703
|
+
return n + 'th';
|
|
29704
|
+
}
|
|
29705
|
+
|
|
29706
|
+
function formatHour(h) {
|
|
29707
|
+
// Compact hour-only format used in ranges: "8 AM" / "12 PM" / "6 PM".
|
|
29708
|
+
var ampm = h >= 12 ? 'PM' : 'AM';
|
|
29709
|
+
var hr = h === 0 ? 12 : (h > 12 ? h - 12 : h);
|
|
29710
|
+
return hr + ' ' + ampm;
|
|
29711
|
+
}
|
|
29712
|
+
|
|
29713
|
+
function formatTimePretty(h, m) {
|
|
29714
|
+
// "8 AM" when minutes=0, "8:30 AM" otherwise. Casual, no leading zeros.
|
|
29715
|
+
if (m === 0) return formatHour(h);
|
|
29716
|
+
var ampm = h >= 12 ? 'PM' : 'AM';
|
|
29717
|
+
var hr = h === 0 ? 12 : (h > 12 ? h - 12 : h);
|
|
29718
|
+
return hr + ':' + String(m).padStart(2, '0') + ' ' + ampm;
|
|
29719
|
+
}
|
|
29720
|
+
|
|
29618
29721
|
function setScheduleFromCron(expr) {
|
|
29619
29722
|
// Try to reverse-map a cron expression back to the builder
|
|
29620
29723
|
const parts = expr.split(/\\s+/);
|