@atolis-hq/wake 0.1.14 → 0.1.16
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.
|
@@ -62,8 +62,10 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
62
62
|
return { owner, repo, repoRef: `${owner}/${repo}`, number: Number(numberStr) };
|
|
63
63
|
}
|
|
64
64
|
async function discoverPullRequests(ingestedAt) {
|
|
65
|
+
const seenPrData = new Map();
|
|
66
|
+
const confirmedOpenRepos = new Set();
|
|
65
67
|
if (!deps.config.sources.github.pullRequests.enabled) {
|
|
66
|
-
return [];
|
|
68
|
+
return { events: [], seenPrData, confirmedOpenRepos };
|
|
67
69
|
}
|
|
68
70
|
const events = [];
|
|
69
71
|
for (const repoRef of deps.config.sources.github.repos) {
|
|
@@ -72,9 +74,22 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
72
74
|
continue;
|
|
73
75
|
}
|
|
74
76
|
try {
|
|
75
|
-
const
|
|
77
|
+
const maxResults = deps.config.sources.github.pullRequests.maxPullRequestsPerRepo;
|
|
78
|
+
const prs = await deps.client.listPullRequests(owner, repo, maxResults);
|
|
79
|
+
// listPullRequests only ever returns open PRs (state: 'open'), so an
|
|
80
|
+
// uncapped result set here is a complete picture of what's currently
|
|
81
|
+
// open — used below to confirm a watched PR has actually closed
|
|
82
|
+
// rather than merely being truncated by maxResults.
|
|
83
|
+
if (prs.length < maxResults) {
|
|
84
|
+
confirmedOpenRepos.add(repoRef);
|
|
85
|
+
}
|
|
76
86
|
for (const pr of prs) {
|
|
77
87
|
const resourceUri = prResourceUri(repoRef, pr.number);
|
|
88
|
+
seenPrData.set(resourceUri, {
|
|
89
|
+
headSha: pr.head.sha,
|
|
90
|
+
baseRef: pr.base?.ref,
|
|
91
|
+
htmlUrl: pr.html_url,
|
|
92
|
+
});
|
|
78
93
|
const known = await deps.resourceIndex.resolve(resourceUri);
|
|
79
94
|
if (known !== undefined) {
|
|
80
95
|
continue;
|
|
@@ -110,7 +125,7 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
110
125
|
console.error(`[github-pr-activity-source] discovery failed for ${repoRef}, skipping this tick: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
126
|
}
|
|
112
127
|
}
|
|
113
|
-
return events;
|
|
128
|
+
return { events, seenPrData, confirmedOpenRepos };
|
|
114
129
|
}
|
|
115
130
|
async function pollWatchedPr(resourceUri, ingestedAt) {
|
|
116
131
|
if (!deps.config.sources.github.pullRequests.enabled) {
|
|
@@ -236,7 +251,7 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
236
251
|
}
|
|
237
252
|
return events;
|
|
238
253
|
}
|
|
239
|
-
async function pollRequiredCheckFailures(ref, resourceUri, ingestedAt) {
|
|
254
|
+
async function pollRequiredCheckFailures(ref, resourceUri, ingestedAt, cachedPr) {
|
|
240
255
|
if (!deps.config.sources.github.pullRequests.enabled ||
|
|
241
256
|
!deps.config.sources.github.pullRequests.checks.enabled ||
|
|
242
257
|
deps.client.getRequiredStatusChecks === undefined ||
|
|
@@ -244,9 +259,25 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
244
259
|
deps.client.getCombinedStatusForRef === undefined) {
|
|
245
260
|
return [];
|
|
246
261
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
262
|
+
// headSha/baseRef/htmlUrl are already returned by this tick's PR
|
|
263
|
+
// discovery pass (listPullRequests) — reuse that instead of spending a
|
|
264
|
+
// dedicated getPullRequest call per watched PR. Falls back only when the
|
|
265
|
+
// watched PR wasn't present in discovery (e.g. truncated by
|
|
266
|
+
// maxPullRequestsPerRepo, or pullRequests polling failed this tick).
|
|
267
|
+
let headSha;
|
|
268
|
+
let baseRef;
|
|
269
|
+
let htmlUrl;
|
|
270
|
+
if (cachedPr?.headSha !== undefined && cachedPr.baseRef !== undefined) {
|
|
271
|
+
headSha = cachedPr.headSha;
|
|
272
|
+
baseRef = cachedPr.baseRef;
|
|
273
|
+
htmlUrl = cachedPr.htmlUrl;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const pr = await deps.client.getPullRequest(ref.owner, ref.repo, ref.number);
|
|
277
|
+
headSha = pr.head.sha;
|
|
278
|
+
baseRef = pr.base?.ref;
|
|
279
|
+
htmlUrl = pr.html_url;
|
|
280
|
+
}
|
|
250
281
|
if (headSha === undefined || baseRef === undefined) {
|
|
251
282
|
return [];
|
|
252
283
|
}
|
|
@@ -265,7 +296,7 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
265
296
|
continue;
|
|
266
297
|
}
|
|
267
298
|
const occurredAt = run.completed_at ?? run.started_at ?? ingestedAt;
|
|
268
|
-
const sourceUrl = run.html_url ?? run.details_url ??
|
|
299
|
+
const sourceUrl = run.html_url ?? run.details_url ?? htmlUrl;
|
|
269
300
|
events.push(createUnkeyedEventEnvelope({
|
|
270
301
|
eventId: `pr-check-failed-${safeEventToken(ref.repoRef)}-${ref.number}-${safeEventToken(run.name)}-${headSha}-${safeEventToken(occurredAt)}-${run.conclusion}`,
|
|
271
302
|
streamScope: 'work-item',
|
|
@@ -306,7 +337,7 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
306
337
|
sourceEventType: 'pr.checks.failed',
|
|
307
338
|
sourceRefs: {
|
|
308
339
|
repo: ref.repoRef,
|
|
309
|
-
sourceUrl: status.target_url ??
|
|
340
|
+
sourceUrl: status.target_url ?? htmlUrl,
|
|
310
341
|
resourceUri,
|
|
311
342
|
},
|
|
312
343
|
occurredAt: status.updated_at,
|
|
@@ -336,15 +367,28 @@ export function createGitHubPullRequestActivitySource(deps) {
|
|
|
336
367
|
async pollEvents(input) {
|
|
337
368
|
const ingestedAt = deps.now().toISOString();
|
|
338
369
|
const watched = (input?.watch ?? []).filter((ref) => ref.resourceUri.startsWith('github:pr:'));
|
|
339
|
-
const discovered = await discoverPullRequests(ingestedAt);
|
|
340
|
-
|
|
341
|
-
|
|
370
|
+
const { events: discovered, seenPrData, confirmedOpenRepos, } = await discoverPullRequests(ingestedAt);
|
|
371
|
+
// A watched PR absent from this tick's complete open-PR listing has
|
|
372
|
+
// closed or merged — stop spending its 7 activity/checks calls every
|
|
373
|
+
// tick. `confirmedOpenRepos` only covers repos where discovery
|
|
374
|
+
// succeeded and wasn't truncated, so an untruncated miss or a failed
|
|
375
|
+
// repo poll leaves the PR in the active set rather than risking a
|
|
376
|
+
// false drop.
|
|
377
|
+
const activeWatched = watched.filter((ref) => {
|
|
378
|
+
const parsed = repoAndNumberFromPrUri(ref.resourceUri);
|
|
379
|
+
if (parsed === null) {
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
return !confirmedOpenRepos.has(parsed.repoRef) || seenPrData.has(ref.resourceUri);
|
|
383
|
+
});
|
|
384
|
+
const activityBatches = await Promise.all(activeWatched.map((ref) => pollWatchedPr(ref.resourceUri, ingestedAt)));
|
|
385
|
+
const checkBatches = await Promise.all(activeWatched.map(async (watchRef) => {
|
|
342
386
|
const ref = repoAndNumberFromPrUri(watchRef.resourceUri);
|
|
343
387
|
if (ref === null) {
|
|
344
388
|
return [];
|
|
345
389
|
}
|
|
346
390
|
try {
|
|
347
|
-
return await pollRequiredCheckFailures(ref, watchRef.resourceUri, ingestedAt);
|
|
391
|
+
return await pollRequiredCheckFailures(ref, watchRef.resourceUri, ingestedAt, seenPrData.get(watchRef.resourceUri));
|
|
348
392
|
}
|
|
349
393
|
catch (error) {
|
|
350
394
|
console.error(`[github-pr-activity-source] check poll failed for ${watchRef.resourceUri}, skipping this tick: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1,29 +1,54 @@
|
|
|
1
1
|
import { wakeVersion } from '../../version.js';
|
|
2
|
+
const logoSvg = `<svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g transform="translate(55 55)">
|
|
4
|
+
<circle r="54" fill="none" stroke="#0F766E" opacity=".10"></circle>
|
|
5
|
+
<path d="M-42.5-11.5 A44 44 0 1 0 42.5-11.5" fill="none" stroke="#2DD4BF" stroke-width="7.5" stroke-linecap="round"></path>
|
|
6
|
+
<path d="M-36-25.2 A44 44 0 0 1-25.2-36" fill="none" stroke="#5EEAD4" stroke-width="7.5" stroke-linecap="round"></path>
|
|
7
|
+
<path d="M25.2-36 A44 44 0 0 1 36-25.2" fill="none" stroke="#5EEAD4" stroke-width="7.5" stroke-linecap="round"></path>
|
|
8
|
+
<circle r="25" fill="#2DD4BF" opacity=".08"></circle>
|
|
9
|
+
<circle r="11" fill="#2DD4BF" opacity=".25"></circle>
|
|
10
|
+
<path d="M-18 23 C-9 22 -8 2 0-2 C8 2 9 22 18 23" fill="none" stroke="#2DD4BF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
11
|
+
</g>
|
|
12
|
+
</svg>`;
|
|
13
|
+
const faviconHref = `data:image/svg+xml,${encodeURIComponent(logoSvg)}`;
|
|
2
14
|
export const indexHtml = `<!DOCTYPE html>
|
|
3
15
|
<html lang="en">
|
|
4
16
|
<head>
|
|
5
17
|
<meta charset="utf-8" />
|
|
6
18
|
<title>Wake control plane</title>
|
|
19
|
+
<link rel="icon" type="image/svg+xml" href="${faviconHref}" />
|
|
7
20
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
21
|
<style>
|
|
9
|
-
:root {
|
|
22
|
+
:root {
|
|
23
|
+
color-scheme: dark light;
|
|
24
|
+
--brand: #0f766e;
|
|
25
|
+
--brand-dark: #134e4a;
|
|
26
|
+
--brand-darker: #103a37;
|
|
27
|
+
--accent: #2dd4bf;
|
|
28
|
+
--accent-light: #5eead4;
|
|
29
|
+
}
|
|
10
30
|
body { font-family: -apple-system, Segoe UI, Roboto, sans-serif; margin: 0; background: #14161a; color: #e8e8e8; }
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.
|
|
31
|
+
.topbar { display: flex; align-items: center; gap: 0.55rem; padding: 0.55rem 1rem; background: var(--brand); }
|
|
32
|
+
.topbar .logo { display: flex; width: 24px; height: 24px; flex-shrink: 0; }
|
|
33
|
+
.topbar .logo svg { width: 100%; height: 100%; display: block; }
|
|
34
|
+
.topbar .brand-name { font-size: 1.05rem; font-weight: 700; color: #fff; letter-spacing: 0.01em; }
|
|
35
|
+
.topbar .version { color: rgba(255, 255, 255, 0.7); font-size: 0.78rem; }
|
|
36
|
+
.statusbar { display: flex; align-items: center; gap: 1rem; padding: 0.45rem 1rem; background: var(--brand-dark); border-top: 1px solid rgba(0, 0, 0, 0.18); flex-wrap: wrap; font-size: 0.8rem; }
|
|
37
|
+
.statusbar .meta { color: rgba(255, 255, 255, 0.72); }
|
|
14
38
|
.pill { padding: 0.15rem 0.5rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; }
|
|
15
39
|
.pill-idle { background: #1f3d2c; color: #7fe3a3; }
|
|
16
40
|
.pill-ticking { background: #1f3350; color: #7fb3ff; }
|
|
17
41
|
.pill-paused { background: #4a3510; color: #ffcf7f; }
|
|
18
|
-
nav { display: flex; gap: 0.25rem; padding: 0.
|
|
19
|
-
nav button { background: none; border: none; color:
|
|
20
|
-
nav button
|
|
42
|
+
nav { display: flex; gap: 0.25rem; padding: 0.4rem 1rem 0 0.3rem; background: var(--brand-darker); border-bottom: 1px solid #2c313a; }
|
|
43
|
+
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; }
|
|
44
|
+
nav button:hover { color: #fff; }
|
|
45
|
+
nav button.active { color: var(--accent-light); border-bottom-color: var(--accent); }
|
|
21
46
|
main { padding: 1rem; }
|
|
22
47
|
.columns { display: grid; grid-template-columns: repeat(6, minmax(180px, 1fr)); gap: 0.6rem; overflow-x: auto; }
|
|
23
|
-
.col { background: #1a1d23; border-radius:
|
|
48
|
+
.col { background: #1a1d23; border-radius: 10px; padding: 0.5rem; min-height: 200px; }
|
|
24
49
|
.col h2 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: #9aa2ad; margin: 0.2rem 0.4rem 0.5rem; }
|
|
25
|
-
.card { background: #22262e; border: 1px solid #2c313a; border-radius:
|
|
26
|
-
.card:hover { border-color:
|
|
50
|
+
.card { background: #22262e; border: 1px solid #2c313a; border-radius: 8px; padding: 0.5rem; margin-bottom: 0.5rem; cursor: pointer; font-size: 0.8rem; transition: border-color 0.12s ease; }
|
|
51
|
+
.card:hover { border-color: var(--accent); }
|
|
27
52
|
.card .title { font-weight: 600; margin-bottom: 0.25rem; }
|
|
28
53
|
.card .meta { color: #9aa2ad; font-size: 0.72rem; }
|
|
29
54
|
.chip { display: inline-block; background: #2c313a; border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.68rem; margin-right: 0.2rem; }
|
|
@@ -34,23 +59,28 @@ export const indexHtml = `<!DOCTYPE html>
|
|
|
34
59
|
.drawer { position: fixed; top: 0; right: 0; width: min(560px, 100%); height: 100%; background: #191c22; border-left: 1px solid #2c313a; overflow-y: auto; padding: 1rem; transform: translateX(100%); transition: transform 0.15s ease; }
|
|
35
60
|
.drawer.open { transform: translateX(0); }
|
|
36
61
|
.drawer .close { float: right; cursor: pointer; color: #9aa2ad; }
|
|
62
|
+
.drawer .close:hover { color: var(--accent-light); }
|
|
37
63
|
.tiles { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1rem; }
|
|
38
|
-
.tile { background: #1a1d23; border-radius:
|
|
64
|
+
.tile { background: #1a1d23; border-radius: 10px; padding: 0.6rem 0.9rem; min-width: 120px; }
|
|
39
65
|
.tile .n { font-size: 1.3rem; font-weight: 700; }
|
|
40
66
|
.tile .l { color: #9aa2ad; font-size: 0.72rem; text-transform: uppercase; }
|
|
41
67
|
.amber { color: #ffcf7f; }
|
|
42
68
|
.red { color: #ff8f7f; }
|
|
43
69
|
.ok { color: #7fe3a3; }
|
|
44
|
-
input[type=text] { background: #1a1d23; border: 1px solid #2c313a; color: #e8e8e8; padding: 0.3rem 0.5rem; border-radius: 6px; margin-bottom: 0.6rem; width: 260px; }
|
|
70
|
+
input[type=text] { background: #1a1d23; border: 1px solid #2c313a; color: #e8e8e8; padding: 0.3rem 0.5rem; border-radius: 6px; margin-bottom: 0.6rem; width: 260px; transition: border-color 0.12s ease, box-shadow 0.12s ease; }
|
|
71
|
+
input[type=text]:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.15); }
|
|
45
72
|
</style>
|
|
46
73
|
</head>
|
|
47
74
|
<body>
|
|
48
|
-
<header>
|
|
49
|
-
<
|
|
75
|
+
<header class="topbar">
|
|
76
|
+
<span class="logo">${logoSvg}</span>
|
|
77
|
+
<span class="brand-name">Wake</span>
|
|
50
78
|
<span class="version">${wakeVersion}</span>
|
|
79
|
+
</header>
|
|
80
|
+
<div class="statusbar">
|
|
51
81
|
<span id="loop-pill" class="pill">…</span>
|
|
52
82
|
<span id="status-summary" class="meta"></span>
|
|
53
|
-
</
|
|
83
|
+
</div>
|
|
54
84
|
<nav>
|
|
55
85
|
<button data-view="board" class="active">Board</button>
|
|
56
86
|
<button data-view="activity">Activity</button>
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const wakeVersion = "0.1.
|
|
1
|
+
export const wakeVersion = "0.1.16+g97c5859";
|