@cremini/skillpack 1.2.9 → 1.2.10

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.
Files changed (2) hide show
  1. package/dist/cli.js +108 -58
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9,6 +9,26 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/job-schedule.ts
13
+ function normalizeJobCron(cron2) {
14
+ if (typeof cron2 !== "string") {
15
+ return void 0;
16
+ }
17
+ const normalized = cron2.trim();
18
+ return normalized ? normalized : void 0;
19
+ }
20
+ function hasJobSchedule(jobOrCron) {
21
+ if (typeof jobOrCron === "string" || jobOrCron == null) {
22
+ return !!normalizeJobCron(jobOrCron);
23
+ }
24
+ return !!normalizeJobCron(jobOrCron.cron);
25
+ }
26
+ var init_job_schedule = __esm({
27
+ "src/job-schedule.ts"() {
28
+ "use strict";
29
+ }
30
+ });
31
+
12
32
  // src/job-config.ts
13
33
  import fs2 from "fs";
14
34
  import path2 from "path";
@@ -27,9 +47,9 @@ function validateScheduledJobConfig(value, sourceLabel, index) {
27
47
  `Invalid job config from ${sourceLabel}: "jobs[${index}].name" is required`
28
48
  );
29
49
  }
30
- if (typeof job.cron !== "string" || !job.cron.trim()) {
50
+ if (job.cron !== void 0 && typeof job.cron !== "string") {
31
51
  throw new Error(
32
- `Invalid job config from ${sourceLabel}: "jobs[${index}].cron" is required`
52
+ `Invalid job config from ${sourceLabel}: "jobs[${index}].cron" must be a string`
33
53
  );
34
54
  }
35
55
  if (typeof job.prompt !== "string" || !job.prompt.trim()) {
@@ -87,18 +107,25 @@ function validateJobFileShape(value, sourceLabel) {
87
107
  function normalizeJobFile(jobFile) {
88
108
  return {
89
109
  jobs: jobFile.jobs.map((job) => ({
90
- name: job.name.trim(),
91
- cron: job.cron.trim(),
92
- prompt: job.prompt,
93
- notify: {
94
- adapter: job.notify.adapter.trim(),
95
- channelId: job.notify.channelId.trim()
96
- },
97
- ...job.enabled !== void 0 ? { enabled: job.enabled } : {},
98
- ...job.timezone !== void 0 ? { timezone: job.timezone.trim() } : {}
110
+ ...normalizeScheduledJobConfig(job)
99
111
  }))
100
112
  };
101
113
  }
114
+ function normalizeScheduledJobConfig(job) {
115
+ const normalizedCron = normalizeJobCron(job.cron);
116
+ const normalizedTimezone = typeof job.timezone === "string" && job.timezone.trim() ? job.timezone.trim() : void 0;
117
+ return {
118
+ name: job.name.trim(),
119
+ ...normalizedCron ? { cron: normalizedCron } : {},
120
+ prompt: job.prompt,
121
+ notify: {
122
+ adapter: job.notify.adapter.trim(),
123
+ channelId: job.notify.channelId.trim()
124
+ },
125
+ ...normalizedCron && job.enabled !== void 0 ? { enabled: job.enabled } : {},
126
+ ...normalizedCron && normalizedTimezone ? { timezone: normalizedTimezone } : {}
127
+ };
128
+ }
102
129
  function loadJobFile(workDir) {
103
130
  const filePath = getJobFilePath(workDir);
104
131
  if (!fs2.existsSync(filePath)) {
@@ -119,6 +146,7 @@ var JOB_FILE;
119
146
  var init_job_config = __esm({
120
147
  "src/job-config.ts"() {
121
148
  "use strict";
149
+ init_job_schedule();
122
150
  JOB_FILE = "job.json";
123
151
  }
124
152
  });
@@ -1402,11 +1430,15 @@ function isValidTimezone(tz) {
1402
1430
  function isValidJobName(name) {
1403
1431
  return VALID_JOB_NAME.test(name) && name.length <= 64;
1404
1432
  }
1433
+ function isRecurringJob(jobConfig) {
1434
+ return hasJobSchedule(jobConfig);
1435
+ }
1405
1436
  var VALID_JOB_NAME, SchedulerAdapter;
1406
1437
  var init_scheduler = __esm({
1407
1438
  "src/runtime/adapters/scheduler.ts"() {
1408
1439
  "use strict";
1409
1440
  init_job_config();
1441
+ init_job_schedule();
1410
1442
  VALID_JOB_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
1411
1443
  SchedulerAdapter = class {
1412
1444
  name = "scheduler";
@@ -1423,10 +1455,13 @@ var init_scheduler = __esm({
1423
1455
  const jobConfigs = loadJobFile(this.rootDir).jobs;
1424
1456
  let scheduledCount = 0;
1425
1457
  let disabledCount = 0;
1458
+ let oneTimeCount = 0;
1426
1459
  for (const jc of jobConfigs) {
1427
1460
  const result = this.registerJob(jc);
1428
1461
  if (result.registered) {
1429
- if (jc.enabled === false) {
1462
+ if (!isRecurringJob(jc)) {
1463
+ oneTimeCount++;
1464
+ } else if (jc.enabled === false) {
1430
1465
  disabledCount++;
1431
1466
  } else {
1432
1467
  scheduledCount++;
@@ -1436,6 +1471,7 @@ var init_scheduler = __esm({
1436
1471
  const parts = [];
1437
1472
  if (scheduledCount > 0) parts.push(`${scheduledCount} active`);
1438
1473
  if (disabledCount > 0) parts.push(`${disabledCount} disabled`);
1474
+ if (oneTimeCount > 0) parts.push(`${oneTimeCount} one-time`);
1439
1475
  if (parts.length > 0) {
1440
1476
  console.log(`[SchedulerAdapter] Started with ${parts.join(", ")} job(s)`);
1441
1477
  } else {
@@ -1450,43 +1486,43 @@ var init_scheduler = __esm({
1450
1486
  * Does NOT persist – callers decide when to persist.
1451
1487
  */
1452
1488
  registerJob(jobConfig) {
1453
- if (!isValidJobName(jobConfig.name)) {
1454
- const msg = `[Scheduler] Invalid job name "${jobConfig.name}": must match ${VALID_JOB_NAME} and be \u226464 chars`;
1489
+ const normalizedConfig = normalizeScheduledJobConfig(jobConfig);
1490
+ const normalizedCron = normalizeJobCron(normalizedConfig.cron);
1491
+ if (!isValidJobName(normalizedConfig.name)) {
1492
+ const msg = `[Scheduler] Invalid job name "${normalizedConfig.name}": must match ${VALID_JOB_NAME} and be \u226464 chars`;
1455
1493
  console.error(msg);
1456
1494
  return { registered: false, message: msg };
1457
1495
  }
1458
- if (!cron.validate(jobConfig.cron)) {
1459
- const msg = `[Scheduler] Invalid cron expression for job "${jobConfig.name}": ${jobConfig.cron}`;
1460
- console.error(msg);
1461
- return { registered: false, message: msg };
1462
- }
1463
- if (jobConfig.timezone && !isValidTimezone(jobConfig.timezone)) {
1464
- const msg = `[Scheduler] Invalid timezone for job "${jobConfig.name}": ${jobConfig.timezone}`;
1465
- console.error(msg);
1466
- return { registered: false, message: msg };
1496
+ if (normalizedCron) {
1497
+ if (!cron.validate(normalizedCron)) {
1498
+ const msg = `[Scheduler] Invalid cron expression for job "${normalizedConfig.name}": ${normalizedCron}`;
1499
+ console.error(msg);
1500
+ return { registered: false, message: msg };
1501
+ }
1502
+ if (normalizedConfig.timezone && !isValidTimezone(normalizedConfig.timezone)) {
1503
+ const msg = `[Scheduler] Invalid timezone for job "${normalizedConfig.name}": ${normalizedConfig.timezone}`;
1504
+ console.error(msg);
1505
+ return { registered: false, message: msg };
1506
+ }
1467
1507
  }
1468
- this.removeFromMap(jobConfig.name);
1508
+ this.removeFromMap(normalizedConfig.name);
1469
1509
  let task = null;
1470
- if (jobConfig.enabled !== false) {
1471
- task = cron.schedule(
1472
- jobConfig.cron,
1473
- () => {
1474
- void this.runJob(jobConfig);
1475
- },
1476
- {
1477
- timezone: jobConfig.timezone
1478
- }
1510
+ if (normalizedCron && normalizedConfig.enabled !== false) {
1511
+ task = this.createCronTask(normalizedConfig);
1512
+ console.log(
1513
+ `[Scheduler] Job "${normalizedConfig.name}" scheduled: ${normalizedCron}${normalizedConfig.timezone ? ` (${normalizedConfig.timezone})` : ""}`
1479
1514
  );
1515
+ } else if (normalizedCron) {
1480
1516
  console.log(
1481
- `[Scheduler] Job "${jobConfig.name}" scheduled: ${jobConfig.cron}${jobConfig.timezone ? ` (${jobConfig.timezone})` : ""}`
1517
+ `[Scheduler] Job "${normalizedConfig.name}" registered (disabled)`
1482
1518
  );
1483
1519
  } else {
1484
1520
  console.log(
1485
- `[Scheduler] Job "${jobConfig.name}" registered (disabled)`
1521
+ `[Scheduler] Job "${normalizedConfig.name}" registered as one-time (manual trigger only)`
1486
1522
  );
1487
1523
  }
1488
- this.jobs.set(jobConfig.name, {
1489
- config: jobConfig,
1524
+ this.jobs.set(normalizedConfig.name, {
1525
+ config: normalizedConfig,
1490
1526
  task,
1491
1527
  running: false,
1492
1528
  notifyFailed: false
@@ -1599,10 +1635,11 @@ var init_scheduler = __esm({
1599
1635
  return { success: false, message: result.message };
1600
1636
  }
1601
1637
  this.persistJobs();
1602
- const enabled = jobConfig.enabled !== false;
1638
+ const recurring = isRecurringJob(jobConfig);
1639
+ const enabled = recurring ? jobConfig.enabled !== false : true;
1603
1640
  return {
1604
1641
  success: true,
1605
- message: enabled ? `Job "${jobConfig.name}" created and scheduled.` : `Job "${jobConfig.name}" created (disabled).`
1642
+ message: recurring ? enabled ? `Job "${jobConfig.name}" created and scheduled.` : `Job "${jobConfig.name}" created (disabled).` : `Job "${jobConfig.name}" created as a one-time task.`
1606
1643
  };
1607
1644
  }
1608
1645
  /**
@@ -1647,17 +1684,15 @@ var init_scheduler = __esm({
1647
1684
  if (!job) {
1648
1685
  return { success: false, message: `Job "${name}" not found.` };
1649
1686
  }
1687
+ if (!isRecurringJob(job.config)) {
1688
+ return {
1689
+ success: false,
1690
+ message: `Job "${name}" does not have a schedule and cannot be enabled or disabled.`
1691
+ };
1692
+ }
1650
1693
  job.config.enabled = enabled;
1651
1694
  if (enabled && !job.task) {
1652
- job.task = cron.schedule(
1653
- job.config.cron,
1654
- () => {
1655
- void this.runJob(job.config);
1656
- },
1657
- {
1658
- timezone: job.config.timezone
1659
- }
1660
- );
1695
+ job.task = this.createCronTask(job.config);
1661
1696
  } else if (enabled && job.task) {
1662
1697
  job.task.start();
1663
1698
  } else if (!enabled && job.task) {
@@ -1703,13 +1738,13 @@ var init_scheduler = __esm({
1703
1738
  for (const [, job] of this.jobs) {
1704
1739
  result.push({
1705
1740
  name: job.config.name,
1706
- cron: job.config.cron,
1741
+ ...job.config.cron ? { cron: job.config.cron } : {},
1707
1742
  prompt: job.config.prompt,
1708
1743
  notify: job.config.notify,
1709
- enabled: job.config.enabled !== false,
1710
- timezone: job.config.timezone,
1711
- lastRunAt: job.lastRunAt,
1712
- lastError: job.lastError,
1744
+ enabled: isRecurringJob(job.config) ? job.config.enabled !== false : true,
1745
+ ...job.config.timezone ? { timezone: job.config.timezone } : {},
1746
+ ...job.lastRunAt ? { lastRunAt: job.lastRunAt } : {},
1747
+ ...job.lastError ? { lastError: job.lastError } : {},
1713
1748
  running: job.running,
1714
1749
  notifyFailed: job.notifyFailed
1715
1750
  });
@@ -1749,6 +1784,21 @@ var init_scheduler = __esm({
1749
1784
  this.jobs.clear();
1750
1785
  console.log("[SchedulerAdapter] All jobs stopped.");
1751
1786
  }
1787
+ createCronTask(jobConfig) {
1788
+ const cronExpr = normalizeJobCron(jobConfig.cron);
1789
+ if (!cronExpr) {
1790
+ throw new Error(`Job "${jobConfig.name}" does not have a valid cron expression`);
1791
+ }
1792
+ return cron.schedule(
1793
+ cronExpr,
1794
+ () => {
1795
+ void this.runJob(jobConfig);
1796
+ },
1797
+ {
1798
+ timezone: jobConfig.timezone
1799
+ }
1800
+ );
1801
+ }
1752
1802
  };
1753
1803
  }
1754
1804
  });
@@ -4615,20 +4665,20 @@ var WebAdapter = class {
4615
4665
  return;
4616
4666
  }
4617
4667
  const { name, cron: cronExpr, prompt, notify, enabled, timezone } = req.body;
4618
- if (!name || !cronExpr || !prompt || !notify?.adapter || !notify?.channelId) {
4668
+ if (!name || !prompt || !notify?.adapter || !notify?.channelId) {
4619
4669
  res.status(400).json({
4620
4670
  success: false,
4621
- message: "Required fields: name, cron, prompt, notify.adapter, notify.channelId"
4671
+ message: "Required fields: name, prompt, notify.adapter, notify.channelId"
4622
4672
  });
4623
4673
  return;
4624
4674
  }
4625
4675
  const result = scheduler.addJob({
4626
4676
  name,
4627
- cron: cronExpr,
4677
+ ...typeof cronExpr === "string" ? { cron: cronExpr } : {},
4628
4678
  prompt,
4629
4679
  notify,
4630
- enabled: enabled !== false,
4631
- timezone
4680
+ ...typeof enabled === "boolean" ? { enabled } : {},
4681
+ ...typeof timezone === "string" ? { timezone } : {}
4632
4682
  });
4633
4683
  res.json(result);
4634
4684
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cremini/skillpack",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
4
4
  "description": "Pack AI Skills into Local Agents",
5
5
  "type": "module",
6
6
  "repository": {