@yail259/overnight 0.2.0 → 1.0.0

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.
package/src/notify.ts DELETED
@@ -1,50 +0,0 @@
1
- import { type JobResult, DEFAULT_NTFY_TOPIC } from "./types.js";
2
-
3
- export async function sendNtfyNotification(
4
- results: JobResult[],
5
- totalDuration: number,
6
- topic: string = DEFAULT_NTFY_TOPIC
7
- ): Promise<boolean> {
8
- const succeeded = results.filter((r) => r.status === "success").length;
9
- const failed = results.length - succeeded;
10
-
11
- // Format duration
12
- let durationStr: string;
13
- if (totalDuration >= 3600) {
14
- const hours = Math.floor(totalDuration / 3600);
15
- const mins = Math.floor((totalDuration % 3600) / 60);
16
- durationStr = `${hours}h ${mins}m`;
17
- } else if (totalDuration >= 60) {
18
- const mins = Math.floor(totalDuration / 60);
19
- const secs = Math.floor(totalDuration % 60);
20
- durationStr = `${mins}m ${secs}s`;
21
- } else {
22
- durationStr = `${totalDuration.toFixed(0)}s`;
23
- }
24
-
25
- const title =
26
- failed === 0
27
- ? `overnight: ${succeeded}/${results.length} succeeded`
28
- : `overnight: ${failed} failed`;
29
-
30
- const message = `Completed in ${durationStr}\n${succeeded} succeeded, ${failed} failed`;
31
-
32
- const priority = failed === 0 ? "default" : "high";
33
- const tags = failed === 0 ? "white_check_mark" : "warning";
34
-
35
- try {
36
- const response = await fetch(`https://ntfy.sh/${topic}`, {
37
- method: "POST",
38
- headers: {
39
- Title: title,
40
- Priority: priority,
41
- Tags: tags,
42
- },
43
- body: message,
44
- });
45
-
46
- return response.ok;
47
- } catch {
48
- return false;
49
- }
50
- }
package/src/report.ts DELETED
@@ -1,115 +0,0 @@
1
- import { writeFileSync } from "fs";
2
- import { type JobResult } from "./types.js";
3
-
4
- export function generateReport(
5
- results: JobResult[],
6
- totalDuration: number,
7
- outputPath?: string
8
- ): string {
9
- const lines: string[] = [];
10
-
11
- // Header
12
- lines.push("# Overnight Run Report");
13
- lines.push("");
14
- lines.push(`**Generated:** ${new Date().toISOString().replace("T", " ").split(".")[0]}`);
15
- lines.push("");
16
-
17
- // Summary
18
- const succeeded = results.filter((r) => r.status === "success").length;
19
- const failed = results.length - succeeded;
20
-
21
- lines.push("## Summary");
22
- lines.push("");
23
- lines.push(`- **Jobs:** ${succeeded}/${results.length} succeeded`);
24
- if (failed > 0) {
25
- lines.push(`- **Failed:** ${failed}`);
26
- }
27
-
28
- // Duration formatting
29
- let durationStr: string;
30
- if (totalDuration >= 3600) {
31
- const hours = Math.floor(totalDuration / 3600);
32
- const mins = Math.floor((totalDuration % 3600) / 60);
33
- durationStr = `${hours}h ${mins}m`;
34
- } else if (totalDuration >= 60) {
35
- const mins = Math.floor(totalDuration / 60);
36
- const secs = Math.floor(totalDuration % 60);
37
- durationStr = `${mins}m ${secs}s`;
38
- } else {
39
- durationStr = `${totalDuration.toFixed(1)}s`;
40
- }
41
-
42
- lines.push(`- **Total duration:** ${durationStr}`);
43
- lines.push("");
44
-
45
- // Job details table
46
- lines.push("## Job Results");
47
- lines.push("");
48
- lines.push("| # | Status | Duration | Task |");
49
- lines.push("|---|--------|----------|------|");
50
-
51
- const statusEmoji: Record<string, string> = {
52
- success: "✅",
53
- failed: "❌",
54
- timeout: "⏱️",
55
- stalled: "🔄",
56
- verification_failed: "⚠️",
57
- };
58
-
59
- results.forEach((r, i) => {
60
- let taskPreview = r.task.slice(0, 50).replace(/\n/g, " ").trim();
61
- if (r.task.length > 50) taskPreview += "...";
62
- const emoji = statusEmoji[r.status] ?? "❓";
63
- lines.push(
64
- `| ${i + 1} | ${emoji} ${r.status} | ${r.duration_seconds.toFixed(1)}s | ${taskPreview} |`
65
- );
66
- });
67
-
68
- lines.push("");
69
-
70
- // Failed jobs details
71
- const failures = results.filter((r) => r.status !== "success");
72
- if (failures.length > 0) {
73
- lines.push("## Failed Jobs");
74
- lines.push("");
75
-
76
- failures.forEach((r, i) => {
77
- const taskPreview = r.task.slice(0, 80).replace(/\n/g, " ").trim();
78
- lines.push(`### ${i + 1}. ${taskPreview}`);
79
- lines.push("");
80
- lines.push(`- **Status:** ${r.status}`);
81
- if (r.error) {
82
- lines.push(`- **Error:** ${r.error.slice(0, 200)}`);
83
- }
84
- if (r.retries > 0) {
85
- lines.push(`- **Retries:** ${r.retries}`);
86
- }
87
- lines.push("");
88
- });
89
- }
90
-
91
- // Next steps
92
- lines.push("## Next Steps");
93
- lines.push("");
94
- if (failed === 0) {
95
- lines.push("All jobs completed successfully! No action needed.");
96
- } else {
97
- lines.push("The following jobs need attention:");
98
- lines.push("");
99
- results.forEach((r, i) => {
100
- if (r.status !== "success") {
101
- const taskPreview = r.task.slice(0, 60).replace(/\n/g, " ").trim();
102
- lines.push(`- [ ] Job ${i + 1}: ${taskPreview} (${r.status})`);
103
- }
104
- });
105
- }
106
- lines.push("");
107
-
108
- const content = lines.join("\n");
109
-
110
- if (outputPath) {
111
- writeFileSync(outputPath, content);
112
- }
113
-
114
- return content;
115
- }