@wonsukchoi/crondex 0.12.0 → 0.14.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 +60 -52
- package/bin/crondex.js +102 -3
- package/catalog.json +352 -2
- package/jobs/automotive/estimate-approval-followup-check.yaml +36 -0
- package/jobs/automotive/vehicle-pickup-reminder.yaml +35 -0
- package/jobs/childcare/enrollment-waitlist-followup-check.yaml +35 -0
- package/jobs/childcare/staff-certification-expiry-check.yaml +35 -0
- package/jobs/devops/error-rate-spike-watch.yaml +2 -2
- package/jobs/fitness/facility-cleaning-log-check.yaml +36 -0
- package/jobs/fitness/trainer-session-package-expiry-check.yaml +35 -0
- package/jobs/government/budget-appropriation-deadline-check.yaml +35 -0
- package/jobs/government/public-meeting-notice-compliance-check.yaml +39 -0
- package/jobs/retail/gift-card-liability-reconciliation-check.yaml +36 -0
- package/jobs/retail/return-fraud-pattern-check.yaml +39 -0
- package/jobs/veterinary/diagnostic-equipment-calibration-check.yaml +35 -0
- package/jobs/veterinary/surgery-prep-check.yaml +30 -0
- package/jobs/warehousing/cycle-count-schedule-check.yaml +35 -0
- package/jobs/warehousing/forklift-inspection-check.yaml +36 -0
- package/lib/catalog-summary.js +47 -0
- package/lib/category-descriptions.js +50 -0
- package/lib/deploy.js +84 -0
- package/lib/duplicates-allowlist.js +18 -0
- package/lib/duplicates.js +29 -0
- package/lib/shellcheck-prep.js +20 -0
- package/lib/smoke-test.js +32 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ access.
|
|
|
25
25
|
```bash
|
|
26
26
|
npx @wonsukchoi/crondex recommend "warn me before my SSL cert expires"
|
|
27
27
|
npx @wonsukchoi/crondex show ssl-cert-expiry-check
|
|
28
|
-
npx @wonsukchoi/crondex
|
|
28
|
+
npx @wonsukchoi/crondex deploy ssl-cert-expiry-check --var host=example.com
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
- `recommend "<what you want>"` — find the closest matching job (zero
|
|
@@ -35,6 +35,14 @@ npx @wonsukchoi/crondex add ssl-cert-expiry-check --dest ./cron/ssl-cert-expiry-
|
|
|
35
35
|
- `show <id>` — print a job's full YAML
|
|
36
36
|
- `add <id> [--dest path]` — copy it into your project to edit
|
|
37
37
|
- `init <id> [--category x]` — scaffold a brand-new job from the template
|
|
38
|
+
- `deploy <id> [--target crontab|github-actions] [--var name=value ...]`
|
|
39
|
+
— turn a job into something that actually runs: prints a ready crontab
|
|
40
|
+
line (or installs it into your own crontab with `--install`), or writes
|
|
41
|
+
a scheduled GitHub Actions workflow file. `--var` overrides a variable's
|
|
42
|
+
default (repeatable). `hybrid` jobs deploy their `command` by default;
|
|
43
|
+
pass `--mode prompt` to deploy the `prompt` side instead — for a
|
|
44
|
+
crontab target that means wiring in your own agent CLI via a
|
|
45
|
+
`CRONDEX_AGENT_CLI` env var, since crondex can't guess its syntax.
|
|
38
46
|
|
|
39
47
|
Add `--json` to `list`/`categories`/`show`/`recommend` for machine-readable
|
|
40
48
|
output — useful when an agent is parsing the result programmatically
|
|
@@ -91,54 +99,54 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
|
|
|
91
99
|
`jobs/<category>/` directly.
|
|
92
100
|
|
|
93
101
|
<!-- BEGIN JOB SUMMARY -->
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
| category | jobs |
|
|
97
|
-
|
|
98
|
-
| `agriculture` | 5 |
|
|
99
|
-
| `automotive` |
|
|
100
|
-
| `childcare` |
|
|
101
|
-
| `construction` | 5 |
|
|
102
|
-
| `content` | 13 |
|
|
103
|
-
| `creator` | 5 |
|
|
104
|
-
| `crypto` | 5 |
|
|
105
|
-
| `devops` | 26 |
|
|
106
|
-
| `ecommerce` | 5 |
|
|
107
|
-
| `education` | 5 |
|
|
108
|
-
| `events` | 5 |
|
|
109
|
-
| `finance` | 10 |
|
|
110
|
-
| `fitness` |
|
|
111
|
-
| `fleet` | 5 |
|
|
112
|
-
| `gaming` | 5 |
|
|
113
|
-
| `government` |
|
|
114
|
-
| `growth` | 5 |
|
|
115
|
-
| `healthcare` | 5 |
|
|
116
|
-
| `hiring` | 5 |
|
|
117
|
-
| `home` | 5 |
|
|
118
|
-
| `hospitality` | 5 |
|
|
119
|
-
| `hr` | 5 |
|
|
120
|
-
| `insurance` | 5 |
|
|
121
|
-
| `inventory` | 5 |
|
|
122
|
-
| `investing` | 5 |
|
|
123
|
-
| `learning` | 5 |
|
|
124
|
-
| `legal` | 5 |
|
|
125
|
-
| `logistics` | 5 |
|
|
126
|
-
| `manufacturing` | 5 |
|
|
127
|
-
| `marketing` | 5 |
|
|
128
|
-
| `nonprofit` | 5 |
|
|
129
|
-
| `personal` | 9 |
|
|
130
|
-
| `podcast` | 5 |
|
|
131
|
-
| `productivity` | 9 |
|
|
132
|
-
| `realestate` | 5 |
|
|
133
|
-
| `restaurant` | 5 |
|
|
134
|
-
| `retail` |
|
|
135
|
-
| `sales` | 5 |
|
|
136
|
-
| `security` | 14 |
|
|
137
|
-
| `support` | 5 |
|
|
138
|
-
| `team` | 5 |
|
|
139
|
-
| `travel` | 5 |
|
|
140
|
-
| `veterinary` |
|
|
141
|
-
| `warehousing` |
|
|
102
|
+
271 jobs across 44 categories:
|
|
103
|
+
|
|
104
|
+
| category | jobs | description |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `agriculture` | 5 | Farm operations — weather risk, irrigation, equipment, market prices. |
|
|
107
|
+
| `automotive` | 5 | Repair shop workflow — repair orders, parts, loaners. |
|
|
108
|
+
| `childcare` | 5 | Daycare compliance and ops — ratios, immunizations, tuition. |
|
|
109
|
+
| `construction` | 5 | Job site compliance — permits, inspections, change orders, rentals. |
|
|
110
|
+
| `content` | 13 | Site/content health — SEO, broken links, freshness, repurposing. |
|
|
111
|
+
| `creator` | 5 | Influencer/creator ops — content calendar, cross-posting, sponsorships. |
|
|
112
|
+
| `crypto` | 5 | Wallets, gas prices, DeFi risk, and token unlock schedules. |
|
|
113
|
+
| `devops` | 26 | Infra health — backups, deploys, dependencies, monitoring. |
|
|
114
|
+
| `ecommerce` | 5 | Storefront ops — carts, stock, returns, reviews. |
|
|
115
|
+
| `education` | 5 | Classroom ops — grading, attendance, deadlines, permission slips. |
|
|
116
|
+
| `events` | 5 | Event planning — budget, RSVPs, staffing, vendors, day-of check-in. |
|
|
117
|
+
| `finance` | 10 | Personal/business finance — budgets, invoices, taxes, subscriptions. |
|
|
118
|
+
| `fitness` | 5 | Gym/studio ops — memberships, class utilization, equipment. |
|
|
119
|
+
| `fleet` | 5 | Vehicle fleet compliance — maintenance, registration, licenses, fuel. |
|
|
120
|
+
| `gaming` | 5 | Streaming and community server ops — schedules, patches, tournaments. |
|
|
121
|
+
| `government` | 5 | Public-sector ops — records requests, permits, constituent casework. |
|
|
122
|
+
| `growth` | 5 | Lifecycle marketing — churn, trials, onboarding, reviews. |
|
|
123
|
+
| `healthcare` | 5 | Clinic ops — appointments, recalls, licenses, lab results. |
|
|
124
|
+
| `hiring` | 5 | Recruiting pipeline — candidates, offers, interviews, reqs. |
|
|
125
|
+
| `home` | 5 | Household reminders — maintenance, warranties, plants, safety. |
|
|
126
|
+
| `hospitality` | 5 | Hotel ops — guest reviews, inspections, maintenance requests. |
|
|
127
|
+
| `hr` | 5 | People ops — payroll, onboarding, benefits, reviews, offboarding. |
|
|
128
|
+
| `insurance` | 5 | Policy tracking — renewals, claims, coverage, certificates. |
|
|
129
|
+
| `inventory` | 5 | Stock accuracy — counts, shrinkage, expiry, overstock. |
|
|
130
|
+
| `investing` | 5 | Portfolio tracking — prices, dividends, rebalancing, taxes. |
|
|
131
|
+
| `learning` | 5 | Personal learning — certs, courses, flashcards, reading. |
|
|
132
|
+
| `legal` | 5 | Contracts and deadlines — NDAs, trademarks, court, compliance filings. |
|
|
133
|
+
| `logistics` | 5 | Shipping ops — customs, freight, delays, fees. |
|
|
134
|
+
| `manufacturing` | 5 | Production ops — downtime, defects, maintenance, suppliers, materials. |
|
|
135
|
+
| `marketing` | 5 | Campaign ops — ad spend, SEO rank, deliverability, competitors. |
|
|
136
|
+
| `nonprofit` | 5 | Fundraising ops — grants, donors, volunteers, board follow-ups. |
|
|
137
|
+
| `personal` | 9 | Daily life reminders — bills, habits, meals, screen time. |
|
|
138
|
+
| `podcast` | 5 | Show ops — publish cadence, guests, sponsors, ratings. |
|
|
139
|
+
| `productivity` | 9 | Work habits — inbox, standups, focus, meetings, reports. |
|
|
140
|
+
| `realestate` | 5 | Property management — leases, rent, vacancy, inspections, tax. |
|
|
141
|
+
| `restaurant` | 5 | Kitchen/FOH ops — food cost, labor cost, waste, inspections. |
|
|
142
|
+
| `retail` | 5 | Store ops — till reconciliation, opening checklist, scheduling. |
|
|
143
|
+
| `sales` | 5 | Pipeline ops — leads, deals, quota, CRM sync. |
|
|
144
|
+
| `security` | 14 | Security posture — keys, certs, access, scans, firewalls. |
|
|
145
|
+
| `support` | 5 | Helpdesk ops — SLA, backlog, CSAT, agent workload. |
|
|
146
|
+
| `team` | 5 | Team ops — 1:1s, on-call, PTO, anniversaries. |
|
|
147
|
+
| `travel` | 5 | Trip logistics — flights, passports, visas, insurance, miles. |
|
|
148
|
+
| `veterinary` | 5 | Clinic ops for animals — vaccines, controlled substances, boarding. |
|
|
149
|
+
| `warehousing` | 5 | Warehouse ops — dock scheduling, pick/pack errors, climate control. |
|
|
142
150
|
<!-- END JOB SUMMARY -->
|
|
143
151
|
|
|
144
152
|
---
|
|
@@ -148,12 +156,12 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
|
|
|
148
156
|
```
|
|
149
157
|
crondex/
|
|
150
158
|
├── llms.txt agent-discovery manifest (llms.txt convention)
|
|
151
|
-
├── bin/crondex.js CLI: list / categories / show / add / recommend / init
|
|
152
|
-
├── lib/recommend
|
|
159
|
+
├── bin/crondex.js CLI: list / categories / show / add / recommend / init / deploy
|
|
160
|
+
├── lib/ recommend, deploy, and catalog-building logic (unit tested in test/)
|
|
153
161
|
├── catalog.json generated index of every job — read this first
|
|
154
162
|
├── schema/job.schema.json spec every job file follows
|
|
155
163
|
├── jobs/ one YAML per job, grouped by category subdirectory
|
|
156
|
-
└── scripts/ build-catalog.js, validate-jobs.js, lint-shell.js
|
|
164
|
+
└── scripts/ build-catalog.js, validate-jobs.js, lint-shell.js, check-duplicates.js, smoke-test.js
|
|
157
165
|
```
|
|
158
166
|
|
|
159
167
|
---
|
package/bin/crondex.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// CLI over catalog.json — browse jobs, read one, or pull one into your project.
|
|
3
|
-
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
4
|
+
import { join, dirname } from "node:path";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
5
6
|
import yaml from "js-yaml";
|
|
6
7
|
import { tokenize, rankJobs } from "../lib/recommend.js";
|
|
8
|
+
import { CATEGORY_DESCRIPTIONS } from "../lib/category-descriptions.js";
|
|
9
|
+
import { resolveVariables, substitutePlaceholders, pickMode, buildCrontabLine, buildGithubActionsWorkflow } from "../lib/deploy.js";
|
|
7
10
|
|
|
8
11
|
const ROOT = new URL("..", import.meta.url).pathname;
|
|
9
12
|
const CATALOG = JSON.parse(readFileSync(join(ROOT, "catalog.json"), "utf8"));
|
|
@@ -20,6 +23,19 @@ function hasFlag(name) {
|
|
|
20
23
|
return args.includes(`--${name}`);
|
|
21
24
|
}
|
|
22
25
|
|
|
26
|
+
// Collects every `--var name=value` occurrence (the flag can repeat).
|
|
27
|
+
function varOverrides() {
|
|
28
|
+
const overrides = {};
|
|
29
|
+
for (let i = 0; i < args.length; i++) {
|
|
30
|
+
if (args[i] !== "--var") continue;
|
|
31
|
+
const pair = args[i + 1] ?? "";
|
|
32
|
+
const eq = pair.indexOf("=");
|
|
33
|
+
if (eq === -1) continue;
|
|
34
|
+
overrides[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
35
|
+
}
|
|
36
|
+
return overrides;
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
function printJson(value) {
|
|
24
40
|
console.log(JSON.stringify(value, null, 2));
|
|
25
41
|
}
|
|
@@ -43,6 +59,8 @@ Usage:
|
|
|
43
59
|
crondex add <id> [--dest <path>]
|
|
44
60
|
crondex recommend "<what you want done>" [--limit <n>] [--json]
|
|
45
61
|
crondex init <id> [--category <name>] [--dest <path>]
|
|
62
|
+
crondex deploy <id> [--target crontab|github-actions] [--mode script|prompt]
|
|
63
|
+
[--var name=value ...] [--dest <path>] [--install]
|
|
46
64
|
|
|
47
65
|
Examples:
|
|
48
66
|
crondex list --category devops
|
|
@@ -51,10 +69,19 @@ Examples:
|
|
|
51
69
|
crondex add backup-reminder --dest ./cron/backup-reminder.yaml
|
|
52
70
|
crondex recommend "warn me before my SSL cert expires"
|
|
53
71
|
crondex init ssl-cert-expiry-check --category security
|
|
72
|
+
crondex deploy ssl-cert-expiry-check --var host=example.com --var port=443
|
|
73
|
+
crondex deploy repo-health-check --target github-actions
|
|
54
74
|
|
|
55
75
|
Add --json to list/categories/show/recommend for machine-readable output —
|
|
56
76
|
useful when an agent is parsing crondex's output programmatically instead
|
|
57
77
|
of a human reading it.
|
|
78
|
+
|
|
79
|
+
deploy turns a job into something you can actually run: --target crontab
|
|
80
|
+
(default) prints a ready crontab line, or installs it into your own
|
|
81
|
+
crontab with --install; --target github-actions writes a scheduled
|
|
82
|
+
workflow file (default .github/workflows/<id>.yml). --var overrides a
|
|
83
|
+
job's variable defaults (repeatable). hybrid jobs default to --mode script
|
|
84
|
+
(zero tokens); pass --mode prompt to deploy the agent-prompt side instead.
|
|
58
85
|
`);
|
|
59
86
|
}
|
|
60
87
|
|
|
@@ -134,11 +161,16 @@ function categories() {
|
|
|
134
161
|
const counts = {};
|
|
135
162
|
for (const j of CATALOG.jobs) counts[j.category] = (counts[j.category] ?? 0) + 1;
|
|
136
163
|
const sorted = Object.entries(counts).sort(([a], [b]) => a.localeCompare(b));
|
|
137
|
-
if (hasFlag("json"))
|
|
164
|
+
if (hasFlag("json")) {
|
|
165
|
+
return printJson(
|
|
166
|
+
sorted.map(([category, count]) => ({ category, count, description: CATEGORY_DESCRIPTIONS[category] ?? "" }))
|
|
167
|
+
);
|
|
168
|
+
}
|
|
138
169
|
console.log(catalogInfoLine());
|
|
139
170
|
console.log();
|
|
140
171
|
for (const [cat, n] of sorted) {
|
|
141
172
|
console.log(`${cat} (${n})`);
|
|
173
|
+
if (CATEGORY_DESCRIPTIONS[cat]) console.log(` ${CATEGORY_DESCRIPTIONS[cat]}`);
|
|
142
174
|
}
|
|
143
175
|
}
|
|
144
176
|
|
|
@@ -162,6 +194,66 @@ function init(id) {
|
|
|
162
194
|
console.log(`wrote ${dest} — fill in the fields, then \`npm run validate\` (see CONTRIBUTING.md to submit it upstream).`);
|
|
163
195
|
}
|
|
164
196
|
|
|
197
|
+
function installCrontabLine(id, line) {
|
|
198
|
+
let existing = "";
|
|
199
|
+
try {
|
|
200
|
+
existing = execFileSync("crontab", ["-l"], { encoding: "utf8" });
|
|
201
|
+
} catch {
|
|
202
|
+
existing = "";
|
|
203
|
+
}
|
|
204
|
+
const marker = `# crondex:${id}`;
|
|
205
|
+
const kept = existing
|
|
206
|
+
.split("\n")
|
|
207
|
+
.filter((l) => l.trim().length > 0 && !l.includes(marker));
|
|
208
|
+
const updated = [...kept, line].join("\n") + "\n";
|
|
209
|
+
execFileSync("crontab", ["-"], { input: updated });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function deploy(id) {
|
|
213
|
+
const meta = findJob(id);
|
|
214
|
+
const doc = yaml.load(readFileSync(join(ROOT, meta.path), "utf8"));
|
|
215
|
+
doc.path = meta.path;
|
|
216
|
+
const target = flag("target") ?? "crontab";
|
|
217
|
+
const mode = pickMode(doc, flag("mode"));
|
|
218
|
+
|
|
219
|
+
if (mode === "prompt" && !doc.prompt) {
|
|
220
|
+
console.error(`"${id}" has no prompt to deploy (runner: ${doc.runner}). Try without --mode prompt.`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
if (mode === "script" && !doc.command) {
|
|
224
|
+
console.error(`"${id}" has no command to deploy (runner: ${doc.runner}). Try --mode prompt.`);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const values = resolveVariables(doc, varOverrides());
|
|
229
|
+
const command = doc.command ? substitutePlaceholders(doc.command, values) : undefined;
|
|
230
|
+
const prompt = doc.prompt ? substitutePlaceholders(doc.prompt, values) : undefined;
|
|
231
|
+
|
|
232
|
+
if (target === "crontab") {
|
|
233
|
+
const line = buildCrontabLine(doc, mode === "prompt" ? prompt : command, mode === "prompt");
|
|
234
|
+
if (hasFlag("install")) {
|
|
235
|
+
installCrontabLine(id, line);
|
|
236
|
+
console.log(`installed into your crontab (replacing any previous "${id}" entry):`);
|
|
237
|
+
console.log(line);
|
|
238
|
+
} else {
|
|
239
|
+
console.log(line);
|
|
240
|
+
}
|
|
241
|
+
} else if (target === "github-actions") {
|
|
242
|
+
const workflow = buildGithubActionsWorkflow(doc, { command, prompt, mode });
|
|
243
|
+
const dest = flag("dest") ?? join(".github/workflows", `${id}.yml`);
|
|
244
|
+
if (existsSync(dest)) {
|
|
245
|
+
console.error(`${dest} already exists — refusing to overwrite. Pass --dest to choose another path.`);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
249
|
+
writeFileSync(dest, workflow);
|
|
250
|
+
console.log(`wrote ${dest}`);
|
|
251
|
+
} else {
|
|
252
|
+
console.error(`unknown --target "${target}" — use "crontab" or "github-actions".`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
165
257
|
function add(id) {
|
|
166
258
|
const meta = findJob(id);
|
|
167
259
|
const dest = flag("dest") ?? `./${id}.yaml`;
|
|
@@ -208,6 +300,13 @@ switch (cmd) {
|
|
|
208
300
|
}
|
|
209
301
|
recommend(args[0]);
|
|
210
302
|
break;
|
|
303
|
+
case "deploy":
|
|
304
|
+
if (!args[0]) {
|
|
305
|
+
console.error("usage: crondex deploy <id> [--target crontab|github-actions] [--mode script|prompt] [--var name=value ...] [--dest <path>] [--install]");
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
deploy(args[0]);
|
|
309
|
+
break;
|
|
211
310
|
default:
|
|
212
311
|
printHelp();
|
|
213
312
|
}
|