@atolis-hq/wake 0.2.72 → 0.2.74

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.
@@ -65,6 +65,13 @@ export const indexHtml = `<!DOCTYPE html>
65
65
  .card:hover { border-color: var(--accent); }
66
66
  .card .title { font-weight: 600; margin-bottom: 0.25rem; }
67
67
  .card .meta { color: #9aa2ad; font-size: 0.72rem; }
68
+ .item-summary { margin: 0.45rem 0 0.75rem; }
69
+ .item-summary .meta { color: #9aa2ad; font-size: 0.78rem; }
70
+ .item-summary .card-stats { font-size: 0.76rem; }
71
+ .item-summary .child-run { max-width: 560px; }
72
+ .detail-kv { display: grid; grid-template-columns: max-content 1fr; gap: 0.35rem 0.7rem; align-items: baseline; margin: 0.75rem 0 1rem; font-size: 0.8rem; }
73
+ .detail-kv dt { margin: 0; color: #9aa2ad; }
74
+ .detail-kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
68
75
  .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; }
69
76
  .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
77
  .child-run .dot-watch { background: #ffcf7f; box-shadow: 0 0 0 3px rgba(255, 207, 127, 0.14); }
@@ -171,6 +178,21 @@ export const indexHtml = `<!DOCTYPE html>
171
178
  <script>
172
179
  const API = '/api/v1';
173
180
  const CONDITIONS = ['ready', 'scheduled', 'active', 'needs-human', 'error', 'finished'];
181
+ const COND_PILL = {
182
+ ready: 'pill-ready',
183
+ active: 'pill-active',
184
+ scheduled: 'pill-scheduled',
185
+ 'needs-human': 'pill-needs-human',
186
+ error: 'pill-error',
187
+ finished: 'pill-finished',
188
+ };
189
+ const COND_CARD = {
190
+ ready: 'card-ready',
191
+ active: 'card-active',
192
+ scheduled: 'card-scheduled',
193
+ 'needs-human': 'card-needs-human',
194
+ error: 'card-error',
195
+ };
174
196
  let currentView = 'board';
175
197
  let analyticsWindow = '1d';
176
198
  let analyticsMetric = 'runs-over-time';
