@atolis-hq/wake 0.2.66 → 0.2.67
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.
|
@@ -47,6 +47,12 @@ export const indexHtml = `<!DOCTYPE html>
|
|
|
47
47
|
.pill-polling { background: #1f3350; color: #7fb3ff; }
|
|
48
48
|
.pill-working { background: #2d1f50; color: #c79bff; }
|
|
49
49
|
.pill-paused { background: #4a3510; color: #ffcf7f; }
|
|
50
|
+
.pill-ready { background: #1f3d2c; color: #7fe3a3; }
|
|
51
|
+
.pill-active { background: #2d1f50; color: #c79bff; }
|
|
52
|
+
.pill-scheduled { background: #1f3350; color: #7fb3ff; }
|
|
53
|
+
.pill-needs-human { background: #4a3510; color: #ffcf7f; }
|
|
54
|
+
.pill-error { background: #3d1f1f; color: #ff8f7f; }
|
|
55
|
+
.pill-finished { background: #252830; color: #9aa2ad; }
|
|
50
56
|
nav { display: flex; gap: 0.25rem; padding: 0.4rem 1rem 0 0.3rem; background: var(--brand-darker); border-bottom: 1px solid #2c313a; }
|
|
51
57
|
nav button { background: none; border: none; border-bottom: 2px solid transparent; color: rgba(255, 255, 255, 0.65); padding: 0.4rem 0.7rem 0.45rem; margin-bottom: -1px; cursor: pointer; font-size: 0.85rem; transition: color 0.12s ease; }
|
|
52
58
|
nav button:hover { color: #fff; }
|
|
@@ -61,8 +67,15 @@ export const indexHtml = `<!DOCTYPE html>
|
|
|
61
67
|
.card .meta { color: #9aa2ad; font-size: 0.72rem; }
|
|
62
68
|
.child-run { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 0.45rem; align-items: center; margin-top: 0.45rem; padding: 0.4rem; border-radius: 6px; background: #181c22; border: 1px solid #334155; color: #cbd5e1; font-size: 0.72rem; }
|
|
63
69
|
.child-run .dot { width: 0.48rem; height: 0.48rem; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.14); }
|
|
70
|
+
.child-run .dot-watch { background: #ffcf7f; box-shadow: 0 0 0 3px rgba(255, 207, 127, 0.14); }
|
|
64
71
|
.child-run .run-title { font-weight: 650; color: #e5e7eb; }
|
|
65
72
|
.child-run .run-meta { grid-column: 2; color: #94a3b8; }
|
|
73
|
+
.card-stats { color: #8892a0; font-size: 0.7rem; margin-top: 0.3rem; }
|
|
74
|
+
.card-ready { border-left: 3px solid #7fe3a3; }
|
|
75
|
+
.card-active { border-left: 3px solid #c79bff; }
|
|
76
|
+
.card-scheduled { border-left: 3px solid #7fb3ff; }
|
|
77
|
+
.card-needs-human { border-left: 3px solid #ffcf7f; }
|
|
78
|
+
.card-error { border-left: 3px solid #ff8f7f; }
|
|
66
79
|
.chip { display: inline-block; background: #2c313a; border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.68rem; margin-right: 0.2rem; }
|
|
67
80
|
.chip-label { background: transparent; border: 1px solid #3a4150; color: #9aa2ad; margin-bottom: 0.2rem; }
|
|
68
81
|
table { border-collapse: collapse; width: 100%; font-size: 0.8rem; }
|
|
@@ -290,9 +303,24 @@ async function renderBoard(context) {
|
|
|
290
303
|
const board = await getJson('/board', context.signal);
|
|
291
304
|
if (!isActiveRequest(context.requestId)) return;
|
|
292
305
|
const main = document.getElementById('main');
|
|
306
|
+
const COND_PILL = {
|
|
307
|
+
ready: 'pill-ready',
|
|
308
|
+
active: 'pill-active',
|
|
309
|
+
scheduled: 'pill-scheduled',
|
|
310
|
+
'needs-human': 'pill-needs-human',
|
|
311
|
+
error: 'pill-error',
|
|
312
|
+
finished: 'pill-finished',
|
|
313
|
+
};
|
|
314
|
+
const COND_CARD = {
|
|
315
|
+
ready: 'card-ready',
|
|
316
|
+
active: 'card-active',
|
|
317
|
+
scheduled: 'card-scheduled',
|
|
318
|
+
'needs-human': 'card-needs-human',
|
|
319
|
+
error: 'card-error',
|
|
320
|
+
};
|
|
293
321
|
const renderChildRun = (run) => el('div', { class: 'child-run' }, [
|
|
294
|
-
el('span', { class: 'dot' }),
|
|
295
|
-
el('div', { class: 'run-title', text: run.action + ' running' }),
|
|
322
|
+
el('span', { class: 'dot' + (run.isWatcher ? ' dot-watch' : '') }),
|
|
323
|
+
el('div', { class: 'run-title', text: (run.isWatcher ? '⟳ ' : '') + run.action + ' running' }),
|
|
296
324
|
el('div', {
|
|
297
325
|
class: 'run-meta',
|
|
298
326
|
text: [run.runnerName, run.tier, fmtMs(run.ageMs)].filter(Boolean).join(' · '),
|
|
@@ -301,24 +329,29 @@ async function renderBoard(context) {
|
|
|
301
329
|
const columns = el('div', { class: 'columns' }, CONDITIONS.map((cond) => {
|
|
302
330
|
const items = board.filter((c) => c.condition === cond);
|
|
303
331
|
if (cond === 'finished') items.sort((a, b) => a.timeInStageMs - b.timeInStageMs);
|
|
304
|
-
const cards = items.map((item) =>
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
: [
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
332
|
+
const cards = items.map((item) => {
|
|
333
|
+
const nonWakeLabels = (item.labels || []).filter((l) => !l.startsWith('wake:'));
|
|
334
|
+
const pillClass = 'pill ' + (COND_PILL[item.condition] || 'pill-finished');
|
|
335
|
+
const cardCondClass = COND_CARD[item.condition] ?? '';
|
|
336
|
+
const statsText = item.totalRuns + ' runs | ' + fmtCost(item.totalCostUsd) + ' | ' + fmtCompact(item.totalTokens) + ' tokens';
|
|
337
|
+
const isFinished = item.condition === 'finished';
|
|
338
|
+
return el('div', {
|
|
339
|
+
class: 'card' + (cardCondClass ? ' ' + cardCondClass : ''),
|
|
340
|
+
onclick: () => openItemModal(item.repo, item.number),
|
|
341
|
+
}, [
|
|
342
|
+
el('div', { class: 'title', text: item.repo + '#' + item.number + ' ' + item.title }),
|
|
343
|
+
el('div', { class: 'meta', style: 'display:flex;align-items:center;gap:0.3rem;flex-wrap:wrap;margin-top:0.2rem;' }, [
|
|
344
|
+
el('span', { class: pillClass, text: item.condition }),
|
|
345
|
+
document.createTextNode('| ' + item.workflow + ' | ' + item.stage + (isFinished ? '' : ' | ' + fmtMs(item.timeInStageMs) + ' in stage')),
|
|
346
|
+
]),
|
|
347
|
+
el('div', { class: 'card-stats', text: statsText }),
|
|
348
|
+
...(nonWakeLabels.length > 0
|
|
349
|
+
? [el('div', { class: 'meta', style: 'margin-top:0.2rem;' }, nonWakeLabels.map((label) => el('span', { class: 'chip chip-label', text: label })))]
|
|
350
|
+
: []),
|
|
351
|
+
...(item.activeMainRun ? [renderChildRun(item.activeMainRun)] : []),
|
|
352
|
+
...((item.activeChildRuns || []).map(renderChildRun)),
|
|
353
|
+
]);
|
|
354
|
+
});
|
|
322
355
|
return el('div', { class: 'col' + (items.length === 0 ? ' col-empty' : '') }, [
|
|
323
356
|
el('h2', { text: cond + ' (' + items.length + ')' }),
|
|
324
357
|
...cards,
|
|
@@ -529,6 +562,13 @@ function fmtTokens(tokenUsage) {
|
|
|
529
562
|
return total >= 1000 ? (total / 1000).toFixed(1) + 'k' : String(total);
|
|
530
563
|
}
|
|
531
564
|
|
|
565
|
+
function fmtCompact(value) {
|
|
566
|
+
const n = Number(value || 0);
|
|
567
|
+
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
|
568
|
+
if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
|
|
569
|
+
return String(n);
|
|
570
|
+
}
|
|
571
|
+
|
|
532
572
|
function fmtNumber(value) {
|
|
533
573
|
return Number(value || 0).toLocaleString();
|
|
534
574
|
}
|
|
@@ -2,7 +2,7 @@ import { readFile, readdir, stat } from 'node:fs/promises';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { buildResourceUri } from '../../domain/resource-uri.js';
|
|
4
4
|
import { isTerminalStage } from '../../domain/stages.js';
|
|
5
|
-
import { workflowForProjection } from '../../domain/workflows.js';
|
|
5
|
+
import { workflowForProjection, workflowNameForProjection } from '../../domain/workflows.js';
|
|
6
6
|
function parseLockMetadata(raw) {
|
|
7
7
|
try {
|
|
8
8
|
const parsed = JSON.parse(raw);
|
|
@@ -89,6 +89,7 @@ function activeChildRunsForItem(item, runs, now) {
|
|
|
89
89
|
status: run.status,
|
|
90
90
|
startedAt: run.startedAt,
|
|
91
91
|
ageMs: now.getTime() - Date.parse(run.startedAt),
|
|
92
|
+
isWatcher: run.metadata?.watcher === true,
|
|
92
93
|
...(run.routing?.runnerName === undefined ? {} : { runnerName: run.routing.runnerName }),
|
|
93
94
|
...(run.routing?.runnerKind === undefined ? {} : { runnerKind: run.routing.runnerKind }),
|
|
94
95
|
...(run.routing?.tier === undefined ? {} : { tier: run.routing.tier }),
|
|
@@ -106,6 +107,25 @@ export async function buildBoard(input) {
|
|
|
106
107
|
// with items x run-history size instead of just run-history size.
|
|
107
108
|
const runs = await input.stateStore.listRunRecordSummaries();
|
|
108
109
|
const runsById = new Map(runs.map((run) => [run.runId, run]));
|
|
110
|
+
const runTotalsByItem = new Map();
|
|
111
|
+
for (const run of runs) {
|
|
112
|
+
const existing = runTotalsByItem.get(run.workItemKey) ?? {
|
|
113
|
+
totalRuns: 0,
|
|
114
|
+
totalTokens: 0,
|
|
115
|
+
totalCostUsd: 0,
|
|
116
|
+
};
|
|
117
|
+
existing.totalRuns += 1;
|
|
118
|
+
const usage = run.tokenUsage;
|
|
119
|
+
if (usage !== undefined) {
|
|
120
|
+
existing.totalTokens +=
|
|
121
|
+
(usage.inputTokens ?? 0) +
|
|
122
|
+
(usage.outputTokens ?? 0) +
|
|
123
|
+
(usage.cacheCreationInputTokens ?? 0) +
|
|
124
|
+
(usage.cacheReadInputTokens ?? 0);
|
|
125
|
+
existing.totalCostUsd += usage.costUsd ?? 0;
|
|
126
|
+
}
|
|
127
|
+
runTotalsByItem.set(run.workItemKey, existing);
|
|
128
|
+
}
|
|
109
129
|
return items.map((item) => {
|
|
110
130
|
const lastRun = item.wake.lastRunId === undefined ? null : (runsById.get(item.wake.lastRunId) ?? null);
|
|
111
131
|
const { condition, reason } = deriveCondition(item, lastRun, input.config);
|
|
@@ -122,16 +142,25 @@ export async function buildBoard(input) {
|
|
|
122
142
|
...(lastRun.routing?.tier === undefined ? {} : { tier: lastRun.routing.tier }),
|
|
123
143
|
}
|
|
124
144
|
: undefined;
|
|
145
|
+
const runTotals = runTotalsByItem.get(item.workItemKey) ?? {
|
|
146
|
+
totalRuns: 0,
|
|
147
|
+
totalTokens: 0,
|
|
148
|
+
totalCostUsd: 0,
|
|
149
|
+
};
|
|
125
150
|
return {
|
|
126
151
|
repo: item.issue.repo,
|
|
127
152
|
number: item.issue.number,
|
|
128
153
|
title: item.issue.title,
|
|
129
154
|
url: item.issue.url,
|
|
130
155
|
stage: item.wake.stage,
|
|
156
|
+
workflow: workflowNameForProjection(item, input.config),
|
|
131
157
|
labels: item.issue.labels,
|
|
132
158
|
condition,
|
|
133
159
|
conditionReason: reason,
|
|
134
160
|
timeInStageMs: timeInStageMs(item, input.now),
|
|
161
|
+
totalRuns: runTotals.totalRuns,
|
|
162
|
+
totalTokens: runTotals.totalTokens,
|
|
163
|
+
totalCostUsd: runTotals.totalCostUsd,
|
|
135
164
|
lastRunAction: lastRun?.action,
|
|
136
165
|
lastRunSentinel: lastRun?.sentinel,
|
|
137
166
|
lastRunStatus: lastRun?.status,
|
package/dist/src/version.js
CHANGED