1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono@3/jsx */
import { chat } from "https://esm.town/v/stevekrouse/openai";
import cronstrue from "npm:cronstrue";
import { Hono } from "npm:hono@3";
const css = `
.htmx-indicator{
display:none;
}
.htmx-request .htmx-indicator{
display:inline;
}
.htmx-request.htmx-indicator{
display:inline;
}`;
const app = new Hono();
export default app.fetch;
app.get("/", (c) =>
c.html(
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com" />
<script
src="https://unpkg.com/htmx.org@1.9.11"
integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0"
crossorigin="anonymous"
>
</script>
<style dangerouslySetInnerHTML={{ __html: css }} />
<title>CronGPT</title>
</head>
<body class="flex p-6 mt-4 flex-col space-y-12 max-w-2xl mx-auto">
<div class="flex flex-col text-center space-y-2">
<h1 class="text-3xl font-bold">CronGPT</h1>
<p class="text-lg">Generate cron expressions with ChatGPT</p>
</div>
<form hx-post="/compile" hx-target="#cron" class="flex flex-col space-y-4" hx-disabled-elt="button">
<div class="flex flex-col">
<label for="description">Natural language description</label>
<input
name="description"
value="On weekdays at noon"
required
class="border-2 rounded-lg p-2 text-lg text-center"
/>
</div>
<div class="flex flex-col">
<label for="description">Timezone</label>
<select name="timezone" class=" border-2 rounded-lg text-lg p-2 text-center">
{Intl.supportedValuesOf("timeZone").map((tz) => (
<option value={tz} id={tz.replace("/", "-")}>{tz}</option>
))}
</select>
</div>
<script
dangerouslySetInnerHTML={{
__html: `
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
document.querySelector("#" + tz.replace("/", "-")).selected = true
`,
}}
/>
<button class="bg-sky-500 hover:bg-sky-700 text-white font-bold py-2 px-4 rounded disabled:hidden">
Compile
</button>
<button class="htmx-indicator text-white font-bold py-2 px-4 rounded bg-gray-400" disabled>
Loading...
</button>
</form>
<div hx-swap-oob="true" id="cron">
<Cron cron="0 16 * * 1-5" timezone="America/New_York" />
</div>
<div class="text-gray-500 text-center flex flex-col space-y-2">
<div>
Need a place to run cron jobs? Try <Link href="https://val.town">Val Town</Link>
</div>{" "}
<div>
<Link href="https://www.val.town/v/stevekrouse/cron">View source</Link>
{" | "} Inspired by <Link href="https://cronprompt.com/">Cron Prompt</Link>
</div>
</div>
</body>
</html>,
));
app.post("/compile", async (c) => {
const form = await c.req.formData();
const description = form.get("description") as string;
const timezone = form.get("timezone") as string;
const { content } = await chat([
{
role: "system",
content: `You are an natural language to cron syntax compiler.
Return a cron expression in UTC.
The user is in ${timezone}. The timezone offset is ${getOffset(timezone)}.
Return ONLY the cron expression.`,
},
{ role: "user", content: description },