next-anteater 0.2.8 → 0.2.9
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-anteater-runs.d.ts","sourceRoot":"","sources":["../../src/hooks/use-anteater-runs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,WAAW,EAEZ,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"use-anteater-runs.d.ts","sourceRoot":"","sources":["../../src/hooks/use-anteater-runs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,WAAW,EAEZ,MAAM,UAAU,CAAC;AAuElB,wBAAgB,eAAe,CAAC,WAAW,GAAE,MAAwB;;;;;sBAsIjD,eAAe;2BAgEb,MAAM;EA+B3B"}
|
|
@@ -3,6 +3,8 @@ import { useState, useCallback, useRef, useEffect } from "react";
|
|
|
3
3
|
const POLL_ACTIVE = 3000;
|
|
4
4
|
const POLL_IDLE = 30000;
|
|
5
5
|
const STORAGE_KEY = "anteater_pending_runs";
|
|
6
|
+
const DISMISSED_FAILED_RUNS_KEY = "anteater_dismissed_failed_runs";
|
|
7
|
+
const FAILED_RUN_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
|
|
6
8
|
function loadPendingRuns() {
|
|
7
9
|
if (typeof window === "undefined")
|
|
8
10
|
return [];
|
|
@@ -29,6 +31,40 @@ function savePendingRuns(runs) {
|
|
|
29
31
|
}
|
|
30
32
|
catch { }
|
|
31
33
|
}
|
|
34
|
+
function loadDismissedRuns() {
|
|
35
|
+
if (typeof window === "undefined")
|
|
36
|
+
return [];
|
|
37
|
+
try {
|
|
38
|
+
const raw = localStorage.getItem(DISMISSED_FAILED_RUNS_KEY);
|
|
39
|
+
if (!raw)
|
|
40
|
+
return [];
|
|
41
|
+
const runs = JSON.parse(raw);
|
|
42
|
+
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
43
|
+
return runs.filter((r) => r.dismissedAt > cutoff).slice(-100);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function saveDismissedRuns(runs) {
|
|
50
|
+
if (typeof window === "undefined")
|
|
51
|
+
return;
|
|
52
|
+
try {
|
|
53
|
+
if (runs.length === 0)
|
|
54
|
+
localStorage.removeItem(DISMISSED_FAILED_RUNS_KEY);
|
|
55
|
+
else
|
|
56
|
+
localStorage.setItem(DISMISSED_FAILED_RUNS_KEY, JSON.stringify(runs));
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
}
|
|
60
|
+
function isExpiredFailedRun(run) {
|
|
61
|
+
if (run.step !== "error")
|
|
62
|
+
return false;
|
|
63
|
+
const startedAtMs = new Date(run.startedAt).getTime();
|
|
64
|
+
if (Number.isNaN(startedAtMs))
|
|
65
|
+
return false;
|
|
66
|
+
return Date.now() - startedAtMs > FAILED_RUN_MAX_AGE_MS;
|
|
67
|
+
}
|
|
32
68
|
export function useAnteaterRuns(apiEndpoint = "/api/anteater") {
|
|
33
69
|
const [runs, setRuns] = useState([]);
|
|
34
70
|
const [submitting, setSubmitting] = useState(false);
|
|
@@ -37,8 +73,10 @@ export function useAnteaterRuns(apiEndpoint = "/api/anteater") {
|
|
|
37
73
|
const initialDeploymentIdRef = useRef(null);
|
|
38
74
|
const mountedRef = useRef(true);
|
|
39
75
|
const pendingRunsRef = useRef([]);
|
|
76
|
+
const dismissedRunsRef = useRef([]);
|
|
40
77
|
useEffect(() => {
|
|
41
78
|
pendingRunsRef.current = loadPendingRuns();
|
|
79
|
+
dismissedRunsRef.current = loadDismissedRuns();
|
|
42
80
|
}, []);
|
|
43
81
|
/**
|
|
44
82
|
* Merge server runs with optimistic pending runs.
|
|
@@ -46,7 +84,12 @@ export function useAnteaterRuns(apiEndpoint = "/api/anteater") {
|
|
|
46
84
|
* before the server picks up a newly dispatched workflow (~5-10s).
|
|
47
85
|
*/
|
|
48
86
|
const mergeRuns = useCallback((serverRuns) => {
|
|
49
|
-
const
|
|
87
|
+
const visibleServerRuns = serverRuns.filter((r) => {
|
|
88
|
+
if (isExpiredFailedRun(r))
|
|
89
|
+
return false;
|
|
90
|
+
return !dismissedRunsRef.current.some((d) => d.requestId === r.requestId);
|
|
91
|
+
});
|
|
92
|
+
const serverRequestIds = new Set(visibleServerRuns.map((r) => r.requestId));
|
|
50
93
|
const cutoff = Date.now() - 10 * 60 * 1000;
|
|
51
94
|
// Keep pending runs the server doesn't know about yet
|
|
52
95
|
const stillPending = pendingRunsRef.current.filter((p) => p.submittedAt > cutoff && !serverRequestIds.has(p.requestId));
|
|
@@ -62,7 +105,7 @@ export function useAnteaterRuns(apiEndpoint = "/api/anteater") {
|
|
|
62
105
|
startedAt: new Date(p.submittedAt).toISOString(),
|
|
63
106
|
}));
|
|
64
107
|
// Pending first (newest), then server runs, cap at 5
|
|
65
|
-
return [...pendingAsRuns, ...
|
|
108
|
+
return [...pendingAsRuns, ...visibleServerRuns].slice(0, 5);
|
|
66
109
|
}, []);
|
|
67
110
|
const pollRuns = useCallback(async () => {
|
|
68
111
|
try {
|
|
@@ -201,12 +244,21 @@ export function useAnteaterRuns(apiEndpoint = "/api/anteater") {
|
|
|
201
244
|
savePendingRuns(pendingRunsRef.current);
|
|
202
245
|
// Delete from GitHub Actions
|
|
203
246
|
try {
|
|
204
|
-
|
|
247
|
+
dismissedRunsRef.current = [
|
|
248
|
+
...dismissedRunsRef.current.filter((r) => r.requestId !== requestId),
|
|
249
|
+
{ requestId, dismissedAt: Date.now() },
|
|
250
|
+
];
|
|
251
|
+
saveDismissedRuns(dismissedRunsRef.current);
|
|
252
|
+
const res = await fetch(`${apiEndpoint}/runs?requestId=${encodeURIComponent(requestId)}`, {
|
|
205
253
|
method: "DELETE",
|
|
206
254
|
});
|
|
255
|
+
if (!res.ok && res.status !== 404 && res.status !== 405 && res.status !== 409) {
|
|
256
|
+
setError(`Delete failed (${res.status}). Hidden locally for now.`);
|
|
257
|
+
}
|
|
207
258
|
}
|
|
208
259
|
catch {
|
|
209
|
-
// Best-effort — the run is already gone from the UI
|
|
260
|
+
// Best-effort — the run is already gone from the UI.
|
|
261
|
+
// Keep it dismissed locally so old backends don't re-add it.
|
|
210
262
|
}
|
|
211
263
|
}, [apiEndpoint]);
|
|
212
264
|
const canSubmit = !submitting && runs.length < 5;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-anteater-runs.js","sourceRoot":"","sources":["../../src/hooks/use-anteater-runs.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAQjE,MAAM,WAAW,GAAG,IAAI,CAAC;AACzB,MAAM,SAAS,GAAG,KAAK,CAAC;AACxB,MAAM,WAAW,GAAG,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-anteater-runs.js","sourceRoot":"","sources":["../../src/hooks/use-anteater-runs.ts"],"names":[],"mappings":"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"}
|
package/lib/scaffold.mjs
CHANGED
|
@@ -15,6 +15,20 @@ async function writeIfNotExists(path, content) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
async function patchRunsRouteDeleteIfMissing(path, isTypeScript) {
|
|
19
|
+
try {
|
|
20
|
+
const existing = await readFile(path, "utf-8");
|
|
21
|
+
if (existing.includes("export async function DELETE(")) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const patched = `${existing.trimEnd()}\n\n${buildRunsDeleteHandler(isTypeScript)}\n`;
|
|
25
|
+
await writeFile(path, patched, "utf-8");
|
|
26
|
+
return true;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
/**
|
|
19
33
|
* Generate anteater.config.ts
|
|
20
34
|
*/
|
|
@@ -595,10 +609,19 @@ export function generateRunsRoute({ isTypeScript }) {
|
|
|
595
609
|
add(" }");
|
|
596
610
|
add("");
|
|
597
611
|
add(" // Sort newest first, cap at 5");
|
|
598
|
-
add("
|
|
612
|
+
add(" // Drop stale failed runs (>1h) so old errors don't clutter the bar");
|
|
613
|
+
add(" const failedCutoffMs = 60 * 60 * 1000;");
|
|
614
|
+
add(" const freshRuns = runs.filter((r) => {");
|
|
615
|
+
add(" if (r.step !== \"error\") return true;");
|
|
616
|
+
add(" const startedAtMs = new Date(r.startedAt).getTime();");
|
|
617
|
+
add(" if (Number.isNaN(startedAtMs)) return true;");
|
|
618
|
+
add(" return Date.now() - startedAtMs <= failedCutoffMs;");
|
|
619
|
+
add(" });");
|
|
620
|
+
add("");
|
|
621
|
+
add(" freshRuns.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());");
|
|
599
622
|
add("");
|
|
600
623
|
add(" return NextResponse.json" + (TS ? "<AnteaterRunsResponse>" : "") + "(");
|
|
601
|
-
add(" { runs:
|
|
624
|
+
add(" { runs: freshRuns.slice(0, 5), deploymentId: process.env.VERCEL_DEPLOYMENT_ID }");
|
|
602
625
|
add(" );");
|
|
603
626
|
add(" } catch {");
|
|
604
627
|
add(" return emptyResponse();");
|
|
@@ -606,7 +629,20 @@ export function generateRunsRoute({ isTypeScript }) {
|
|
|
606
629
|
add("}");
|
|
607
630
|
add("");
|
|
608
631
|
|
|
609
|
-
|
|
632
|
+
for (const line of buildRunsDeleteHandlerLines(TS)) add(line);
|
|
633
|
+
|
|
634
|
+
return {
|
|
635
|
+
filename: `route.${ext}`,
|
|
636
|
+
content: lines.join("\n") + "\n",
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function buildRunsDeleteHandlerLines(TS) {
|
|
641
|
+
const lines = [];
|
|
642
|
+
const add = (s) => lines.push(s);
|
|
643
|
+
|
|
644
|
+
add("");
|
|
645
|
+
add("// --- DELETE handler: delete a failed workflow run by requestId ---");
|
|
610
646
|
add("export async function DELETE(request" + (TS ? ": Request" : "") + ") {");
|
|
611
647
|
add(" const repo = getRepo();");
|
|
612
648
|
add(" const token = process.env.GITHUB_TOKEN;");
|
|
@@ -670,10 +706,11 @@ export function generateRunsRoute({ isTypeScript }) {
|
|
|
670
706
|
add(" }");
|
|
671
707
|
add("}");
|
|
672
708
|
|
|
673
|
-
return
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
709
|
+
return lines;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function buildRunsDeleteHandler(isTypeScript) {
|
|
713
|
+
return buildRunsDeleteHandlerLines(isTypeScript).join("\n");
|
|
677
714
|
}
|
|
678
715
|
|
|
679
716
|
/**
|
|
@@ -860,6 +897,8 @@ export async function scaffoldFiles(cwd, options) {
|
|
|
860
897
|
const runsPath = join(cwd, runsDir, runsRoute.filename);
|
|
861
898
|
if (await writeIfNotExists(runsPath, runsRoute.content)) {
|
|
862
899
|
results.push(join(runsDir, runsRoute.filename));
|
|
900
|
+
} else if (await patchRunsRouteDeleteIfMissing(runsPath, options.isTypeScript)) {
|
|
901
|
+
results.push(`${join(runsDir, runsRoute.filename)} (patched DELETE handler)`);
|
|
863
902
|
}
|
|
864
903
|
|
|
865
904
|
// GitHub Action workflow
|
package/package.json
CHANGED
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
const POLL_ACTIVE = 3000;
|
|
12
12
|
const POLL_IDLE = 30000;
|
|
13
13
|
const STORAGE_KEY = "anteater_pending_runs";
|
|
14
|
+
const DISMISSED_FAILED_RUNS_KEY = "anteater_dismissed_failed_runs";
|
|
15
|
+
const FAILED_RUN_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
|
|
14
16
|
|
|
15
17
|
/** Optimistic run saved to localStorage for instant UI before server catches up */
|
|
16
18
|
interface PendingRun {
|
|
@@ -21,6 +23,11 @@ interface PendingRun {
|
|
|
21
23
|
submittedAt: number;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
interface DismissedRun {
|
|
27
|
+
requestId: string;
|
|
28
|
+
dismissedAt: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
function loadPendingRuns(): PendingRun[] {
|
|
25
32
|
if (typeof window === "undefined") return [];
|
|
26
33
|
try {
|
|
@@ -42,6 +49,34 @@ function savePendingRuns(runs: PendingRun[]) {
|
|
|
42
49
|
} catch {}
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
function loadDismissedRuns(): DismissedRun[] {
|
|
53
|
+
if (typeof window === "undefined") return [];
|
|
54
|
+
try {
|
|
55
|
+
const raw = localStorage.getItem(DISMISSED_FAILED_RUNS_KEY);
|
|
56
|
+
if (!raw) return [];
|
|
57
|
+
const runs: DismissedRun[] = JSON.parse(raw);
|
|
58
|
+
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
59
|
+
return runs.filter((r) => r.dismissedAt > cutoff).slice(-100);
|
|
60
|
+
} catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function saveDismissedRuns(runs: DismissedRun[]) {
|
|
66
|
+
if (typeof window === "undefined") return;
|
|
67
|
+
try {
|
|
68
|
+
if (runs.length === 0) localStorage.removeItem(DISMISSED_FAILED_RUNS_KEY);
|
|
69
|
+
else localStorage.setItem(DISMISSED_FAILED_RUNS_KEY, JSON.stringify(runs));
|
|
70
|
+
} catch {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isExpiredFailedRun(run: AnteaterRun): boolean {
|
|
74
|
+
if (run.step !== "error") return false;
|
|
75
|
+
const startedAtMs = new Date(run.startedAt).getTime();
|
|
76
|
+
if (Number.isNaN(startedAtMs)) return false;
|
|
77
|
+
return Date.now() - startedAtMs > FAILED_RUN_MAX_AGE_MS;
|
|
78
|
+
}
|
|
79
|
+
|
|
45
80
|
export function useAnteaterRuns(apiEndpoint: string = "/api/anteater") {
|
|
46
81
|
const [runs, setRuns] = useState<AnteaterRun[]>([]);
|
|
47
82
|
const [submitting, setSubmitting] = useState(false);
|
|
@@ -50,9 +85,11 @@ export function useAnteaterRuns(apiEndpoint: string = "/api/anteater") {
|
|
|
50
85
|
const initialDeploymentIdRef = useRef<string | null>(null);
|
|
51
86
|
const mountedRef = useRef(true);
|
|
52
87
|
const pendingRunsRef = useRef<PendingRun[]>([]);
|
|
88
|
+
const dismissedRunsRef = useRef<DismissedRun[]>([]);
|
|
53
89
|
|
|
54
90
|
useEffect(() => {
|
|
55
91
|
pendingRunsRef.current = loadPendingRuns();
|
|
92
|
+
dismissedRunsRef.current = loadDismissedRuns();
|
|
56
93
|
}, []);
|
|
57
94
|
|
|
58
95
|
/**
|
|
@@ -61,7 +98,11 @@ export function useAnteaterRuns(apiEndpoint: string = "/api/anteater") {
|
|
|
61
98
|
* before the server picks up a newly dispatched workflow (~5-10s).
|
|
62
99
|
*/
|
|
63
100
|
const mergeRuns = useCallback((serverRuns: AnteaterRun[]): AnteaterRun[] => {
|
|
64
|
-
const
|
|
101
|
+
const visibleServerRuns = serverRuns.filter((r) => {
|
|
102
|
+
if (isExpiredFailedRun(r)) return false;
|
|
103
|
+
return !dismissedRunsRef.current.some((d) => d.requestId === r.requestId);
|
|
104
|
+
});
|
|
105
|
+
const serverRequestIds = new Set(visibleServerRuns.map((r) => r.requestId));
|
|
65
106
|
const cutoff = Date.now() - 10 * 60 * 1000;
|
|
66
107
|
|
|
67
108
|
// Keep pending runs the server doesn't know about yet
|
|
@@ -82,7 +123,7 @@ export function useAnteaterRuns(apiEndpoint: string = "/api/anteater") {
|
|
|
82
123
|
}));
|
|
83
124
|
|
|
84
125
|
// Pending first (newest), then server runs, cap at 5
|
|
85
|
-
return [...pendingAsRuns, ...
|
|
126
|
+
return [...pendingAsRuns, ...visibleServerRuns].slice(0, 5);
|
|
86
127
|
}, []);
|
|
87
128
|
|
|
88
129
|
const pollRuns = useCallback(async () => {
|
|
@@ -242,11 +283,21 @@ export function useAnteaterRuns(apiEndpoint: string = "/api/anteater") {
|
|
|
242
283
|
|
|
243
284
|
// Delete from GitHub Actions
|
|
244
285
|
try {
|
|
245
|
-
|
|
286
|
+
dismissedRunsRef.current = [
|
|
287
|
+
...dismissedRunsRef.current.filter((r) => r.requestId !== requestId),
|
|
288
|
+
{ requestId, dismissedAt: Date.now() },
|
|
289
|
+
];
|
|
290
|
+
saveDismissedRuns(dismissedRunsRef.current);
|
|
291
|
+
|
|
292
|
+
const res = await fetch(`${apiEndpoint}/runs?requestId=${encodeURIComponent(requestId)}`, {
|
|
246
293
|
method: "DELETE",
|
|
247
294
|
});
|
|
295
|
+
if (!res.ok && res.status !== 404 && res.status !== 405 && res.status !== 409) {
|
|
296
|
+
setError(`Delete failed (${res.status}). Hidden locally for now.`);
|
|
297
|
+
}
|
|
248
298
|
} catch {
|
|
249
|
-
// Best-effort — the run is already gone from the UI
|
|
299
|
+
// Best-effort — the run is already gone from the UI.
|
|
300
|
+
// Keep it dismissed locally so old backends don't re-add it.
|
|
250
301
|
}
|
|
251
302
|
},
|
|
252
303
|
[apiEndpoint],
|