@wonsukchoi/crondex 0.13.0 → 0.15.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 +28 -12
- package/bin/crondex.js +219 -2
- 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/deploy.js +167 -0
- package/lib/duplicates-allowlist.js +18 -0
- package/lib/smoke-test.js +32 -0
- package/package.json +3 -1
- package/templates/job.template.yaml +49 -0
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,22 @@ 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
|
+
- `update <path>` — re-pull a job you already `add`ed/`init`ed (matched by
|
|
39
|
+
its `id` field) against the current catalog and overwrite it in place
|
|
40
|
+
- `deploy <id> [--target crontab|github-actions|systemd|docker] [--var name=value ...]`
|
|
41
|
+
— turn a job into something that actually runs: prints a ready crontab
|
|
42
|
+
line (or installs it into your own crontab with `--install`), writes a
|
|
43
|
+
scheduled GitHub Actions workflow file, writes a systemd
|
|
44
|
+
`<id>.service` + `<id>.timer` pair, or writes a Dockerfile + `/etc/cron.d`
|
|
45
|
+
entry that runs the job on its own schedule in a container. `--var`
|
|
46
|
+
overrides a variable's default (repeatable). `hybrid` jobs deploy their
|
|
47
|
+
`command` by default; pass `--mode prompt` to deploy the `prompt` side
|
|
48
|
+
instead — for crontab/systemd/docker targets that means wiring in your
|
|
49
|
+
own agent CLI via a `CRONDEX_AGENT_CLI` env var, since crondex can't
|
|
50
|
+
guess its syntax.
|
|
51
|
+
- `deploy --list-installed` — show every crondex-managed line in your
|
|
52
|
+
crontab (the ones left by `--install`)
|
|
53
|
+
- `uninstall <id>` — remove one of those installed crontab entries
|
|
38
54
|
|
|
39
55
|
Add `--json` to `list`/`categories`/`show`/`recommend` for machine-readable
|
|
40
56
|
output — useful when an agent is parsing the result programmatically
|
|
@@ -91,13 +107,13 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
|
|
|
91
107
|
`jobs/<category>/` directly.
|
|
92
108
|
|
|
93
109
|
<!-- BEGIN JOB SUMMARY -->
|
|
94
|
-
|
|
110
|
+
271 jobs across 44 categories:
|
|
95
111
|
|
|
96
112
|
| category | jobs | description |
|
|
97
113
|
|---|---|---|
|
|
98
114
|
| `agriculture` | 5 | Farm operations — weather risk, irrigation, equipment, market prices. |
|
|
99
|
-
| `automotive` |
|
|
100
|
-
| `childcare` |
|
|
115
|
+
| `automotive` | 5 | Repair shop workflow — repair orders, parts, loaners. |
|
|
116
|
+
| `childcare` | 5 | Daycare compliance and ops — ratios, immunizations, tuition. |
|
|
101
117
|
| `construction` | 5 | Job site compliance — permits, inspections, change orders, rentals. |
|
|
102
118
|
| `content` | 13 | Site/content health — SEO, broken links, freshness, repurposing. |
|
|
103
119
|
| `creator` | 5 | Influencer/creator ops — content calendar, cross-posting, sponsorships. |
|
|
@@ -107,10 +123,10 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
|
|
|
107
123
|
| `education` | 5 | Classroom ops — grading, attendance, deadlines, permission slips. |
|
|
108
124
|
| `events` | 5 | Event planning — budget, RSVPs, staffing, vendors, day-of check-in. |
|
|
109
125
|
| `finance` | 10 | Personal/business finance — budgets, invoices, taxes, subscriptions. |
|
|
110
|
-
| `fitness` |
|
|
126
|
+
| `fitness` | 5 | Gym/studio ops — memberships, class utilization, equipment. |
|
|
111
127
|
| `fleet` | 5 | Vehicle fleet compliance — maintenance, registration, licenses, fuel. |
|
|
112
128
|
| `gaming` | 5 | Streaming and community server ops — schedules, patches, tournaments. |
|
|
113
|
-
| `government` |
|
|
129
|
+
| `government` | 5 | Public-sector ops — records requests, permits, constituent casework. |
|
|
114
130
|
| `growth` | 5 | Lifecycle marketing — churn, trials, onboarding, reviews. |
|
|
115
131
|
| `healthcare` | 5 | Clinic ops — appointments, recalls, licenses, lab results. |
|
|
116
132
|
| `hiring` | 5 | Recruiting pipeline — candidates, offers, interviews, reqs. |
|
|
@@ -131,14 +147,14 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
|
|
|
131
147
|
| `productivity` | 9 | Work habits — inbox, standups, focus, meetings, reports. |
|
|
132
148
|
| `realestate` | 5 | Property management — leases, rent, vacancy, inspections, tax. |
|
|
133
149
|
| `restaurant` | 5 | Kitchen/FOH ops — food cost, labor cost, waste, inspections. |
|
|
134
|
-
| `retail` |
|
|
150
|
+
| `retail` | 5 | Store ops — till reconciliation, opening checklist, scheduling. |
|
|
135
151
|
| `sales` | 5 | Pipeline ops — leads, deals, quota, CRM sync. |
|
|
136
152
|
| `security` | 14 | Security posture — keys, certs, access, scans, firewalls. |
|
|
137
153
|
| `support` | 5 | Helpdesk ops — SLA, backlog, CSAT, agent workload. |
|
|
138
154
|
| `team` | 5 | Team ops — 1:1s, on-call, PTO, anniversaries. |
|
|
139
155
|
| `travel` | 5 | Trip logistics — flights, passports, visas, insurance, miles. |
|
|
140
|
-
| `veterinary` |
|
|
141
|
-
| `warehousing` |
|
|
156
|
+
| `veterinary` | 5 | Clinic ops for animals — vaccines, controlled substances, boarding. |
|
|
157
|
+
| `warehousing` | 5 | Warehouse ops — dock scheduling, pick/pack errors, climate control. |
|
|
142
158
|
<!-- END JOB SUMMARY -->
|
|
143
159
|
|
|
144
160
|
---
|
|
@@ -148,12 +164,12 @@ tags, variables) use `crondex list`, `crondex recommend`, or browse
|
|
|
148
164
|
```
|
|
149
165
|
crondex/
|
|
150
166
|
├── llms.txt agent-discovery manifest (llms.txt convention)
|
|
151
|
-
├── bin/crondex.js CLI: list / categories / show / add / recommend / init
|
|
152
|
-
├── lib/recommend
|
|
167
|
+
├── bin/crondex.js CLI: list / categories / show / add / recommend / init / update / deploy / uninstall
|
|
168
|
+
├── lib/ recommend, deploy, and catalog-building logic (unit tested in test/)
|
|
153
169
|
├── catalog.json generated index of every job — read this first
|
|
154
170
|
├── schema/job.schema.json spec every job file follows
|
|
155
171
|
├── jobs/ one YAML per job, grouped by category subdirectory
|
|
156
|
-
└── scripts/ build-catalog.js, validate-jobs.js, lint-shell.js
|
|
172
|
+
└── scripts/ build-catalog.js, validate-jobs.js, lint-shell.js, check-duplicates.js, smoke-test.js
|
|
157
173
|
```
|
|
158
174
|
|
|
159
175
|
---
|
package/bin/crondex.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
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";
|
|
7
8
|
import { CATEGORY_DESCRIPTIONS } from "../lib/category-descriptions.js";
|
|
9
|
+
import {
|
|
10
|
+
resolveVariables,
|
|
11
|
+
substitutePlaceholders,
|
|
12
|
+
pickMode,
|
|
13
|
+
buildCrontabLine,
|
|
14
|
+
buildGithubActionsWorkflow,
|
|
15
|
+
buildSystemdUnits,
|
|
16
|
+
buildDockerArtifacts,
|
|
17
|
+
} from "../lib/deploy.js";
|
|
8
18
|
|
|
9
19
|
const ROOT = new URL("..", import.meta.url).pathname;
|
|
10
20
|
const CATALOG = JSON.parse(readFileSync(join(ROOT, "catalog.json"), "utf8"));
|
|
@@ -21,6 +31,19 @@ function hasFlag(name) {
|
|
|
21
31
|
return args.includes(`--${name}`);
|
|
22
32
|
}
|
|
23
33
|
|
|
34
|
+
// Collects every `--var name=value` occurrence (the flag can repeat).
|
|
35
|
+
function varOverrides() {
|
|
36
|
+
const overrides = {};
|
|
37
|
+
for (let i = 0; i < args.length; i++) {
|
|
38
|
+
if (args[i] !== "--var") continue;
|
|
39
|
+
const pair = args[i + 1] ?? "";
|
|
40
|
+
const eq = pair.indexOf("=");
|
|
41
|
+
if (eq === -1) continue;
|
|
42
|
+
overrides[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
43
|
+
}
|
|
44
|
+
return overrides;
|
|
45
|
+
}
|
|
46
|
+
|
|
24
47
|
function printJson(value) {
|
|
25
48
|
console.log(JSON.stringify(value, null, 2));
|
|
26
49
|
}
|
|
@@ -44,6 +67,11 @@ Usage:
|
|
|
44
67
|
crondex add <id> [--dest <path>]
|
|
45
68
|
crondex recommend "<what you want done>" [--limit <n>] [--json]
|
|
46
69
|
crondex init <id> [--category <name>] [--dest <path>]
|
|
70
|
+
crondex update <path>
|
|
71
|
+
crondex deploy <id> [--target crontab|github-actions|systemd|docker] [--mode script|prompt]
|
|
72
|
+
[--var name=value ...] [--dest <path>] [--install]
|
|
73
|
+
crondex deploy --list-installed [--json]
|
|
74
|
+
crondex uninstall <id>
|
|
47
75
|
|
|
48
76
|
Examples:
|
|
49
77
|
crondex list --category devops
|
|
@@ -52,10 +80,34 @@ Examples:
|
|
|
52
80
|
crondex add backup-reminder --dest ./cron/backup-reminder.yaml
|
|
53
81
|
crondex recommend "warn me before my SSL cert expires"
|
|
54
82
|
crondex init ssl-cert-expiry-check --category security
|
|
83
|
+
crondex update ./cron/backup-reminder.yaml
|
|
84
|
+
crondex deploy ssl-cert-expiry-check --var host=example.com --var port=443
|
|
85
|
+
crondex deploy repo-health-check --target github-actions
|
|
86
|
+
crondex deploy repo-health-check --target systemd --dest ./systemd
|
|
87
|
+
crondex deploy repo-health-check --target docker --dest ./docker/repo-health-check
|
|
88
|
+
crondex deploy --list-installed
|
|
89
|
+
crondex uninstall ssl-cert-expiry-check
|
|
55
90
|
|
|
56
91
|
Add --json to list/categories/show/recommend for machine-readable output —
|
|
57
92
|
useful when an agent is parsing crondex's output programmatically instead
|
|
58
93
|
of a human reading it.
|
|
94
|
+
|
|
95
|
+
deploy turns a job into something you can actually run: --target crontab
|
|
96
|
+
(default) prints a ready crontab line, or installs it into your own
|
|
97
|
+
crontab with --install; --target github-actions writes a scheduled
|
|
98
|
+
workflow file (default .github/workflows/<id>.yml); --target systemd
|
|
99
|
+
writes a <id>.service + <id>.timer pair (default ./systemd/); --target
|
|
100
|
+
docker writes a Dockerfile + crontab pair that runs the job on its own
|
|
101
|
+
schedule in a container (default ./docker/<id>/). --var overrides a job's
|
|
102
|
+
variable defaults (repeatable). hybrid jobs default to --mode script (zero
|
|
103
|
+
tokens); pass --mode prompt to deploy the agent-prompt side instead.
|
|
104
|
+
|
|
105
|
+
deploy --list-installed shows every crondex-managed line in your crontab
|
|
106
|
+
(the ones with a "# crondex:<id>" marker, as left by --install). uninstall
|
|
107
|
+
removes one of those by id.
|
|
108
|
+
|
|
109
|
+
update re-pulls a job you already added/inited (matched by its "id" field)
|
|
110
|
+
against the current catalog and overwrites the local file in place.
|
|
59
111
|
`);
|
|
60
112
|
}
|
|
61
113
|
|
|
@@ -168,6 +220,150 @@ function init(id) {
|
|
|
168
220
|
console.log(`wrote ${dest} — fill in the fields, then \`npm run validate\` (see CONTRIBUTING.md to submit it upstream).`);
|
|
169
221
|
}
|
|
170
222
|
|
|
223
|
+
function readCrontab() {
|
|
224
|
+
try {
|
|
225
|
+
return execFileSync("crontab", ["-l"], { encoding: "utf8" });
|
|
226
|
+
} catch {
|
|
227
|
+
return "";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function installCrontabLine(id, line) {
|
|
232
|
+
const marker = `# crondex:${id}`;
|
|
233
|
+
const kept = readCrontab()
|
|
234
|
+
.split("\n")
|
|
235
|
+
.filter((l) => l.trim().length > 0 && !l.includes(marker));
|
|
236
|
+
const updated = [...kept, line].join("\n") + "\n";
|
|
237
|
+
execFileSync("crontab", ["-"], { input: updated });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function uninstall(id) {
|
|
241
|
+
const marker = `# crondex:${id}`;
|
|
242
|
+
const existing = readCrontab();
|
|
243
|
+
if (!existing.includes(marker)) {
|
|
244
|
+
console.error(`no installed crontab entry for "${id}" — run "crondex deploy --list-installed" to see what's there.`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
const kept = existing.split("\n").filter((l) => l.trim().length > 0 && !l.includes(marker));
|
|
248
|
+
execFileSync("crontab", ["-"], { input: kept.join("\n") + "\n" });
|
|
249
|
+
console.log(`removed "${id}" from your crontab.`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function listInstalled() {
|
|
253
|
+
const managed = readCrontab()
|
|
254
|
+
.split("\n")
|
|
255
|
+
.filter((l) => l.includes("# crondex:"));
|
|
256
|
+
if (hasFlag("json")) {
|
|
257
|
+
return printJson(
|
|
258
|
+
managed.map((l) => ({ id: l.match(/# crondex:(\S+)/)?.[1], line: l }))
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
if (!managed.length) {
|
|
262
|
+
console.log("no crondex-managed crontab entries installed.");
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
for (const l of managed) console.log(l);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function update(path) {
|
|
269
|
+
if (!existsSync(path)) {
|
|
270
|
+
console.error(`${path} does not exist.`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
const localRaw = readFileSync(path, "utf8");
|
|
274
|
+
let localDoc;
|
|
275
|
+
try {
|
|
276
|
+
localDoc = yaml.load(localRaw);
|
|
277
|
+
} catch (e) {
|
|
278
|
+
console.error(`${path} isn't valid YAML: ${e.message}`);
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
if (!localDoc?.id) {
|
|
282
|
+
console.error(`${path} has no "id" field — can't match it to a catalog job.`);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
const meta = findJob(localDoc.id);
|
|
286
|
+
const latestRaw = readFileSync(join(ROOT, meta.path), "utf8");
|
|
287
|
+
if (latestRaw === localRaw) {
|
|
288
|
+
console.log(`${path} is already up to date with "${localDoc.id}".`);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
writeFileSync(path, latestRaw);
|
|
292
|
+
console.log(`updated ${path} to the latest "${localDoc.id}" from the catalog.`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function deploy(id) {
|
|
296
|
+
if (id === undefined && hasFlag("list-installed")) return listInstalled();
|
|
297
|
+
const meta = findJob(id);
|
|
298
|
+
const doc = yaml.load(readFileSync(join(ROOT, meta.path), "utf8"));
|
|
299
|
+
doc.path = meta.path;
|
|
300
|
+
const target = flag("target") ?? "crontab";
|
|
301
|
+
const mode = pickMode(doc, flag("mode"));
|
|
302
|
+
|
|
303
|
+
if (mode === "prompt" && !doc.prompt) {
|
|
304
|
+
console.error(`"${id}" has no prompt to deploy (runner: ${doc.runner}). Try without --mode prompt.`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
if (mode === "script" && !doc.command) {
|
|
308
|
+
console.error(`"${id}" has no command to deploy (runner: ${doc.runner}). Try --mode prompt.`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const values = resolveVariables(doc, varOverrides());
|
|
313
|
+
const command = doc.command ? substitutePlaceholders(doc.command, values) : undefined;
|
|
314
|
+
const prompt = doc.prompt ? substitutePlaceholders(doc.prompt, values) : undefined;
|
|
315
|
+
|
|
316
|
+
if (target === "crontab") {
|
|
317
|
+
const line = buildCrontabLine(doc, mode === "prompt" ? prompt : command, mode === "prompt");
|
|
318
|
+
if (hasFlag("install")) {
|
|
319
|
+
installCrontabLine(id, line);
|
|
320
|
+
console.log(`installed into your crontab (replacing any previous "${id}" entry):`);
|
|
321
|
+
console.log(line);
|
|
322
|
+
} else {
|
|
323
|
+
console.log(line);
|
|
324
|
+
}
|
|
325
|
+
} else if (target === "github-actions") {
|
|
326
|
+
const workflow = buildGithubActionsWorkflow(doc, { command, prompt, mode });
|
|
327
|
+
const dest = flag("dest") ?? join(".github/workflows", `${id}.yml`);
|
|
328
|
+
if (existsSync(dest)) {
|
|
329
|
+
console.error(`${dest} already exists — refusing to overwrite. Pass --dest to choose another path.`);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
333
|
+
writeFileSync(dest, workflow);
|
|
334
|
+
console.log(`wrote ${dest}`);
|
|
335
|
+
} else if (target === "systemd") {
|
|
336
|
+
const { service, timer } = buildSystemdUnits(doc, mode === "prompt" ? prompt : command, mode === "prompt");
|
|
337
|
+
const destDir = flag("dest") ?? "./systemd";
|
|
338
|
+
const serviceDest = join(destDir, `${id}.service`);
|
|
339
|
+
const timerDest = join(destDir, `${id}.timer`);
|
|
340
|
+
if (existsSync(serviceDest) || existsSync(timerDest)) {
|
|
341
|
+
console.error(`${serviceDest} or ${timerDest} already exists — refusing to overwrite. Pass --dest to choose another directory.`);
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
mkdirSync(destDir, { recursive: true });
|
|
345
|
+
writeFileSync(serviceDest, service);
|
|
346
|
+
writeFileSync(timerDest, timer);
|
|
347
|
+
console.log(`wrote ${serviceDest} and ${timerDest} — enable with:\n systemctl --user enable --now ${id}.timer`);
|
|
348
|
+
} else if (target === "docker") {
|
|
349
|
+
const { dockerfile, crontab } = buildDockerArtifacts(doc, mode === "prompt" ? prompt : command, mode === "prompt");
|
|
350
|
+
const destDir = flag("dest") ?? join("./docker", id);
|
|
351
|
+
const dockerfileDest = join(destDir, "Dockerfile");
|
|
352
|
+
const crontabDest = join(destDir, "crontab");
|
|
353
|
+
if (existsSync(dockerfileDest) || existsSync(crontabDest)) {
|
|
354
|
+
console.error(`${dockerfileDest} or ${crontabDest} already exists — refusing to overwrite. Pass --dest to choose another directory.`);
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
mkdirSync(destDir, { recursive: true });
|
|
358
|
+
writeFileSync(dockerfileDest, dockerfile);
|
|
359
|
+
writeFileSync(crontabDest, crontab);
|
|
360
|
+
console.log(`wrote ${dockerfileDest} and ${crontabDest} — build with:\n docker build -t ${id} ${destDir}`);
|
|
361
|
+
} else {
|
|
362
|
+
console.error(`unknown --target "${target}" — use "crontab", "github-actions", "systemd", or "docker".`);
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
171
367
|
function add(id) {
|
|
172
368
|
const meta = findJob(id);
|
|
173
369
|
const dest = flag("dest") ?? `./${id}.yaml`;
|
|
@@ -214,6 +410,27 @@ switch (cmd) {
|
|
|
214
410
|
}
|
|
215
411
|
recommend(args[0]);
|
|
216
412
|
break;
|
|
413
|
+
case "deploy":
|
|
414
|
+
if (!args[0] && !hasFlag("list-installed")) {
|
|
415
|
+
console.error("usage: crondex deploy <id> [--target crontab|github-actions|systemd|docker] [--mode script|prompt] [--var name=value ...] [--dest <path>] [--install]\n or: crondex deploy --list-installed [--json]");
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
deploy(hasFlag("list-installed") ? undefined : args[0]);
|
|
419
|
+
break;
|
|
420
|
+
case "uninstall":
|
|
421
|
+
if (!args[0]) {
|
|
422
|
+
console.error("usage: crondex uninstall <id>");
|
|
423
|
+
process.exit(1);
|
|
424
|
+
}
|
|
425
|
+
uninstall(args[0]);
|
|
426
|
+
break;
|
|
427
|
+
case "update":
|
|
428
|
+
if (!args[0]) {
|
|
429
|
+
console.error("usage: crondex update <path>");
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
update(args[0]);
|
|
433
|
+
break;
|
|
217
434
|
default:
|
|
218
435
|
printHelp();
|
|
219
436
|
}
|