@atolis-hq/wake 0.2.25 → 0.2.27
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.
|
@@ -80,6 +80,10 @@ export const indexHtml = `<!DOCTYPE html>
|
|
|
80
80
|
@media (max-width: 767px) {
|
|
81
81
|
.modal-overlay { padding: 0; }
|
|
82
82
|
.modal { width: 100%; height: 100%; max-height: none; border-radius: 0; border-left: 0; border-right: 0; }
|
|
83
|
+
.columns { display: flex; flex-direction: column; overflow-x: unset; }
|
|
84
|
+
.col { min-height: unset; }
|
|
85
|
+
.col-empty { padding: 0.25rem 0.5rem; }
|
|
86
|
+
.col-empty h2 { margin: 0.1rem 0.4rem; }
|
|
83
87
|
}
|
|
84
88
|
.tiles { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1rem; }
|
|
85
89
|
.tile { background: #1a1d23; border-radius: 10px; padding: 0.6rem 0.9rem; min-width: 120px; }
|
|
@@ -164,6 +168,7 @@ const METRIC_OPTIONS = [
|
|
|
164
168
|
['duration-over-time', 'Duration over time'],
|
|
165
169
|
['work-items-over-time', 'Work items completed over time'],
|
|
166
170
|
['work-item-durations', 'Work item e2e duration'],
|
|
171
|
+
['work-item-totals', 'Work item totals'],
|
|
167
172
|
];
|
|
168
173
|
|
|
169
174
|
async function getJson(path, signal) {
|
|
@@ -248,7 +253,6 @@ async function renderBoard(context) {
|
|
|
248
253
|
const board = await getJson('/board', context.signal);
|
|
249
254
|
if (!isActiveRequest(context.requestId)) return;
|
|
250
255
|
const main = document.getElementById('main');
|
|
251
|
-
main.innerHTML = '';
|
|
252
256
|
const columns = el('div', { class: 'columns' }, CONDITIONS.map((cond) => {
|
|
253
257
|
const items = board.filter((c) => c.condition === cond);
|
|
254
258
|
const cards = items.map((item) => el('div', {
|
|
@@ -262,12 +266,12 @@ async function renderBoard(context) {
|
|
|
262
266
|
]),
|
|
263
267
|
el('div', { class: 'meta', text: item.lastRunSentinel ? 'last: ' + item.lastRunAction + ' → ' + item.lastRunSentinel : item.conditionReason }),
|
|
264
268
|
]));
|
|
265
|
-
return el('div', { class: 'col' }, [
|
|
269
|
+
return el('div', { class: 'col' + (items.length === 0 ? ' col-empty' : '') }, [
|
|
266
270
|
el('h2', { text: cond + ' (' + items.length + ')' }),
|
|
267
271
|
...cards,
|
|
268
272
|
]);
|
|
269
273
|
}));
|
|
270
|
-
main.
|
|
274
|
+
main.replaceChildren(columns);
|
|
271
275
|
}
|
|
272
276
|
|
|
273
277
|
function resourceUriToUrl(resourceUri) {
|
|
@@ -434,15 +438,13 @@ async function renderActivity(context) {
|
|
|
434
438
|
const events = await getJson('/events?limit=200', context.signal);
|
|
435
439
|
if (!isActiveRequest(context.requestId)) return;
|
|
436
440
|
const main = document.getElementById('main');
|
|
437
|
-
main.
|
|
438
|
-
const table = el('table', {}, [
|
|
441
|
+
main.replaceChildren(el('table', {}, [
|
|
439
442
|
el('tr', {}, ['time', 'direction', 'type', 'work item'].map((h) => el('th', { text: h }))),
|
|
440
443
|
...events.map((ev) => el('tr', {}, [
|
|
441
444
|
el('td', { text: ev.ingestedAt }), el('td', { text: ev.direction }),
|
|
442
445
|
el('td', { text: ev.sourceEventType }), el('td', { text: ev.workItemKey }),
|
|
443
446
|
])),
|
|
444
|
-
]);
|
|
445
|
-
main.appendChild(table);
|
|
447
|
+
]));
|
|
446
448
|
}
|
|
447
449
|
|
|
448
450
|
function fmtCost(usd) {
|
|
@@ -640,6 +642,20 @@ function renderWorkItemDurations(rows) {
|
|
|
640
642
|
]);
|
|
641
643
|
}
|
|
642
644
|
|
|
645
|
+
function renderWorkItemTotals(rows) {
|
|
646
|
+
if (rows.length === 0) return el('p', { class: 'meta', text: 'No runs for this window.' });
|
|
647
|
+
const maxTokens = Math.max(...rows.map((row) => row.totalTokens));
|
|
648
|
+
return el('table', {}, [
|
|
649
|
+
el('tr', {}, ['work item', 'runs', 'total tokens', 'total duration'].map((h) => el('th', { text: h }))),
|
|
650
|
+
...rows.map((row) => el('tr', {}, [
|
|
651
|
+
el('td', { text: row.key }),
|
|
652
|
+
el('td', { text: String(row.runCount) }),
|
|
653
|
+
metricValueCell(row.totalTokens, maxTokens, fmtNumber),
|
|
654
|
+
el('td', { text: fmtMs(row.totalDurationMs) }),
|
|
655
|
+
])),
|
|
656
|
+
]);
|
|
657
|
+
}
|
|
658
|
+
|
|
643
659
|
function renderMetricDetail(detail) {
|
|
644
660
|
if (detail.kind === 'runs-over-time') return renderRunsOverTime(detail.rows);
|
|
645
661
|
if (detail.kind === 'runs-by-status-over-time') return renderRunsByStatusOverTime(detail.rows);
|
|
@@ -650,6 +666,7 @@ function renderMetricDetail(detail) {
|
|
|
650
666
|
if (detail.kind === 'duration-over-time') return renderDurationTable(detail.rows, 'bucket');
|
|
651
667
|
if (detail.kind === 'work-items-over-time') return renderWorkItemsOverTime(detail.rows);
|
|
652
668
|
if (detail.kind === 'work-item-durations') return renderWorkItemDurations(detail.rows);
|
|
669
|
+
if (detail.kind === 'work-item-totals') return renderWorkItemTotals(detail.rows);
|
|
653
670
|
return el('p', { class: 'meta', text: 'Unsupported metric.' });
|
|
654
671
|
}
|
|
655
672
|
|
|
@@ -657,43 +674,43 @@ async function renderAnalytics(context) {
|
|
|
657
674
|
const metrics = await getJson('/metrics?window=' + encodeURIComponent(analyticsWindow) + '&metric=' + encodeURIComponent(analyticsMetric), context.signal);
|
|
658
675
|
if (!isActiveRequest(context.requestId)) return;
|
|
659
676
|
const main = document.getElementById('main');
|
|
660
|
-
main.
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
677
|
+
main.replaceChildren(
|
|
678
|
+
el('div', { class: 'toolbar' }, [
|
|
679
|
+
el('label', {}, [
|
|
680
|
+
document.createTextNode('Window'),
|
|
681
|
+
selectBox(analyticsWindow, [['1d', '1d'], ['3d', '3d'], ['5d', '5d'], ['7d', '7d']], (value) => {
|
|
682
|
+
analyticsWindow = value;
|
|
683
|
+
switchView('analytics');
|
|
684
|
+
}),
|
|
685
|
+
]),
|
|
686
|
+
el('label', {}, [
|
|
687
|
+
document.createTextNode('Metric'),
|
|
688
|
+
selectBox(analyticsMetric, METRIC_OPTIONS, (value) => {
|
|
689
|
+
analyticsMetric = value;
|
|
690
|
+
switchView('analytics');
|
|
691
|
+
}),
|
|
692
|
+
]),
|
|
668
693
|
]),
|
|
669
|
-
el('
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
694
|
+
el('div', { class: 'tiles' }, [
|
|
695
|
+
tile('Runs', fmtNumber(metrics.summary.totalRuns)),
|
|
696
|
+
tile('Completed', fmtNumber(metrics.summary.completedRuns)),
|
|
697
|
+
tile('Blocked/approval', fmtNumber(metrics.summary.blockedRuns + metrics.summary.awaitingApprovalRuns)),
|
|
698
|
+
tile('Failed', fmtNumber(metrics.summary.failedRuns)),
|
|
699
|
+
tile('Tokens', fmtNumber(metrics.summary.totalTokens)),
|
|
700
|
+
tile('Cost', fmtCost(metrics.summary.totalCostUsd)),
|
|
701
|
+
tile('Median run', fmtDuration(metrics.summary.medianRunDurationMs) || '—'),
|
|
702
|
+
tile('Work done', fmtNumber(metrics.summary.completedWorkItems)),
|
|
703
|
+
tile('Median e2e', fmtDuration(metrics.summary.medianWorkItemDurationMs) || '—'),
|
|
675
704
|
]),
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
tile('Runs', fmtNumber(metrics.summary.totalRuns)),
|
|
679
|
-
tile('Completed', fmtNumber(metrics.summary.completedRuns)),
|
|
680
|
-
tile('Blocked/approval', fmtNumber(metrics.summary.blockedRuns + metrics.summary.awaitingApprovalRuns)),
|
|
681
|
-
tile('Failed', fmtNumber(metrics.summary.failedRuns)),
|
|
682
|
-
tile('Tokens', fmtNumber(metrics.summary.totalTokens)),
|
|
683
|
-
tile('Cost', fmtCost(metrics.summary.totalCostUsd)),
|
|
684
|
-
tile('Median run', fmtDuration(metrics.summary.medianRunDurationMs) || '—'),
|
|
685
|
-
tile('Work done', fmtNumber(metrics.summary.completedWorkItems)),
|
|
686
|
-
tile('Median e2e', fmtDuration(metrics.summary.medianWorkItemDurationMs) || '—'),
|
|
687
|
-
]));
|
|
688
|
-
main.appendChild(renderMetricDetail(metrics.detail));
|
|
705
|
+
renderMetricDetail(metrics.detail),
|
|
706
|
+
);
|
|
689
707
|
}
|
|
690
708
|
|
|
691
709
|
async function renderRuns(context) {
|
|
692
710
|
const runs = await getJson('/runs', context.signal);
|
|
693
711
|
if (!isActiveRequest(context.requestId)) return;
|
|
694
712
|
const main = document.getElementById('main');
|
|
695
|
-
main.
|
|
696
|
-
const table = el('table', {}, [
|
|
713
|
+
main.replaceChildren(el('table', {}, [
|
|
697
714
|
el('tr', {}, ['repo#issue', 'action', 'status', 'sentinel', 'runner', 'tokens', 'cost', 'started', 'finished'].map((h) => el('th', { text: h }))),
|
|
698
715
|
...runs.map((r) => el('tr', {}, [
|
|
699
716
|
el('td', { text: r.repo + '#' + r.issueNumber }), el('td', { text: r.action }), el('td', { text: r.status }),
|
|
@@ -701,87 +718,87 @@ async function renderRuns(context) {
|
|
|
701
718
|
el('td', { text: fmtTokens(r.tokenUsage) }), el('td', { text: fmtCost(r.tokenUsage && r.tokenUsage.costUsd) }),
|
|
702
719
|
el('td', { text: r.startedAt }), el('td', { text: r.finishedAt || '' }),
|
|
703
720
|
])),
|
|
704
|
-
]);
|
|
705
|
-
main.appendChild(table);
|
|
721
|
+
]));
|
|
706
722
|
}
|
|
707
723
|
|
|
708
724
|
async function renderConfig(context) {
|
|
709
725
|
const data = await getJson('/config', context.signal);
|
|
710
726
|
if (!isActiveRequest(context.requestId)) return;
|
|
711
727
|
const main = document.getElementById('main');
|
|
712
|
-
main.
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
728
|
+
main.replaceChildren(
|
|
729
|
+
el('h3', { text: 'Routing table' }),
|
|
730
|
+
el('table', {}, [
|
|
731
|
+
el('tr', {}, ['stage', 'action', 'tier', 'runner', 'model', 'fallback order'].map((h) => el('th', { text: h }))),
|
|
732
|
+
...data.routingTable.map((r) => el('tr', {}, [
|
|
733
|
+
el('td', { text: r.stage }), el('td', { text: r.action || '' }), el('td', { text: r.tier || '' }),
|
|
734
|
+
el('td', { text: r.runnerName || '' }), el('td', { text: r.model || '' }),
|
|
735
|
+
el('td', {}, (r.candidates || []).map((c) => {
|
|
736
|
+
if (!c.paused) return el('span', { class: 'chip', text: c.runnerName });
|
|
737
|
+
const btn = el('button', { type: 'button', class: 'btn', text: 'Unpause', style: 'margin-top:0;font-size:0.7rem;padding:0.1rem 0.4rem;' });
|
|
738
|
+
btn.addEventListener('click', async () => {
|
|
739
|
+
btn.disabled = true;
|
|
740
|
+
btn.textContent = 'Unpausing…';
|
|
741
|
+
try {
|
|
742
|
+
await postJson('/runners/' + encodeURIComponent(c.runnerName) + '/unpause');
|
|
743
|
+
switchView('config');
|
|
744
|
+
} catch (err) {
|
|
745
|
+
btn.disabled = false;
|
|
746
|
+
btn.textContent = 'Unpause';
|
|
747
|
+
document.getElementById('status-summary').textContent = 'unpause failed: ' + err.message;
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
return el('span', { style: 'display:inline-flex;align-items:center;gap:0.25rem;margin-right:0.25rem;' }, [
|
|
751
|
+
el('span', { class: 'chip amber', text: c.runnerName + ' (paused)' }),
|
|
752
|
+
btn,
|
|
753
|
+
]);
|
|
754
|
+
})),
|
|
755
|
+
])),
|
|
756
|
+
]),
|
|
757
|
+
el('h3', { text: 'Effective config (redacted)' }),
|
|
758
|
+
el('pre', { text: JSON.stringify(data.config, null, 2) }),
|
|
759
|
+
);
|
|
743
760
|
}
|
|
744
761
|
|
|
745
762
|
async function renderHealth(context) {
|
|
746
763
|
const health = await getJson('/health', context.signal);
|
|
747
764
|
if (!isActiveRequest(context.requestId)) return;
|
|
748
765
|
const main = document.getElementById('main');
|
|
749
|
-
main.innerHTML = '';
|
|
750
766
|
const runnerNames = Object.keys(health.pause.runnerHealth || {});
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
767
|
+
const runnerSection = runnerNames.length === 0
|
|
768
|
+
? el('p', { class: 'meta', text: 'No quota failures recorded.' })
|
|
769
|
+
: el('table', {}, [
|
|
770
|
+
el('tr', {}, ['runner', 'status', 'paused until', 'failure count', 'last failure'].map((h) => el('th', { text: h }))),
|
|
771
|
+
...runnerNames.map((name) => {
|
|
772
|
+
const entry = health.pause.runnerHealth[name];
|
|
773
|
+
const paused = entry.pausedUntil && Date.parse(entry.pausedUntil) > Date.now();
|
|
774
|
+
return el('tr', {}, [
|
|
775
|
+
el('td', { text: name }),
|
|
776
|
+
el('td', {}, [el('span', { class: 'chip' + (paused ? ' amber' : ' ok') , text: paused ? 'paused' : 'available' })]),
|
|
777
|
+
el('td', { text: entry.pausedUntil || '' }),
|
|
778
|
+
el('td', { text: String(entry.failureCount || 0) }),
|
|
779
|
+
el('td', { text: entry.lastFailureAt || '' }),
|
|
780
|
+
]);
|
|
781
|
+
}),
|
|
782
|
+
]);
|
|
783
|
+
main.replaceChildren(
|
|
784
|
+
el('div', { class: 'tiles' }, [
|
|
785
|
+
tile('Tick lock', health.lock.present ? (health.lock.stale ? 'stale' : 'held') : 'free'),
|
|
786
|
+
tile('Paused', String(health.pause.paused)),
|
|
787
|
+
tile('Runners paused now', String(runnerNames.filter((name) => {
|
|
788
|
+
const until = health.pause.runnerHealth[name].pausedUntil;
|
|
789
|
+
return until && Date.parse(until) > Date.now();
|
|
790
|
+
}).length)),
|
|
791
|
+
tile('Integrity issues', String(health.integrityIssues.length)),
|
|
792
|
+
]),
|
|
793
|
+
el('h3', { text: 'Runner health (quota fallback, #67)' }),
|
|
794
|
+
runnerSection,
|
|
795
|
+
el('h3', { text: 'Storage' }),
|
|
796
|
+
el('pre', { text: JSON.stringify(health.storage, null, 2) }),
|
|
797
|
+
el('h3', { text: 'Source polling' }),
|
|
798
|
+
el('pre', { text: JSON.stringify(health.sources, null, 2) }),
|
|
799
|
+
el('h3', { text: 'Integrity issues' }),
|
|
800
|
+
el('pre', { text: JSON.stringify(health.integrityIssues, null, 2) }),
|
|
801
|
+
);
|
|
785
802
|
}
|
|
786
803
|
|
|
787
804
|
function tile(label, value) {
|
|
@@ -381,6 +381,7 @@ const metricsMetrics = new Set([
|
|
|
381
381
|
'duration-over-time',
|
|
382
382
|
'work-items-over-time',
|
|
383
383
|
'work-item-durations',
|
|
384
|
+
'work-item-totals',
|
|
384
385
|
]);
|
|
385
386
|
function parseMetricsWindow(value) {
|
|
386
387
|
return metricsWindows.has(value) ? value : '7d';
|
|
@@ -867,6 +868,27 @@ export async function buildMetrics(input) {
|
|
|
867
868
|
summary,
|
|
868
869
|
detail: { kind: 'work-item-durations', rows: workItemDurations },
|
|
869
870
|
};
|
|
871
|
+
case 'work-item-totals': {
|
|
872
|
+
const groups = new Map();
|
|
873
|
+
for (const run of runs) {
|
|
874
|
+
const key = run.workItemKey;
|
|
875
|
+
const existing = groups.get(key) ?? { runCount: 0, totalTokens: 0, totalDurationMs: 0 };
|
|
876
|
+
existing.runCount += 1;
|
|
877
|
+
existing.totalTokens += runTokenTotal(run);
|
|
878
|
+
existing.totalDurationMs += runDurationMs(run) ?? 0;
|
|
879
|
+
groups.set(key, existing);
|
|
880
|
+
}
|
|
881
|
+
const rows = [...groups.entries()]
|
|
882
|
+
.map(([key, value]) => ({ key, ...value }))
|
|
883
|
+
.sort((left, right) => right.totalTokens - left.totalTokens || left.key.localeCompare(right.key));
|
|
884
|
+
return {
|
|
885
|
+
window,
|
|
886
|
+
metric,
|
|
887
|
+
generatedAt: input.now.toISOString(),
|
|
888
|
+
summary,
|
|
889
|
+
detail: { kind: 'work-item-totals', rows },
|
|
890
|
+
};
|
|
891
|
+
}
|
|
870
892
|
case 'runs-over-time':
|
|
871
893
|
return {
|
|
872
894
|
window,
|
package/dist/src/version.js
CHANGED