@@ -306,53 +328,17 @@ async function renderBoard(context) {
306
328
  const board = await getJson('/board', context.signal);
307
329
  if (!isActiveRequest(context.requestId)) return;
308
330
  const main = document.getElementById('main');
309
- const COND_PILL = {
310
- ready: 'pill-ready',
311
- active: 'pill-active',
312
- scheduled: 'pill-scheduled',
313
- 'needs-human': 'pill-needs-human',
314
- error: 'pill-error',
315
- finished: 'pill-finished',
316
- };
317
- const COND_CARD = {
318
- ready: 'card-ready',
319
- active: 'card-active',
320
- scheduled: 'card-scheduled',
321
- 'needs-human': 'card-needs-human',
322
- error: 'card-error',
323
- };
324
- const renderChildRun = (run) => el('div', { class: 'child-run' }, [
325
- el('span', { class: 'dot' + (run.isWatcher ? ' dot-watch' : '') }),
326
- el('div', { class: 'run-title', text: (run.isWatcher ? '⟳ ' : '') + run.action + ' running' }),
327
- el('div', {
328
- class: 'run-meta',
329
- text: [run.runnerName, run.tier, fmtMs(run.ageMs)].filter(Boolean).join(' · '),
330
- }),
331
- ]);
332
331
  const columns = el('div', { class: 'columns' }, CONDITIONS.map((cond) => {
333
332
  const items = board.filter((c) => c.condition === cond);
334
333
  if (cond === 'finished') items.sort((a, b) => a.timeInStageMs - b.timeInStageMs);
335
334
  const cards = items.map((item) => {
336
- const nonWakeLabels = (item.labels || []).filter((l) => !l.startsWith('wake:'));
337
- const pillClass = 'pill ' + (COND_PILL[item.condition] || 'pill-finished');
338
335
  const cardCondClass = COND_CARD[item.condition] ?? '';
339
- const statsText = item.totalRuns + ' runs | ' + fmtCost(item.totalCostUsd) + ' | ' + fmtCompact(item.totalTokens) + ' tokens';
340
- const isFinished = item.condition === 'finished';
341
336
  return el('div', {
342
337
  class: 'card' + (cardCondClass ? ' ' + cardCondClass : ''),
343
- onclick: () => openItemModal(item.repo, item.number),
338
+ onclick: () => openItemModal(item.repo, item.number, item),
344
339
  }, [
345
340
  el('div', { class: 'title', text: item.repo + '#' + item.number + ' ' + item.title }),
346
- el('div', { class: 'meta', style: 'display:flex;align-items:center;gap:0.3rem;flex-wrap:wrap;margin-top:0.2rem;' }, [
347
- el('span', { class: pillClass, text: item.condition }),
348
- document.createTextNode('| ' + item.workflow + ' | ' + item.stage + (isFinished ? '' : ' | ' + fmtMs(item.timeInStageMs) + ' in stage')),
349
- ]),
350
- el('div', { class: 'card-stats', text: statsText }),
351
- ...(nonWakeLabels.length > 0
352
- ? [el('div', { class: 'meta', style: 'margin-top:0.2rem;' }, nonWakeLabels.map((label) => el('span', { class: 'chip chip-label', text: label })))]
353
- : []),
354
- ...(item.activeMainRun ? [renderChildRun(item.activeMainRun)] : []),
355
- ...((item.activeChildRuns || []).map(renderChildRun)),
341
+ ...renderCardSummaryNodes(item),
356
342
  ]);
357
343
  });
358
344
  return el('div', { class: 'col' + (items.length === 0 ? ' col-empty' : '') }, [
@@ -363,6 +349,36 @@ async function renderBoard(context) {
363
349
  main.replaceChildren(columns);
364
350
  }
365
351
 
352
+ function renderChildRun(run) {
353
+ return el('div', { class: 'child-run' }, [
354
+ el('span', { class: 'dot' + (run.isWatcher ? ' dot-watch' : '') }),
355
+ el('div', { class: 'run-title', text: (run.isWatcher ? 'watcher ' : '') + run.action + ' running' }),
356
+ el('div', {
357
+ class: 'run-meta',
358
+ text: [run.runnerName, run.tier, fmtMs(run.ageMs)].filter(Boolean).join(' | '),
359
+ }),
360
+ ]);
361
+ }
362
+
363
+ function renderCardSummaryNodes(item) {
364
+ const nonWakeLabels = (item.labels || []).filter((l) => !l.startsWith('wake:'));
365
+ const pillClass = 'pill ' + (COND_PILL[item.condition] || 'pill-finished');
366
+ const statsText = item.totalRuns + ' runs | ' + fmtCost(item.totalCostUsd) + ' | ' + fmtCompact(item.totalTokens) + ' tokens';
367
+ const isFinished = item.condition === 'finished';
368
+ return [
369
+ el('div', { class: 'meta', style: 'display:flex;align-items:center;gap:0.3rem;flex-wrap:wrap;margin-top:0.2rem;' }, [
370
+ el('span', { class: pillClass, text: item.condition }),
371
+ document.createTextNode('| ' + item.workflow + ' | ' + item.stage + (isFinished ? '' : ' | ' + fmtMs(item.timeInStageMs) + ' in stage')),
372
+ ]),
373
+ el('div', { class: 'card-stats', text: statsText }),
374
+ ...(nonWakeLabels.length > 0
375
+ ? [el('div', { class: 'meta', style: 'margin-top:0.2rem;' }, nonWakeLabels.map((label) => el('span', { class: 'chip chip-label', text: label })))]
376
+ : []),
377
+ ...(item.activeMainRun ? [renderChildRun(item.activeMainRun)] : []),
378
+ ...((item.activeChildRuns || []).map(renderChildRun)),
379
+ ];
380
+ }
381
+
366
382
  function resourceUriToUrl(resourceUri) {
367
383
  const parts = resourceUri.split(':');
368
384
  if (parts.length < 3) return null;
@@ -378,7 +394,7 @@ function resourceUriToUrl(resourceUri) {
378
394
  return null;
379
395
  }
380
396
 
381
- async function openItemModal(repo, number) {
397
+ async function openItemModal(repo, number, boardItem) {
382
398
  const overlay = document.getElementById('modal-overlay');
383
399
  const title = document.getElementById('modal-title');
384
400
  const tabs = document.getElementById('modal-tabs');
@@ -430,22 +446,34 @@ async function openItemModal(repo, number) {
430
446
  const transcripts = await getJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/transcripts');
431
447
  return renderTranscripts(transcripts);
432
448
  }
433
- return renderItemDetails(detail);
449
+ return renderItemDetails(detail, boardItem);
434
450
  }
435
451
 
436
452
  await switchItemTab('details');
437
453
  }
438
454
 
439
- function renderItemDetails(detail) {
455
+ function renderItemDetails(detail, boardItem) {
440
456
  const body = el('div');
441
457
  const repo = detail.item.issue.repo;
442
458
  const number = detail.item.issue.number;
443
459
  const headLink = el('a', { href: detail.item.issue.url, target: '_blank', rel: 'noopener noreferrer', text: repo + '#' + number });
444
460
  body.appendChild(el('h3', {}, [headLink]));
445
461
  body.appendChild(el('p', { text: detail.item.issue.title }));
446
- body.appendChild(el('p', { class: 'meta', text: 'stage: ' + detail.item.wake.stage + (detail.item.wake.sessionId ? ' | session: ' + detail.item.wake.sessionId : '') }));
462
+ if (boardItem) {
463
+ body.appendChild(el('div', { class: 'item-summary' }, renderCardSummaryNodes(boardItem)));
464
+ }
465
+ const details = [];
466
+ if (detail.item.wake.sessionId) {
467
+ details.push(['Session', detail.item.wake.sessionId]);
468
+ }
447
469
  if (detail.item.wake.workspacePath) {
448
- body.appendChild(el('p', { class: 'meta', text: 'workspace: ' + detail.item.wake.workspacePath }));
470
+ details.push(['Workspace', detail.item.wake.workspacePath]);
471
+ }
472
+ if (details.length > 0) {
473
+ body.appendChild(el('dl', { class: 'detail-kv' }, details.flatMap(([label, value]) => [
474
+ el('dt', { text: label }),
475
+ el('dd', { text: value }),
476
+ ])));
449
477
  }
450
478
  const isFrozen = typeof detail.item.context.frozenAt === 'string';
451
479
  const actionBar = el('div', { class: 'action-bar' });
@@ -4,44 +4,20 @@ import { buildResourceUri } from '../../domain/resource-uri.js';
4
4
  import { isTerminalStage } from '../../domain/stages.js';
5
5
  import { isWorkItemDeleted, isWorkItemFrozen } from '../../domain/work-item-lifecycle.js';
6
6
  import { workflowForProjection, workflowNameForProjection } from '../../domain/workflows.js';
7
- function parseLockMetadata(raw) {
8
- try {
9
- const parsed = JSON.parse(raw);
10
- if (typeof parsed.pid !== 'number' || typeof parsed.acquiredAt !== 'string') {
11
- return null;
12
- }
13
- return { pid: parsed.pid, acquiredAt: parsed.acquiredAt };
14
- }
15
- catch {
16
- return null;
17
- }
18
- }
19
- function isPidAlive(pid) {
20
- try {
21
- process.kill(pid, 0);
22
- return true;
23
- }
24
- catch (error) {
25
- return error.code !== 'ESRCH';
26
- }
27
- }
28
- async function readLockInfo(lockFile, now) {
29
- try {
30
- const metadata = parseLockMetadata(await readFile(lockFile, 'utf8'));
31
- if (metadata === null) {
32
- return { present: true };
33
- }
34
- return {
35
- present: true,
36
- pid: metadata.pid,
37
- acquiredAt: metadata.acquiredAt,
38
- ageMs: now.getTime() - Date.parse(metadata.acquiredAt),
39
- pidAlive: isPidAlive(metadata.pid),
40
- };
41
- }
42
- catch {
43
- return { present: false };
44
- }
7
+ import { readFileLockStatus } from '../../lib/lock.js';
8
+ async function readLockInfo(lockFile, now, processInspector) {
9
+ const status = await readFileLockStatus(lockFile, {
10
+ now,
11
+ ...(processInspector === undefined ? {} : { processInspector }),
12
+ });
13
+ return {
14
+ present: status.present,
15
+ ...(status.metadata === undefined ? {} : { pid: status.metadata.pid }),
16
+ ...(status.metadata === undefined ? {} : { acquiredAt: status.metadata.acquiredAt }),
17
+ ...(status.ageMs === undefined ? {} : { ageMs: status.ageMs }),
18
+ pidAlive: status.active,
19
+ ...(status.staleReason === undefined ? {} : { staleReason: status.staleReason }),
20
+ };
45
21
  }
46
22
  /**
47
23
  * Reproduces the sentinel-driven part of policy eligibility for display purposes only.
@@ -188,8 +164,8 @@ export async function buildStatus(input) {
188
164
  buildBoard(input),
189
165
  ]);
190
166
  const [tickLock, runnerLock] = await Promise.all([
191
- readLockInfo(input.stateStore.paths.tickLockFile, input.now),
192
- readLockInfo(input.stateStore.paths.runnerLockFile, input.now),
167
+ readLockInfo(input.stateStore.paths.tickLockFile, input.now, input.processInspector),
168
+ readLockInfo(input.stateStore.paths.runnerLockFile, input.now, input.processInspector),
193
169
  ]);
194
170
  const tickLockLive = tickLock.present && tickLock.pidAlive === true;
195
171
  const runnerLockLive = runnerLock.present && runnerLock.pidAlive === true;
@@ -124,4 +124,4 @@ export function resolveWakeVersion(options = {}) {
124
124
  }
125
125
  return '0.1.0-dev';
126
126
  }
127
- export const wakeVersion = "g2328db4";
127
+ export const wakeVersion = "g0f5aad4";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atolis-hq/wake",
3
- "version": "0.2.72",
3
+ "version": "0.2.74",
4
4
  "description": "Local autonomous agent control plane for software development",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {