@grackle-ai/plugin-scheduling 0.98.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 +19 -0
- package/dist/cron-phase.d.ts +49 -0
- package/dist/cron-phase.d.ts.map +1 -0
- package/dist/cron-phase.js +87 -0
- package/dist/cron-phase.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/schedule-expression.d.ts +43 -0
- package/dist/schedule-expression.d.ts.map +1 -0
- package/dist/schedule-expression.js +105 -0
- package/dist/schedule-expression.js.map +1 -0
- package/dist/schedule-handlers.d.ts +24 -0
- package/dist/schedule-handlers.d.ts.map +1 -0
- package/dist/schedule-handlers.js +138 -0
- package/dist/schedule-handlers.js.map +1 -0
- package/dist/schedule-proto.d.ts +8 -0
- package/dist/schedule-proto.d.ts.map +1 -0
- package/dist/schedule-proto.js +25 -0
- package/dist/schedule-proto.js.map +1 -0
- package/dist/scheduling-plugin.d.ts +16 -0
- package/dist/scheduling-plugin.d.ts.map +1 -0
- package/dist/scheduling-plugin.js +41 -0
- package/dist/scheduling-plugin.js.map +1 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# @grackle-ai/plugin-scheduling
|
|
2
|
+
|
|
3
|
+
Scheduling plugin for Grackle. Provides:
|
|
4
|
+
|
|
5
|
+
- **Schedule CRUD handlers** — `createSchedule`, `listSchedules`, `getSchedule`, `updateSchedule`, `deleteSchedule` gRPC methods
|
|
6
|
+
- **Cron reconciliation phase** — fires due schedules on each tick, creates tasks, enqueues for dispatch
|
|
7
|
+
- **Schedule expression parsing** — interval shorthand (`30s`, `5m`, `1h`, `1d`) and standard 5-field cron syntax
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { createSchedulingPlugin } from "@grackle-ai/plugin-scheduling";
|
|
13
|
+
import { createCorePlugin } from "@grackle-ai/server";
|
|
14
|
+
import { loadPlugins } from "@grackle-ai/plugin-sdk";
|
|
15
|
+
|
|
16
|
+
const loaded = await loadPlugins([createCorePlugin(), createSchedulingPlugin()], ctx);
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The scheduling plugin declares `dependencies: ["core"]` so it is always loaded after the core plugin.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron reconciliation phase — fires due schedules on each tick.
|
|
3
|
+
*
|
|
4
|
+
* Creates tasks for due schedules and enqueues them for dispatch.
|
|
5
|
+
* The dispatch phase (separate reconciliation phase) handles starting
|
|
6
|
+
* sessions, respecting concurrency limits, and environment resolution.
|
|
7
|
+
*/
|
|
8
|
+
import type { Logger } from "pino";
|
|
9
|
+
import type { ScheduleRow } from "@grackle-ai/database";
|
|
10
|
+
import type { GrackleEventType } from "@grackle-ai/core";
|
|
11
|
+
import type { ReconciliationPhase } from "@grackle-ai/plugin-sdk";
|
|
12
|
+
/** Dependencies injected into the cron phase for testability. */
|
|
13
|
+
export interface CronPhaseDeps {
|
|
14
|
+
/** Query the schedule store for due entries. */
|
|
15
|
+
getDueSchedules: () => ScheduleRow[];
|
|
16
|
+
/** Advance a schedule after firing (update lastRunAt, nextRunAt, runCount). */
|
|
17
|
+
advanceSchedule: (id: string, lastRunAt: string, nextRunAt: string) => void;
|
|
18
|
+
/** Create a new task in the task store. */
|
|
19
|
+
createTask: (id: string, workspaceId: string | undefined, title: string, description: string, dependsOn: string[], workspaceSlug: string, parentTaskId?: string, canDecompose?: boolean, defaultPersonaId?: string) => void;
|
|
20
|
+
/** Set the schedule_id FK on a task. */
|
|
21
|
+
setTaskScheduleId: (taskId: string, scheduleId: string) => void;
|
|
22
|
+
/** Enqueue a task for the dispatch phase to start. */
|
|
23
|
+
enqueueForDispatch: (entry: {
|
|
24
|
+
id: string;
|
|
25
|
+
taskId: string;
|
|
26
|
+
environmentId?: string;
|
|
27
|
+
personaId?: string;
|
|
28
|
+
}) => void;
|
|
29
|
+
/** Emit a domain event. */
|
|
30
|
+
emit: (type: GrackleEventType, payload: Record<string, unknown>) => void;
|
|
31
|
+
/** Look up a persona by ID. */
|
|
32
|
+
getPersona: (id: string) => {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
runtime: string;
|
|
36
|
+
} | undefined;
|
|
37
|
+
/** Enable or disable a schedule, setting or clearing nextRunAt. */
|
|
38
|
+
setScheduleEnabled: (id: string, enabled: boolean, nextRunAt: string | null) => void;
|
|
39
|
+
/** Logger instance provided by the plugin context. */
|
|
40
|
+
logger: Pick<Logger, "debug" | "info" | "warn" | "error">;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a ReconciliationPhase that fires due schedules.
|
|
44
|
+
*
|
|
45
|
+
* @param deps - Injected dependencies
|
|
46
|
+
* @returns A phase to register with ReconciliationManager
|
|
47
|
+
*/
|
|
48
|
+
export declare function createCronPhase(deps: CronPhaseDeps): ReconciliationPhase;
|
|
49
|
+
//# sourceMappingURL=cron-phase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-phase.d.ts","sourceRoot":"","sources":["../src/cron-phase.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAGlE,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,eAAe,EAAE,MAAM,WAAW,EAAE,CAAC;IACrC,+EAA+E;IAC/E,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5E,2CAA2C;IAC3C,UAAU,EAAE,CACV,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,aAAa,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,OAAO,EACtB,gBAAgB,CAAC,EAAE,MAAM,KACtB,IAAI,CAAC;IACV,wCAAwC;IACxC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,sDAAsD;IACtD,kBAAkB,EAAE,CAAC,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChH,2BAA2B;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACzE,+BAA+B;IAC/B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACtF,mEAAmE;IAEnE,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACrF,sDAAsD;IACtD,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC3D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,mBAAmB,CAcxE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron reconciliation phase — fires due schedules on each tick.
|
|
3
|
+
*
|
|
4
|
+
* Creates tasks for due schedules and enqueues them for dispatch.
|
|
5
|
+
* The dispatch phase (separate reconciliation phase) handles starting
|
|
6
|
+
* sessions, respecting concurrency limits, and environment resolution.
|
|
7
|
+
*/
|
|
8
|
+
import { v4 as uuidv4 } from "uuid";
|
|
9
|
+
import { computeNextRunAt } from "./schedule-expression.js";
|
|
10
|
+
import { ROOT_TASK_ID } from "@grackle-ai/common";
|
|
11
|
+
/**
|
|
12
|
+
* Create a ReconciliationPhase that fires due schedules.
|
|
13
|
+
*
|
|
14
|
+
* @param deps - Injected dependencies
|
|
15
|
+
* @returns A phase to register with ReconciliationManager
|
|
16
|
+
*/
|
|
17
|
+
export function createCronPhase(deps) {
|
|
18
|
+
return {
|
|
19
|
+
name: "cron",
|
|
20
|
+
execute: async () => {
|
|
21
|
+
const due = deps.getDueSchedules();
|
|
22
|
+
if (due.length === 0) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
deps.logger.debug({ count: due.length }, "Cron phase: due schedules");
|
|
26
|
+
for (const schedule of due) {
|
|
27
|
+
fireSchedule(deps, schedule);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Fire a single schedule: create task, enqueue for dispatch, advance. */
|
|
33
|
+
function fireSchedule(deps, schedule) {
|
|
34
|
+
const now = new Date().toISOString();
|
|
35
|
+
let nextRunAt;
|
|
36
|
+
try {
|
|
37
|
+
// Anchor to the schedule's lastRunAt (not current time) to prevent drift
|
|
38
|
+
nextRunAt = computeNextRunAt(schedule.scheduleExpression, schedule.lastRunAt ?? undefined);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
deps.logger.error({ scheduleId: schedule.id, scheduleExpression: schedule.scheduleExpression, err }, "Cron phase: failed to compute nextRunAt; disabling schedule");
|
|
42
|
+
// Disable the schedule to prevent error loop on every tick
|
|
43
|
+
deps.setScheduleEnabled(schedule.id, false, null);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
// Validate persona exists
|
|
48
|
+
const persona = deps.getPersona(schedule.personaId);
|
|
49
|
+
if (!persona) {
|
|
50
|
+
deps.logger.warn({ scheduleId: schedule.id, personaId: schedule.personaId }, "Schedule fire skipped: persona not found");
|
|
51
|
+
deps.advanceSchedule(schedule.id, now, nextRunAt);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Create task
|
|
55
|
+
const taskId = uuidv4();
|
|
56
|
+
const taskTitle = `${schedule.title} @ ${now}`;
|
|
57
|
+
const parentTaskId = schedule.parentTaskId || ROOT_TASK_ID;
|
|
58
|
+
deps.createTask(taskId, schedule.workspaceId || undefined, taskTitle, schedule.description, [], // no dependencies
|
|
59
|
+
"", // no workspace slug
|
|
60
|
+
parentTaskId, false, // canDecompose
|
|
61
|
+
schedule.personaId);
|
|
62
|
+
deps.setTaskScheduleId(taskId, schedule.id);
|
|
63
|
+
// Enqueue for the dispatch phase to start (respects concurrency limits).
|
|
64
|
+
// Environment resolution is handled by the dispatch phase; we pass the
|
|
65
|
+
// schedule's preferred environmentId as a hint.
|
|
66
|
+
deps.enqueueForDispatch({
|
|
67
|
+
id: uuidv4(),
|
|
68
|
+
taskId,
|
|
69
|
+
environmentId: schedule.environmentId,
|
|
70
|
+
personaId: schedule.personaId,
|
|
71
|
+
});
|
|
72
|
+
// Advance schedule
|
|
73
|
+
deps.advanceSchedule(schedule.id, now, nextRunAt);
|
|
74
|
+
deps.emit("schedule.fired", {
|
|
75
|
+
scheduleId: schedule.id,
|
|
76
|
+
taskId,
|
|
77
|
+
firedAt: now,
|
|
78
|
+
});
|
|
79
|
+
deps.logger.info({ scheduleId: schedule.id, taskId, title: schedule.title }, "Schedule fired");
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
deps.logger.error({ scheduleId: schedule.id, err }, "Schedule fire failed with exception");
|
|
83
|
+
// Still advance to prevent retry storms
|
|
84
|
+
deps.advanceSchedule(schedule.id, now, nextRunAt);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=cron-phase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-phase.js","sourceRoot":"","sources":["../src/cron-phase.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createSchedulingPlugin } from "./scheduling-plugin.js";
|
|
2
|
+
export { createCronPhase } from "./cron-phase.js";
|
|
3
|
+
export type { CronPhaseDeps } from "./cron-phase.js";
|
|
4
|
+
export { validateExpression, computeNextRunAt, parseDuration, isIntervalExpression } from "./schedule-expression.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// ─── Plugin Entry Point ──────────────────────────────────────
|
|
2
|
+
export { createSchedulingPlugin } from "./scheduling-plugin.js";
|
|
3
|
+
// ─── Reconciliation Phase ────────────────────────────────────
|
|
4
|
+
export { createCronPhase } from "./cron-phase.js";
|
|
5
|
+
// ─── Expression Utilities ────────────────────────────────────
|
|
6
|
+
export { validateExpression, computeNextRunAt, parseDuration, isIntervalExpression } from "./schedule-expression.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,gEAAgE;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,gEAAgE;AAChE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schedule expression parsing and next-run-time computation.
|
|
3
|
+
*
|
|
4
|
+
* Supports two formats:
|
|
5
|
+
* - **Interval shorthand**: `"<number><unit>"` where unit is s/m/h/d (min 10s)
|
|
6
|
+
* - **Cron expressions**: Standard 5-field cron syntax via `cron-parser`
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Parse an interval shorthand expression to milliseconds.
|
|
10
|
+
*
|
|
11
|
+
* @param expr - e.g. "30s", "5m", "1h", "1d"
|
|
12
|
+
* @returns Duration in milliseconds
|
|
13
|
+
* @throws If the expression is invalid or below the minimum (10s)
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseDuration(expr: string): number;
|
|
16
|
+
/**
|
|
17
|
+
* Detect whether an expression is interval shorthand (vs. cron).
|
|
18
|
+
*
|
|
19
|
+
* @param expr - Schedule expression string
|
|
20
|
+
* @returns true if the expression matches interval shorthand format
|
|
21
|
+
*/
|
|
22
|
+
export declare function isIntervalExpression(expr: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Compute the next run time for a schedule expression.
|
|
25
|
+
*
|
|
26
|
+
* For intervals: if `lastRunAt + interval` is in the future, use that (prevents
|
|
27
|
+
* drift). Otherwise use `now + interval` (prevents burst-firing after downtime).
|
|
28
|
+
*
|
|
29
|
+
* For cron: next occurrence after now.
|
|
30
|
+
*
|
|
31
|
+
* @param expr - Schedule expression (interval or cron)
|
|
32
|
+
* @param lastRunAt - ISO timestamp of the last fire (undefined for first fire)
|
|
33
|
+
* @returns ISO timestamp of the next fire
|
|
34
|
+
*/
|
|
35
|
+
export declare function computeNextRunAt(expr: string, lastRunAt?: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Validate a schedule expression. Throws if invalid.
|
|
38
|
+
*
|
|
39
|
+
* @param expr - Schedule expression to validate
|
|
40
|
+
* @throws If the expression is neither valid interval shorthand nor valid cron
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateExpression(expr: string): void;
|
|
43
|
+
//# sourceMappingURL=schedule-expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-expression.d.ts","sourceRoot":"","sources":["../src/schedule-expression.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmBlD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAwBzE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAmBrD"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schedule expression parsing and next-run-time computation.
|
|
3
|
+
*
|
|
4
|
+
* Supports two formats:
|
|
5
|
+
* - **Interval shorthand**: `"<number><unit>"` where unit is s/m/h/d (min 10s)
|
|
6
|
+
* - **Cron expressions**: Standard 5-field cron syntax via `cron-parser`
|
|
7
|
+
*/
|
|
8
|
+
import cronParser from "cron-parser";
|
|
9
|
+
const INTERVAL_RE = /^(\d+)([smhd])$/;
|
|
10
|
+
const MINIMUM_INTERVAL_MS = 10_000; // 10 seconds
|
|
11
|
+
const UNIT_TO_MS = {
|
|
12
|
+
s: 1_000,
|
|
13
|
+
m: 60_000,
|
|
14
|
+
h: 3_600_000,
|
|
15
|
+
d: 86_400_000,
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Parse an interval shorthand expression to milliseconds.
|
|
19
|
+
*
|
|
20
|
+
* @param expr - e.g. "30s", "5m", "1h", "1d"
|
|
21
|
+
* @returns Duration in milliseconds
|
|
22
|
+
* @throws If the expression is invalid or below the minimum (10s)
|
|
23
|
+
*/
|
|
24
|
+
export function parseDuration(expr) {
|
|
25
|
+
const match = INTERVAL_RE.exec(expr);
|
|
26
|
+
if (!match) {
|
|
27
|
+
throw new Error(`Invalid interval expression: "${expr}". Expected format: <number><s|m|h|d> (e.g. "30s", "5m")`);
|
|
28
|
+
}
|
|
29
|
+
const value = parseInt(match[1], 10);
|
|
30
|
+
const unit = match[2];
|
|
31
|
+
const ms = value * UNIT_TO_MS[unit];
|
|
32
|
+
if (ms <= 0) {
|
|
33
|
+
throw new Error(`Interval must be positive: "${expr}"`);
|
|
34
|
+
}
|
|
35
|
+
if (ms < MINIMUM_INTERVAL_MS) {
|
|
36
|
+
throw new Error(`Interval "${expr}" (${ms}ms) is below the minimum of ${MINIMUM_INTERVAL_MS}ms (10s)`);
|
|
37
|
+
}
|
|
38
|
+
return ms;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Detect whether an expression is interval shorthand (vs. cron).
|
|
42
|
+
*
|
|
43
|
+
* @param expr - Schedule expression string
|
|
44
|
+
* @returns true if the expression matches interval shorthand format
|
|
45
|
+
*/
|
|
46
|
+
export function isIntervalExpression(expr) {
|
|
47
|
+
return INTERVAL_RE.test(expr);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compute the next run time for a schedule expression.
|
|
51
|
+
*
|
|
52
|
+
* For intervals: if `lastRunAt + interval` is in the future, use that (prevents
|
|
53
|
+
* drift). Otherwise use `now + interval` (prevents burst-firing after downtime).
|
|
54
|
+
*
|
|
55
|
+
* For cron: next occurrence after now.
|
|
56
|
+
*
|
|
57
|
+
* @param expr - Schedule expression (interval or cron)
|
|
58
|
+
* @param lastRunAt - ISO timestamp of the last fire (undefined for first fire)
|
|
59
|
+
* @returns ISO timestamp of the next fire
|
|
60
|
+
*/
|
|
61
|
+
export function computeNextRunAt(expr, lastRunAt) {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
if (isIntervalExpression(expr)) {
|
|
64
|
+
const intervalMs = parseDuration(expr);
|
|
65
|
+
const nowPlusInterval = now + intervalMs;
|
|
66
|
+
if (lastRunAt) {
|
|
67
|
+
const lastRunMs = new Date(lastRunAt).getTime();
|
|
68
|
+
const anchored = lastRunMs + intervalMs;
|
|
69
|
+
// Anchored: use lastRunAt + interval if it's in the future (prevents drift).
|
|
70
|
+
// If it's in the past (server was down), cap to now + interval (prevents burst).
|
|
71
|
+
if (anchored > now) {
|
|
72
|
+
return new Date(anchored).toISOString();
|
|
73
|
+
}
|
|
74
|
+
return new Date(nowPlusInterval).toISOString();
|
|
75
|
+
}
|
|
76
|
+
return new Date(nowPlusInterval).toISOString();
|
|
77
|
+
}
|
|
78
|
+
// Cron expression: compute next occurrence after now
|
|
79
|
+
const interval = cronParser.parseExpression(expr, { utc: true });
|
|
80
|
+
return interval.next().toISOString();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Validate a schedule expression. Throws if invalid.
|
|
84
|
+
*
|
|
85
|
+
* @param expr - Schedule expression to validate
|
|
86
|
+
* @throws If the expression is neither valid interval shorthand nor valid cron
|
|
87
|
+
*/
|
|
88
|
+
export function validateExpression(expr) {
|
|
89
|
+
if (!expr) {
|
|
90
|
+
throw new Error("Schedule expression cannot be empty");
|
|
91
|
+
}
|
|
92
|
+
if (isIntervalExpression(expr)) {
|
|
93
|
+
// parseDuration validates format and minimum
|
|
94
|
+
parseDuration(expr);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Try to parse as cron
|
|
98
|
+
try {
|
|
99
|
+
cronParser.parseExpression(expr, { utc: true });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw new Error(`Invalid schedule expression: "${expr}". Must be interval shorthand (e.g. "30s", "5m") or 5-field cron (e.g. "0 9 * * MON")`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=schedule-expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-expression.js","sourceRoot":"","sources":["../src/schedule-expression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schedule gRPC handler factory.
|
|
3
|
+
*
|
|
4
|
+
* Call `createScheduleHandlers(emit)` to obtain the 5 handler functions
|
|
5
|
+
* with `emit` captured via closure. This factory pattern allows the
|
|
6
|
+
* scheduling plugin to inject `ctx.emit` from `PluginContext` without
|
|
7
|
+
* depending on the singleton event bus.
|
|
8
|
+
*/
|
|
9
|
+
import { grackle } from "@grackle-ai/common";
|
|
10
|
+
import type { PluginContext } from "@grackle-ai/plugin-sdk";
|
|
11
|
+
/**
|
|
12
|
+
* Create the 5 schedule gRPC handler functions with `emit` injected.
|
|
13
|
+
*
|
|
14
|
+
* @param emit - Event emitter from the plugin context (`ctx.emit`)
|
|
15
|
+
* @returns Object containing all schedule handler methods
|
|
16
|
+
*/
|
|
17
|
+
export declare function createScheduleHandlers(emit: PluginContext["emit"]): {
|
|
18
|
+
createSchedule: (req: grackle.CreateScheduleRequest) => Promise<grackle.Schedule>;
|
|
19
|
+
listSchedules: (req: grackle.ListSchedulesRequest) => Promise<grackle.ScheduleList>;
|
|
20
|
+
getSchedule: (req: grackle.ScheduleId) => Promise<grackle.Schedule>;
|
|
21
|
+
updateSchedule: (req: grackle.UpdateScheduleRequest) => Promise<grackle.Schedule>;
|
|
22
|
+
deleteSchedule: (req: grackle.ScheduleId) => Promise<grackle.Empty>;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=schedule-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-handlers.d.ts","sourceRoot":"","sources":["../src/schedule-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI5D;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG;IACnE,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClF,aAAa,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACpF,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpE,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClF,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;CACrE,CAwIA"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schedule gRPC handler factory.
|
|
3
|
+
*
|
|
4
|
+
* Call `createScheduleHandlers(emit)` to obtain the 5 handler functions
|
|
5
|
+
* with `emit` captured via closure. This factory pattern allows the
|
|
6
|
+
* scheduling plugin to inject `ctx.emit` from `PluginContext` without
|
|
7
|
+
* depending on the singleton event bus.
|
|
8
|
+
*/
|
|
9
|
+
import { ConnectError, Code } from "@connectrpc/connect";
|
|
10
|
+
import { create } from "@bufbuild/protobuf";
|
|
11
|
+
import { grackle } from "@grackle-ai/common";
|
|
12
|
+
import { personaStore, scheduleStore } from "@grackle-ai/database";
|
|
13
|
+
import { v4 as uuid } from "uuid";
|
|
14
|
+
import { validateExpression, computeNextRunAt } from "./schedule-expression.js";
|
|
15
|
+
import { scheduleRowToProto } from "./schedule-proto.js";
|
|
16
|
+
/**
|
|
17
|
+
* Create the 5 schedule gRPC handler functions with `emit` injected.
|
|
18
|
+
*
|
|
19
|
+
* @param emit - Event emitter from the plugin context (`ctx.emit`)
|
|
20
|
+
* @returns Object containing all schedule handler methods
|
|
21
|
+
*/
|
|
22
|
+
export function createScheduleHandlers(emit) {
|
|
23
|
+
/** Create a new schedule. */
|
|
24
|
+
async function createSchedule(req) {
|
|
25
|
+
const title = req.title.trim();
|
|
26
|
+
const expr = req.scheduleExpression.trim();
|
|
27
|
+
const personaId = req.personaId.trim();
|
|
28
|
+
if (!title) {
|
|
29
|
+
throw new ConnectError("title is required", Code.InvalidArgument);
|
|
30
|
+
}
|
|
31
|
+
if (!expr) {
|
|
32
|
+
throw new ConnectError("schedule_expression is required", Code.InvalidArgument);
|
|
33
|
+
}
|
|
34
|
+
if (!personaId) {
|
|
35
|
+
throw new ConnectError("persona_id is required", Code.InvalidArgument);
|
|
36
|
+
}
|
|
37
|
+
// Validate persona exists
|
|
38
|
+
const persona = personaStore.getPersona(personaId);
|
|
39
|
+
if (!persona) {
|
|
40
|
+
throw new ConnectError(`Persona not found: ${personaId}`, Code.NotFound);
|
|
41
|
+
}
|
|
42
|
+
// Validate expression
|
|
43
|
+
try {
|
|
44
|
+
validateExpression(expr);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
throw new ConnectError(err instanceof Error ? err.message : "Invalid schedule expression", Code.InvalidArgument);
|
|
48
|
+
}
|
|
49
|
+
const id = uuid();
|
|
50
|
+
const nextRunAt = computeNextRunAt(expr);
|
|
51
|
+
scheduleStore.createSchedule(id, title, req.description, expr, personaId, req.environmentId, req.workspaceId, req.parentTaskId, nextRunAt);
|
|
52
|
+
emit("schedule.created", { scheduleId: id });
|
|
53
|
+
const row = scheduleStore.getSchedule(id);
|
|
54
|
+
return scheduleRowToProto(row);
|
|
55
|
+
}
|
|
56
|
+
/** List all schedules, optionally filtered by workspace. */
|
|
57
|
+
async function listSchedules(req) {
|
|
58
|
+
const rows = scheduleStore.listSchedules(req.workspaceId || undefined);
|
|
59
|
+
return create(grackle.ScheduleListSchema, {
|
|
60
|
+
schedules: rows.map(scheduleRowToProto),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/** Get a schedule by ID. */
|
|
64
|
+
async function getSchedule(req) {
|
|
65
|
+
const row = scheduleStore.getSchedule(req.id);
|
|
66
|
+
if (!row) {
|
|
67
|
+
throw new ConnectError(`Schedule not found: ${req.id}`, Code.NotFound);
|
|
68
|
+
}
|
|
69
|
+
return scheduleRowToProto(row);
|
|
70
|
+
}
|
|
71
|
+
/** Update an existing schedule. */
|
|
72
|
+
async function updateSchedule(req) {
|
|
73
|
+
const existing = scheduleStore.getSchedule(req.id);
|
|
74
|
+
if (!existing) {
|
|
75
|
+
throw new ConnectError(`Schedule not found: ${req.id}`, Code.NotFound);
|
|
76
|
+
}
|
|
77
|
+
const update = {};
|
|
78
|
+
if (req.title !== undefined && req.title.trim() !== "") {
|
|
79
|
+
update.title = req.title.trim();
|
|
80
|
+
}
|
|
81
|
+
if (req.description !== undefined) {
|
|
82
|
+
update.description = req.description;
|
|
83
|
+
}
|
|
84
|
+
if (req.personaId !== undefined && req.personaId.trim() !== "") {
|
|
85
|
+
const trimmedPersonaId = req.personaId.trim();
|
|
86
|
+
const persona = personaStore.getPersona(trimmedPersonaId);
|
|
87
|
+
if (!persona) {
|
|
88
|
+
throw new ConnectError(`Persona not found: ${trimmedPersonaId}`, Code.NotFound);
|
|
89
|
+
}
|
|
90
|
+
update.personaId = trimmedPersonaId;
|
|
91
|
+
}
|
|
92
|
+
if (req.environmentId !== undefined) {
|
|
93
|
+
update.environmentId = req.environmentId;
|
|
94
|
+
}
|
|
95
|
+
// Handle schedule expression change
|
|
96
|
+
let expressionChanged = false;
|
|
97
|
+
if (req.scheduleExpression !== undefined && req.scheduleExpression !== "") {
|
|
98
|
+
const expr = req.scheduleExpression.trim();
|
|
99
|
+
try {
|
|
100
|
+
validateExpression(expr);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throw new ConnectError(err instanceof Error ? err.message : "Invalid schedule expression", Code.InvalidArgument);
|
|
104
|
+
}
|
|
105
|
+
update.scheduleExpression = expr;
|
|
106
|
+
expressionChanged = true;
|
|
107
|
+
}
|
|
108
|
+
// Handle enable/disable
|
|
109
|
+
if (req.enabled !== undefined) {
|
|
110
|
+
update.enabled = req.enabled;
|
|
111
|
+
if (req.enabled) {
|
|
112
|
+
const expr = update.scheduleExpression ?? existing.scheduleExpression;
|
|
113
|
+
update.nextRunAt = computeNextRunAt(expr);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
update.nextRunAt = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (expressionChanged) {
|
|
120
|
+
// Recompute nextRunAt when expression changes (if currently enabled)
|
|
121
|
+
if (existing.enabled) {
|
|
122
|
+
update.nextRunAt = computeNextRunAt(update.scheduleExpression);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
scheduleStore.updateSchedule(req.id, update);
|
|
126
|
+
emit("schedule.updated", { scheduleId: req.id });
|
|
127
|
+
const row = scheduleStore.getSchedule(req.id);
|
|
128
|
+
return scheduleRowToProto(row);
|
|
129
|
+
}
|
|
130
|
+
/** Delete a schedule by ID. */
|
|
131
|
+
async function deleteSchedule(req) {
|
|
132
|
+
scheduleStore.deleteSchedule(req.id);
|
|
133
|
+
emit("schedule.deleted", { scheduleId: req.id });
|
|
134
|
+
return create(grackle.EmptySchema, {});
|
|
135
|
+
}
|
|
136
|
+
return { createSchedule, listSchedules, getSchedule, updateSchedule, deleteSchedule };
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=schedule-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-handlers.js","sourceRoot":"","sources":["../src/schedule-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proto converter for schedule database rows.
|
|
3
|
+
*/
|
|
4
|
+
import { grackle } from "@grackle-ai/common";
|
|
5
|
+
import type { scheduleStore } from "@grackle-ai/database";
|
|
6
|
+
/** Convert a schedule database row to its proto representation. */
|
|
7
|
+
export declare function scheduleRowToProto(row: scheduleStore.ScheduleRow): grackle.Schedule;
|
|
8
|
+
//# sourceMappingURL=schedule-proto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-proto.d.ts","sourceRoot":"","sources":["../src/schedule-proto.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,CAAC,WAAW,GAAG,OAAO,CAAC,QAAQ,CAiBnF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proto converter for schedule database rows.
|
|
3
|
+
*/
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import { grackle } from "@grackle-ai/common";
|
|
6
|
+
/** Convert a schedule database row to its proto representation. */
|
|
7
|
+
export function scheduleRowToProto(row) {
|
|
8
|
+
return create(grackle.ScheduleSchema, {
|
|
9
|
+
id: row.id,
|
|
10
|
+
title: row.title,
|
|
11
|
+
description: row.description,
|
|
12
|
+
scheduleExpression: row.scheduleExpression,
|
|
13
|
+
personaId: row.personaId,
|
|
14
|
+
environmentId: row.environmentId,
|
|
15
|
+
workspaceId: row.workspaceId,
|
|
16
|
+
parentTaskId: row.parentTaskId,
|
|
17
|
+
enabled: row.enabled,
|
|
18
|
+
lastRunAt: row.lastRunAt ?? "",
|
|
19
|
+
nextRunAt: row.nextRunAt ?? "",
|
|
20
|
+
runCount: row.runCount,
|
|
21
|
+
createdAt: row.createdAt,
|
|
22
|
+
updatedAt: row.updatedAt,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=schedule-proto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-proto.js","sourceRoot":"","sources":["../src/schedule-proto.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,mEAAmE;AACnE,MAAM,UAAU,kBAAkB,CAAC,GAA8B;IAC/D,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE;QACpC,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;QAC1C,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE;QAC9B,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE;QAC9B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,SAAS,EAAE,GAAG,CAAC,SAAS;KACzB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduling plugin — contributes schedule CRUD handlers and the cron
|
|
3
|
+
* reconciliation phase to the Grackle server.
|
|
4
|
+
*
|
|
5
|
+
* Declares `dependencies: ["core"]` so core handlers are registered first.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { GracklePlugin } from "@grackle-ai/plugin-sdk";
|
|
10
|
+
/**
|
|
11
|
+
* Create the scheduling plugin that contributes schedule CRUD and cron phase.
|
|
12
|
+
*
|
|
13
|
+
* @returns A GracklePlugin ready to pass to `loadPlugins()`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createSchedulingPlugin(): GracklePlugin;
|
|
16
|
+
//# sourceMappingURL=scheduling-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduling-plugin.d.ts","sourceRoot":"","sources":["../src/scheduling-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,wBAAwB,CAAC;AAQ3E;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,aAAa,CAwBtD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduling plugin — contributes schedule CRUD handlers and the cron
|
|
3
|
+
* reconciliation phase to the Grackle server.
|
|
4
|
+
*
|
|
5
|
+
* Declares `dependencies: ["core"]` so core handlers are registered first.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { grackle } from "@grackle-ai/common";
|
|
10
|
+
import { scheduleStore, taskStore, personaStore, dispatchQueueStore, } from "@grackle-ai/database";
|
|
11
|
+
import { createScheduleHandlers } from "./schedule-handlers.js";
|
|
12
|
+
import { createCronPhase } from "./cron-phase.js";
|
|
13
|
+
/**
|
|
14
|
+
* Create the scheduling plugin that contributes schedule CRUD and cron phase.
|
|
15
|
+
*
|
|
16
|
+
* @returns A GracklePlugin ready to pass to `loadPlugins()`.
|
|
17
|
+
*/
|
|
18
|
+
export function createSchedulingPlugin() {
|
|
19
|
+
return {
|
|
20
|
+
name: "scheduling",
|
|
21
|
+
dependencies: ["core"],
|
|
22
|
+
grpcHandlers: (ctx) => [{
|
|
23
|
+
service: grackle.Grackle,
|
|
24
|
+
handlers: createScheduleHandlers(ctx.emit),
|
|
25
|
+
}],
|
|
26
|
+
reconciliationPhases: (ctx) => [
|
|
27
|
+
createCronPhase({
|
|
28
|
+
getDueSchedules: scheduleStore.getDueSchedules,
|
|
29
|
+
advanceSchedule: scheduleStore.advanceSchedule,
|
|
30
|
+
createTask: taskStore.createTask,
|
|
31
|
+
setTaskScheduleId: taskStore.setTaskScheduleId,
|
|
32
|
+
enqueueForDispatch: dispatchQueueStore.enqueue,
|
|
33
|
+
emit: ctx.emit,
|
|
34
|
+
getPersona: personaStore.getPersona,
|
|
35
|
+
setScheduleEnabled: scheduleStore.setScheduleEnabled,
|
|
36
|
+
logger: ctx.logger,
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=scheduling-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduling-plugin.js","sourceRoot":"","sources":["../src/scheduling-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,GAC3D,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,YAAY,EAAE,CAAC,MAAM,CAAC;QAEtB,YAAY,EAAE,CAAC,GAAkB,EAAE,EAAE,CAAC,CAAC;gBACrC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,QAAQ,EAAE,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;aAC3C,CAAC;QAEF,oBAAoB,EAAE,CAAC,GAAkB,EAAE,EAAE,CAAC;YAC5C,eAAe,CAAC;gBACd,eAAe,EAAE,aAAa,CAAC,eAAe;gBAC9C,eAAe,EAAE,aAAa,CAAC,eAAe;gBAC9C,UAAU,EAAE,SAAS,CAAC,UAAU;gBAChC,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;gBAC9C,kBAAkB,EAAE,kBAAkB,CAAC,OAAO;gBAC9C,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,UAAU,EAAE,YAAY,CAAC,UAAU;gBACnC,kBAAkB,EAAE,aAAa,CAAC,kBAAkB;gBACpD,MAAM,EAAE,GAAG,CAAC,MAAM;aACnB,CAAC;SACH;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
+
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
+
{
|
|
4
|
+
"tsdocVersion": "0.12",
|
|
5
|
+
"toolPackages": [
|
|
6
|
+
{
|
|
7
|
+
"packageName": "@microsoft/api-extractor",
|
|
8
|
+
"packageVersion": "7.57.7"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grackle-ai/plugin-scheduling",
|
|
3
|
+
"version": "0.98.0",
|
|
4
|
+
"description": "Scheduling plugin for Grackle — schedule CRUD handlers, cron reconciliation phase, and expression parsing",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/nick-pape/grackle.git",
|
|
9
|
+
"directory": "packages/plugin-scheduling"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"grackle",
|
|
13
|
+
"plugin",
|
|
14
|
+
"scheduling",
|
|
15
|
+
"cron"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/nick-pape/grackle#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/nick-pape/grackle/issues"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.0.0 <24.0.0"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"types": "dist/index.d.ts",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@bufbuild/protobuf": "^2.5.0",
|
|
32
|
+
"@connectrpc/connect": "^2.0.0",
|
|
33
|
+
"cron-parser": "^4.9.0",
|
|
34
|
+
"pino": "^10.3.1",
|
|
35
|
+
"uuid": "^11.0.0",
|
|
36
|
+
"@grackle-ai/common": "0.98.0",
|
|
37
|
+
"@grackle-ai/core": "0.98.0",
|
|
38
|
+
"@grackle-ai/database": "0.98.0",
|
|
39
|
+
"@grackle-ai/plugin-sdk": "0.98.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@rushstack/heft": "1.2.7",
|
|
43
|
+
"@types/node": "^22.0.0",
|
|
44
|
+
"@types/uuid": "^10.0.0",
|
|
45
|
+
"vitest": "^3.2.1",
|
|
46
|
+
"@grackle-ai/heft-rig": "0.0.1"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "heft build --clean",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"clean": "heft clean",
|
|
52
|
+
"_phase:build": "heft run --only build -- --clean",
|
|
53
|
+
"_phase:test": "vitest run"
|
|
54
|
+
}
|
|
55
|
+
}
|