@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/README.md +71 -225
- package/bin/overnight.js +2 -0
- package/dist/cli.js +103923 -23493
- package/package.json +27 -6
- package/bun.lock +0 -63
- package/src/cli.ts +0 -538
- package/src/notify.ts +0 -50
- package/src/report.ts +0 -115
- package/src/runner.ts +0 -660
- package/src/security.ts +0 -162
- package/src/types.ts +0 -81
- package/tsconfig.json +0 -15
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
|
-
}
|