@agentick/scheduler 0.7.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 +183 -0
- package/dist/bridge.d.ts +4 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +8 -0
- package/dist/bridge.js.map +1 -0
- package/dist/cron-service.d.ts +22 -0
- package/dist/cron-service.d.ts.map +1 -0
- package/dist/cron-service.js +121 -0
- package/dist/cron-service.js.map +1 -0
- package/dist/heartbeat.d.ts +3 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +14 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/job-store.d.ts +19 -0
- package/dist/job-store.d.ts.map +1 -0
- package/dist/job-store.js +113 -0
- package/dist/job-store.js.map +1 -0
- package/dist/node-cron-backend.d.ts +12 -0
- package/dist/node-cron-backend.d.ts.map +1 -0
- package/dist/node-cron-backend.js +124 -0
- package/dist/node-cron-backend.js.map +1 -0
- package/dist/schedule-tool.d.ts +11 -0
- package/dist/schedule-tool.d.ts.map +1 -0
- package/dist/schedule-tool.jsx +101 -0
- package/dist/schedule-tool.jsx.map +1 -0
- package/dist/trigger-watcher.d.ts +20 -0
- package/dist/trigger-watcher.d.ts.map +1 -0
- package/dist/trigger-watcher.js +114 -0
- package/dist/trigger-watcher.js.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
- package/src/index.ts +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# @agentick/scheduler
|
|
2
|
+
|
|
3
|
+
Scheduled jobs for Agentick agents. Pluggable backends, file-based persistence,
|
|
4
|
+
crash recovery.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
pnpm add @agentick/scheduler
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { createClient } from "@agentick/client";
|
|
16
|
+
import { CronService, createScheduleTool, bindSchedulerStore } from "@agentick/scheduler";
|
|
17
|
+
|
|
18
|
+
const cronService = new CronService({
|
|
19
|
+
dataDir: ".myagent",
|
|
20
|
+
client,
|
|
21
|
+
defaultTarget: "tui",
|
|
22
|
+
});
|
|
23
|
+
await cronService.start();
|
|
24
|
+
|
|
25
|
+
// Give the agent a tool to manage its own schedule
|
|
26
|
+
const ScheduleTool = createScheduleTool(cronService.store);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
CronService
|
|
33
|
+
├── JobStore (persistent job definitions)
|
|
34
|
+
├── SchedulerBackend (pluggable — when to fire)
|
|
35
|
+
│ └── NodeCronBackend (default, in-process timers)
|
|
36
|
+
└── TriggerWatcher (optional) (external trigger pickup)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**JobStore** persists jobs as individual JSON files in `<dataDir>/jobs/`.
|
|
40
|
+
Survives crashes.
|
|
41
|
+
|
|
42
|
+
**SchedulerBackend** is the pluggable timer mechanism. When a job fires, it
|
|
43
|
+
calls the `onFire` callback provided by CronService, which dispatches to the
|
|
44
|
+
target session via `client.session(target).send()`.
|
|
45
|
+
|
|
46
|
+
**TriggerWatcher** watches `<dataDir>/triggers/` for externally-written JSON
|
|
47
|
+
files. System cron, scripts, webhooks — anything that writes a trigger file
|
|
48
|
+
wakes the agent. Enabled by default, opt out with `watchExternalTriggers: false`.
|
|
49
|
+
|
|
50
|
+
## Backend Interface
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface SchedulerBackend {
|
|
54
|
+
start(): Promise<void>;
|
|
55
|
+
stop(): Promise<void>;
|
|
56
|
+
schedule(job: Job, onFire: () => Promise<void>): void;
|
|
57
|
+
unschedule(jobId: string): void;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Four methods. `start`/`stop` for lifecycle. `schedule`/`unschedule` for per-job
|
|
62
|
+
timer management. The backend decides everything about HOW and WHEN — timers,
|
|
63
|
+
durability, retries. Dispatch is CronService's job.
|
|
64
|
+
|
|
65
|
+
### NodeCronBackend (default)
|
|
66
|
+
|
|
67
|
+
In-process scheduling via `node-cron`. Writes trigger files for crash recovery —
|
|
68
|
+
if the process dies between fire and dispatch, trigger files persist and are
|
|
69
|
+
drained on next `start()`.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { createNodeCronBackend } from "@agentick/scheduler";
|
|
73
|
+
|
|
74
|
+
const backend = createNodeCronBackend({ triggersDir: ".myagent/triggers" });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Custom Backends
|
|
78
|
+
|
|
79
|
+
Implement `SchedulerBackend` for any timer source. Durability is the
|
|
80
|
+
backend's problem — not the framework's.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// BullMQ example (conceptual)
|
|
84
|
+
const bullBackend: SchedulerBackend = {
|
|
85
|
+
async start() {
|
|
86
|
+
/* connect to Redis, start worker */
|
|
87
|
+
},
|
|
88
|
+
async stop() {
|
|
89
|
+
/* close connections */
|
|
90
|
+
},
|
|
91
|
+
schedule(job, onFire) {
|
|
92
|
+
/* create repeatable BullMQ job */
|
|
93
|
+
},
|
|
94
|
+
unschedule(jobId) {
|
|
95
|
+
/* remove BullMQ job scheduler */
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const service = new CronService({
|
|
100
|
+
dataDir: ".myagent",
|
|
101
|
+
client,
|
|
102
|
+
backend: bullBackend,
|
|
103
|
+
watchExternalTriggers: false,
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Agent Tool
|
|
108
|
+
|
|
109
|
+
`createScheduleTool` returns a tool the agent uses to manage its schedule.
|
|
110
|
+
Actions: `add`, `list`, `remove`, `enable`, `disable`.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { createScheduleTool } from "@agentick/scheduler";
|
|
114
|
+
|
|
115
|
+
const ScheduleTool = createScheduleTool(cronService.store);
|
|
116
|
+
|
|
117
|
+
// In your component tree:
|
|
118
|
+
<ScheduleTool />;
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The tool's `render` function puts active jobs in context so the model knows
|
|
122
|
+
what's already scheduled.
|
|
123
|
+
|
|
124
|
+
## Heartbeat
|
|
125
|
+
|
|
126
|
+
A heartbeat is a recurring job that reads a file and prompts the agent to act
|
|
127
|
+
on its contents. If the file is empty or missing, the trigger is skipped.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { createHeartbeatJob } from "@agentick/scheduler";
|
|
131
|
+
|
|
132
|
+
cronService.store.create({
|
|
133
|
+
...createHeartbeatJob({
|
|
134
|
+
cron: "*/5 * * * *",
|
|
135
|
+
target: "tui",
|
|
136
|
+
heartbeatFile: ".myagent/HEARTBEAT.md",
|
|
137
|
+
}),
|
|
138
|
+
id: "heartbeat",
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## External Triggers
|
|
143
|
+
|
|
144
|
+
Anything that writes a JSON file to the triggers directory wakes the agent.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
echo '{"target":"tui","prompt":"wake up","jobId":"manual","jobName":"manual","firedAt":"2025-01-01T00:00:00Z","oneshot":true}' \
|
|
148
|
+
> .myagent/triggers/$(date +%s)-manual.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## CronService Options
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
interface CronServiceOptions {
|
|
155
|
+
dataDir: string; // Root dir — jobs/ and triggers/ live here
|
|
156
|
+
client: AgentickClient; // Client for sending messages to sessions
|
|
157
|
+
backend?: SchedulerBackend; // Default: NodeCronBackend
|
|
158
|
+
watchExternalTriggers?: boolean; // Default: true
|
|
159
|
+
defaultTarget?: string; // Fallback session ID
|
|
160
|
+
onTriggerProcessed?: (trigger: Trigger) => void;
|
|
161
|
+
onError?: (error: Error, context: string) => void;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Exports
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
export { CronService } from "./cron-service.js";
|
|
169
|
+
export { JobStore } from "./job-store.js";
|
|
170
|
+
export { createNodeCronBackend } from "./node-cron-backend.js";
|
|
171
|
+
export { TriggerWatcher } from "./trigger-watcher.js";
|
|
172
|
+
export { createScheduleTool } from "./schedule-tool.js";
|
|
173
|
+
export { createHeartbeatJob } from "./heartbeat.js";
|
|
174
|
+
export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
|
|
175
|
+
export type {
|
|
176
|
+
Job,
|
|
177
|
+
Trigger,
|
|
178
|
+
SchedulerBackend,
|
|
179
|
+
CronServiceOptions,
|
|
180
|
+
HeartbeatOptions,
|
|
181
|
+
CreateJobInput,
|
|
182
|
+
} from "./types.js";
|
|
183
|
+
```
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAExD;AAED,wBAAgB,iBAAiB,IAAI,QAAQ,GAAG,IAAI,CAEnD"}
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAEA,IAAI,MAAM,GAAoB,IAAI,CAAC;AAEnC,MAAM,UAAU,kBAAkB,CAAC,KAAe;IAChD,MAAM,GAAG,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Job, CronServiceOptions, HeartbeatOptions } from "./types.js";
|
|
2
|
+
import { JobStore } from "./job-store.js";
|
|
3
|
+
import { TriggerWatcher } from "./trigger-watcher.js";
|
|
4
|
+
import type { SchedulerBackend } from "./types.js";
|
|
5
|
+
export declare class CronService {
|
|
6
|
+
readonly store: JobStore;
|
|
7
|
+
readonly backend: SchedulerBackend;
|
|
8
|
+
readonly watcher: TriggerWatcher | null;
|
|
9
|
+
private readonly client;
|
|
10
|
+
private readonly defaultTarget;
|
|
11
|
+
private readonly onProcessed;
|
|
12
|
+
private readonly onError;
|
|
13
|
+
private scheduledIds;
|
|
14
|
+
constructor(options: CronServiceOptions);
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
17
|
+
/** Ensure a heartbeat job exists — idempotent */
|
|
18
|
+
ensureHeartbeat(options?: HeartbeatOptions): Job;
|
|
19
|
+
private _onStoreChange;
|
|
20
|
+
private _dispatch;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=cron-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-service.d.ts","sourceRoot":"","sources":["../src/cron-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,WAAW;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,YAAY,CAAqB;gBAE7B,OAAO,EAAE,kBAAkB;IAuBjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IActB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,iDAAiD;IACjD,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,GAAG;IAShD,OAAO,CAAC,cAAc,CAcpB;YAEY,SAAS;CAyDxB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { JobStore } from "./job-store.js";
|
|
3
|
+
import { TriggerWatcher } from "./trigger-watcher.js";
|
|
4
|
+
import { createNodeCronBackend } from "./node-cron-backend.js";
|
|
5
|
+
import { createHeartbeatJob } from "./heartbeat.js";
|
|
6
|
+
export class CronService {
|
|
7
|
+
store;
|
|
8
|
+
backend;
|
|
9
|
+
watcher;
|
|
10
|
+
client;
|
|
11
|
+
defaultTarget;
|
|
12
|
+
onProcessed;
|
|
13
|
+
onError;
|
|
14
|
+
scheduledIds = new Set();
|
|
15
|
+
constructor(options) {
|
|
16
|
+
const jobsDir = join(options.dataDir, "jobs");
|
|
17
|
+
const triggersDir = join(options.dataDir, "triggers");
|
|
18
|
+
this.client = options.client;
|
|
19
|
+
this.defaultTarget = options.defaultTarget;
|
|
20
|
+
this.onProcessed = options.onTriggerProcessed;
|
|
21
|
+
this.onError = options.onError;
|
|
22
|
+
this.store = new JobStore(jobsDir);
|
|
23
|
+
this.backend = options.backend ?? createNodeCronBackend({ triggersDir });
|
|
24
|
+
// External trigger watcher — enabled by default, opt out with watchExternalTriggers: false
|
|
25
|
+
const watchExternal = options.watchExternalTriggers ?? true;
|
|
26
|
+
this.watcher = watchExternal
|
|
27
|
+
? new TriggerWatcher(triggersDir, options.client, this.store, {
|
|
28
|
+
defaultTarget: options.defaultTarget,
|
|
29
|
+
onTriggerProcessed: options.onTriggerProcessed,
|
|
30
|
+
onError: options.onError,
|
|
31
|
+
})
|
|
32
|
+
: null;
|
|
33
|
+
}
|
|
34
|
+
async start() {
|
|
35
|
+
// Register all enabled jobs with the backend
|
|
36
|
+
for (const job of this.store.listEnabled()) {
|
|
37
|
+
this.backend.schedule(job, () => this._dispatch(job));
|
|
38
|
+
this.scheduledIds.add(job.id);
|
|
39
|
+
}
|
|
40
|
+
// React to store changes
|
|
41
|
+
this.store.on("change", this._onStoreChange);
|
|
42
|
+
await this.backend.start();
|
|
43
|
+
await this.watcher?.start();
|
|
44
|
+
}
|
|
45
|
+
async stop() {
|
|
46
|
+
this.store.off("change", this._onStoreChange);
|
|
47
|
+
await this.backend.stop();
|
|
48
|
+
this.watcher?.stop();
|
|
49
|
+
}
|
|
50
|
+
/** Ensure a heartbeat job exists — idempotent */
|
|
51
|
+
ensureHeartbeat(options) {
|
|
52
|
+
const existing = this.store.get("heartbeat");
|
|
53
|
+
if (existing)
|
|
54
|
+
return existing;
|
|
55
|
+
return this.store.create({
|
|
56
|
+
...createHeartbeatJob(options),
|
|
57
|
+
id: "heartbeat",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
_onStoreChange = () => {
|
|
61
|
+
const enabled = new Map(this.store.listEnabled().map((j) => [j.id, j]));
|
|
62
|
+
// Unschedule everything currently tracked
|
|
63
|
+
for (const id of this.scheduledIds) {
|
|
64
|
+
this.backend.unschedule(id);
|
|
65
|
+
}
|
|
66
|
+
this.scheduledIds.clear();
|
|
67
|
+
// Re-schedule enabled jobs
|
|
68
|
+
for (const [, job] of enabled) {
|
|
69
|
+
this.backend.schedule(job, () => this._dispatch(job));
|
|
70
|
+
this.scheduledIds.add(job.id);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
async _dispatch(job) {
|
|
74
|
+
// Read fresh state — job may have been updated since scheduling
|
|
75
|
+
const current = this.store.get(job.id);
|
|
76
|
+
if (!current || !current.enabled)
|
|
77
|
+
return;
|
|
78
|
+
const target = current.target || this.defaultTarget;
|
|
79
|
+
if (!target) {
|
|
80
|
+
this.onError?.(new Error(`Job "${current.id}" has no target and no default configured`), "dispatch");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const session = this.client.session(target);
|
|
85
|
+
const handle = session.send({
|
|
86
|
+
messages: [
|
|
87
|
+
{
|
|
88
|
+
role: "event",
|
|
89
|
+
content: [{ type: "text", text: current.prompt }],
|
|
90
|
+
metadata: {
|
|
91
|
+
source: { type: "cron" },
|
|
92
|
+
event_type: "cron_trigger",
|
|
93
|
+
job_id: current.id,
|
|
94
|
+
job_name: current.name,
|
|
95
|
+
fired_at: new Date().toISOString(),
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
await handle.result;
|
|
101
|
+
// Update lastFiredAt
|
|
102
|
+
this.store.update(current.id, { lastFiredAt: new Date().toISOString() });
|
|
103
|
+
// Oneshot: delete after successful fire
|
|
104
|
+
if (current.oneshot) {
|
|
105
|
+
this.store.delete(current.id);
|
|
106
|
+
}
|
|
107
|
+
this.onProcessed?.({
|
|
108
|
+
jobId: current.id,
|
|
109
|
+
jobName: current.name,
|
|
110
|
+
target,
|
|
111
|
+
prompt: current.prompt,
|
|
112
|
+
firedAt: new Date().toISOString(),
|
|
113
|
+
oneshot: current.oneshot,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.onError?.(error instanceof Error ? error : new Error(String(error)), `dispatch:${current.id}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=cron-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-service.js","sourceRoot":"","sources":["../src/cron-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnE,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,cAAc,CAa1E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function createHeartbeatJob(opts) {
|
|
2
|
+
return {
|
|
3
|
+
name: "heartbeat",
|
|
4
|
+
cron: opts?.cron ?? "*/5 * * * *",
|
|
5
|
+
target: opts?.target ?? "tui",
|
|
6
|
+
prompt: "[heartbeat] Review your heartbeat file and act on any due, prioritized, or in-progress work.",
|
|
7
|
+
oneshot: false,
|
|
8
|
+
enabled: true,
|
|
9
|
+
metadata: {
|
|
10
|
+
heartbeatFile: opts?.heartbeatFile ?? ".tentickle/HEARTBEAT.md",
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,IAAuB;IACxD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,aAAa;QACjC,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK;QAC7B,MAAM,EACJ,8FAA8F;QAChG,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE;YACR,aAAa,EAAE,IAAI,EAAE,aAAa,IAAI,yBAAyB;SAChE;KACF,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { CronService } from "./cron-service.js";
|
|
2
|
+
export { JobStore } from "./job-store.js";
|
|
3
|
+
export { createNodeCronBackend } from "./node-cron-backend.js";
|
|
4
|
+
export { TriggerWatcher } from "./trigger-watcher.js";
|
|
5
|
+
export { createScheduleTool } from "./schedule-tool.js";
|
|
6
|
+
export { createHeartbeatJob } from "./heartbeat.js";
|
|
7
|
+
export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
|
|
8
|
+
export type { Job, Trigger, SchedulerBackend, CronServiceOptions, HeartbeatOptions, CreateJobInput, } from "./types.js";
|
|
9
|
+
export type { NodeCronBackendOptions } from "./node-cron-backend.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EACV,GAAG,EACH,OAAO,EACP,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { CronService } from "./cron-service.js";
|
|
2
|
+
export { JobStore } from "./job-store.js";
|
|
3
|
+
export { createNodeCronBackend } from "./node-cron-backend.js";
|
|
4
|
+
export { TriggerWatcher } from "./trigger-watcher.js";
|
|
5
|
+
export { createScheduleTool } from "./schedule-tool.js";
|
|
6
|
+
export { createHeartbeatJob } from "./heartbeat.js";
|
|
7
|
+
export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { Job, CreateJobInput } from "./types.js";
|
|
3
|
+
export declare class JobStore extends EventEmitter {
|
|
4
|
+
private jobs;
|
|
5
|
+
private readonly jobsDir;
|
|
6
|
+
constructor(jobsDir: string);
|
|
7
|
+
list(): Job[];
|
|
8
|
+
listEnabled(): Job[];
|
|
9
|
+
get(id: string): Job | null;
|
|
10
|
+
create(input: CreateJobInput): Job;
|
|
11
|
+
update(id: string, updates: Partial<Pick<Job, "name" | "cron" | "target" | "prompt" | "oneshot" | "enabled" | "lastFiredAt" | "metadata">>): Job | null;
|
|
12
|
+
delete(id: string): boolean;
|
|
13
|
+
private _generateId;
|
|
14
|
+
private _nanoid;
|
|
15
|
+
private _load;
|
|
16
|
+
private _save;
|
|
17
|
+
private _remove;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=job-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-store.d.ts","sourceRoot":"","sources":["../src/job-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C,OAAO,KAAK,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAUtD,qBAAa,QAAS,SAAQ,YAAY;IACxC,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,MAAM;IAO3B,IAAI,IAAI,GAAG,EAAE;IAIb,WAAW,IAAI,GAAG,EAAE;IAIpB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAI3B,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,GAAG;IAiBlC,MAAM,CACJ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,CACd,IAAI,CACF,GAAG,EACH,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,CAC3F,CACF,GACA,GAAG,GAAG,IAAI;IASb,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAS3B,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,KAAK;IAcb,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,OAAO;CAQhB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync, existsSync, } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
function slugify(name) {
|
|
5
|
+
return name
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
8
|
+
.replace(/^-|-$/g, "")
|
|
9
|
+
.slice(0, 48);
|
|
10
|
+
}
|
|
11
|
+
export class JobStore extends EventEmitter {
|
|
12
|
+
jobs = new Map();
|
|
13
|
+
jobsDir;
|
|
14
|
+
constructor(jobsDir) {
|
|
15
|
+
super();
|
|
16
|
+
this.jobsDir = jobsDir;
|
|
17
|
+
mkdirSync(jobsDir, { recursive: true });
|
|
18
|
+
this._load();
|
|
19
|
+
}
|
|
20
|
+
list() {
|
|
21
|
+
return [...this.jobs.values()];
|
|
22
|
+
}
|
|
23
|
+
listEnabled() {
|
|
24
|
+
return this.list().filter((j) => j.enabled);
|
|
25
|
+
}
|
|
26
|
+
get(id) {
|
|
27
|
+
return this.jobs.get(id) ?? null;
|
|
28
|
+
}
|
|
29
|
+
create(input) {
|
|
30
|
+
const id = input.id ?? this._generateId(input.name);
|
|
31
|
+
if (this.jobs.has(id)) {
|
|
32
|
+
throw new Error(`Job "${id}" already exists`);
|
|
33
|
+
}
|
|
34
|
+
const job = {
|
|
35
|
+
...input,
|
|
36
|
+
id,
|
|
37
|
+
createdAt: new Date().toISOString(),
|
|
38
|
+
};
|
|
39
|
+
this.jobs.set(id, job);
|
|
40
|
+
this._save(job);
|
|
41
|
+
this.emit("change");
|
|
42
|
+
return job;
|
|
43
|
+
}
|
|
44
|
+
update(id, updates) {
|
|
45
|
+
const job = this.jobs.get(id);
|
|
46
|
+
if (!job)
|
|
47
|
+
return null;
|
|
48
|
+
Object.assign(job, updates);
|
|
49
|
+
this._save(job);
|
|
50
|
+
this.emit("change");
|
|
51
|
+
return job;
|
|
52
|
+
}
|
|
53
|
+
delete(id) {
|
|
54
|
+
const existed = this.jobs.delete(id);
|
|
55
|
+
if (existed) {
|
|
56
|
+
this._remove(id);
|
|
57
|
+
this.emit("change");
|
|
58
|
+
}
|
|
59
|
+
return existed;
|
|
60
|
+
}
|
|
61
|
+
_generateId(name) {
|
|
62
|
+
const base = slugify(name);
|
|
63
|
+
if (!base)
|
|
64
|
+
return this._nanoid();
|
|
65
|
+
if (!this.jobs.has(base))
|
|
66
|
+
return base;
|
|
67
|
+
for (let i = 2; i < 1000; i++) {
|
|
68
|
+
const candidate = `${base}-${i}`;
|
|
69
|
+
if (!this.jobs.has(candidate))
|
|
70
|
+
return candidate;
|
|
71
|
+
}
|
|
72
|
+
return `${base}-${this._nanoid()}`;
|
|
73
|
+
}
|
|
74
|
+
_nanoid() {
|
|
75
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
76
|
+
let id = "";
|
|
77
|
+
const bytes = new Uint8Array(8);
|
|
78
|
+
crypto.getRandomValues(bytes);
|
|
79
|
+
for (const b of bytes)
|
|
80
|
+
id += chars[b % chars.length];
|
|
81
|
+
return id;
|
|
82
|
+
}
|
|
83
|
+
_load() {
|
|
84
|
+
if (!existsSync(this.jobsDir))
|
|
85
|
+
return;
|
|
86
|
+
for (const file of readdirSync(this.jobsDir)) {
|
|
87
|
+
if (!file.endsWith(".json"))
|
|
88
|
+
continue;
|
|
89
|
+
try {
|
|
90
|
+
const raw = readFileSync(join(this.jobsDir, file), "utf-8");
|
|
91
|
+
const job = JSON.parse(raw);
|
|
92
|
+
if (job.id)
|
|
93
|
+
this.jobs.set(job.id, job);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Skip malformed files
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
_save(job) {
|
|
101
|
+
writeFileSync(join(this.jobsDir, `${job.id}.json`), JSON.stringify(job, null, 2) + "\n");
|
|
102
|
+
}
|
|
103
|
+
_remove(id) {
|
|
104
|
+
const path = join(this.jobsDir, `${id}.json`);
|
|
105
|
+
try {
|
|
106
|
+
unlinkSync(path);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Already gone
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=job-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../src/job-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SchedulerBackend } from "./types.js";
|
|
2
|
+
export interface NodeCronBackendOptions {
|
|
3
|
+
/** Directory for trigger files (crash recovery). If omitted, no trigger files — fires directly. */
|
|
4
|
+
triggersDir?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* In-process scheduling via node-cron. Optionally writes trigger files for
|
|
8
|
+
* crash recovery — if the process dies between fire and dispatch, trigger
|
|
9
|
+
* files persist and are drained on next `start()`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createNodeCronBackend(options?: NodeCronBackendOptions): SchedulerBackend;
|
|
12
|
+
//# sourceMappingURL=node-cron-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-cron-backend.d.ts","sourceRoot":"","sources":["../src/node-cron-backend.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,sBAAsB;IACrC,mGAAmG;IACnG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,gBAAgB,CAgDxF"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import cron, {} from "node-cron";
|
|
2
|
+
import { writeFileSync, readFileSync, readdirSync, unlinkSync, existsSync, mkdirSync, } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* In-process scheduling via node-cron. Optionally writes trigger files for
|
|
6
|
+
* crash recovery — if the process dies between fire and dispatch, trigger
|
|
7
|
+
* files persist and are drained on next `start()`.
|
|
8
|
+
*/
|
|
9
|
+
export function createNodeCronBackend(options) {
|
|
10
|
+
const timers = new Map();
|
|
11
|
+
const triggersDir = options?.triggersDir;
|
|
12
|
+
const pendingCallbacks = new Map();
|
|
13
|
+
if (triggersDir) {
|
|
14
|
+
mkdirSync(triggersDir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
async start() {
|
|
18
|
+
if (triggersDir) {
|
|
19
|
+
await drainPendingTriggers(triggersDir, pendingCallbacks);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
async stop() {
|
|
23
|
+
for (const [id, entry] of timers) {
|
|
24
|
+
entry.task.stop();
|
|
25
|
+
timers.delete(id);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
schedule(job, onFire) {
|
|
29
|
+
if (!cron.validate(job.cron))
|
|
30
|
+
return;
|
|
31
|
+
// Store callback for crash recovery drain
|
|
32
|
+
pendingCallbacks.set(job.id, onFire);
|
|
33
|
+
const wrappedFire = triggersDir
|
|
34
|
+
? () => fireWithTriggerFile(triggersDir, job, onFire)
|
|
35
|
+
: () => {
|
|
36
|
+
onFire();
|
|
37
|
+
};
|
|
38
|
+
const task = cron.schedule(job.cron, wrappedFire);
|
|
39
|
+
timers.set(job.id, { task, cronExpr: job.cron, onFire });
|
|
40
|
+
},
|
|
41
|
+
unschedule(jobId) {
|
|
42
|
+
const entry = timers.get(jobId);
|
|
43
|
+
if (entry) {
|
|
44
|
+
entry.task.stop();
|
|
45
|
+
timers.delete(jobId);
|
|
46
|
+
}
|
|
47
|
+
pendingCallbacks.delete(jobId);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Write trigger file, call onFire, delete file on success. */
|
|
52
|
+
function fireWithTriggerFile(triggersDir, job, onFire) {
|
|
53
|
+
// Heartbeat pre-filter: read heartbeat file, skip if empty/missing
|
|
54
|
+
let prompt = job.prompt;
|
|
55
|
+
const heartbeatFile = job.metadata?.heartbeatFile;
|
|
56
|
+
if (heartbeatFile) {
|
|
57
|
+
try {
|
|
58
|
+
if (!existsSync(heartbeatFile))
|
|
59
|
+
return;
|
|
60
|
+
const contents = readFileSync(heartbeatFile, "utf-8").trim();
|
|
61
|
+
if (!contents)
|
|
62
|
+
return;
|
|
63
|
+
prompt = `${prompt}\n\n---\n\n${contents}`;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const now = new Date();
|
|
70
|
+
const trigger = {
|
|
71
|
+
jobId: job.id,
|
|
72
|
+
jobName: job.name,
|
|
73
|
+
target: job.target,
|
|
74
|
+
prompt,
|
|
75
|
+
firedAt: now.toISOString(),
|
|
76
|
+
oneshot: job.oneshot,
|
|
77
|
+
};
|
|
78
|
+
const filename = `${now.getTime()}-${job.id}.json`;
|
|
79
|
+
const filepath = join(triggersDir, filename);
|
|
80
|
+
writeFileSync(filepath, JSON.stringify(trigger, null, 2) + "\n");
|
|
81
|
+
// Fire callback, clean up trigger file on success
|
|
82
|
+
onFire()
|
|
83
|
+
.then(() => {
|
|
84
|
+
try {
|
|
85
|
+
unlinkSync(filepath);
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
})
|
|
89
|
+
.catch(() => {
|
|
90
|
+
// Trigger file persists for retry on next start
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** Drain trigger files that survived a crash. */
|
|
94
|
+
async function drainPendingTriggers(triggersDir, callbacks) {
|
|
95
|
+
let files;
|
|
96
|
+
try {
|
|
97
|
+
files = readdirSync(triggersDir)
|
|
98
|
+
.filter((f) => f.endsWith(".json"))
|
|
99
|
+
.sort();
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
const filepath = join(triggersDir, file);
|
|
106
|
+
try {
|
|
107
|
+
const raw = readFileSync(filepath, "utf-8");
|
|
108
|
+
const trigger = JSON.parse(raw);
|
|
109
|
+
const callback = callbacks.get(trigger.jobId);
|
|
110
|
+
if (callback) {
|
|
111
|
+
await callback();
|
|
112
|
+
}
|
|
113
|
+
unlinkSync(filepath);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Skip malformed trigger files
|
|
117
|
+
try {
|
|
118
|
+
unlinkSync(filepath);
|
|
119
|
+
}
|
|
120
|
+
catch { }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=node-cron-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-cron-backend.js","sourceRoot":"","sources":["../src/node-cron-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { JobStore } from "./job-store.js";
|
|
2
|
+
export declare function createScheduleTool(store: JobStore): import("@agentick/core/tool").RunnableToolClass<{
|
|
3
|
+
action: "add" | "list" | "remove" | "enable" | "disable";
|
|
4
|
+
name?: string | undefined;
|
|
5
|
+
cron?: string | undefined;
|
|
6
|
+
target?: string | undefined;
|
|
7
|
+
prompt?: string | undefined;
|
|
8
|
+
oneshot?: boolean | undefined;
|
|
9
|
+
id?: string | undefined;
|
|
10
|
+
}>;
|
|
11
|
+
//# sourceMappingURL=schedule-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-tool.d.ts","sourceRoot":"","sources":["../src/schedule-tool.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAe/C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ;;;;;;;;GAwFjD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import cron from "node-cron";
|
|
3
|
+
import { createTool, Section } from "@agentick/core";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
function formatJob(j) {
|
|
6
|
+
const flags = [j.oneshot ? "oneshot" : null, !j.enabled ? "disabled" : null].filter(Boolean);
|
|
7
|
+
const suffix = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
|
|
8
|
+
return `${j.id}: "${j.name}" (${j.cron}) → ${j.target}${suffix}`;
|
|
9
|
+
}
|
|
10
|
+
export function createScheduleTool(store) {
|
|
11
|
+
return createTool({
|
|
12
|
+
name: "schedule",
|
|
13
|
+
description: "Manage scheduled jobs. 'add' to create a recurring/oneshot job, " +
|
|
14
|
+
"'list' to see all jobs, 'remove' to delete, 'enable'/'disable' to toggle.",
|
|
15
|
+
displaySummary: (input) => {
|
|
16
|
+
if (input.action === "add")
|
|
17
|
+
return `add: ${input.name ?? "unnamed"}`;
|
|
18
|
+
if (input.action === "list")
|
|
19
|
+
return "list";
|
|
20
|
+
return `${input.action}: ${input.id}`;
|
|
21
|
+
},
|
|
22
|
+
input: z.object({
|
|
23
|
+
action: z.enum(["add", "list", "remove", "enable", "disable"]),
|
|
24
|
+
name: z.string().optional().describe("Job name (add)"),
|
|
25
|
+
cron: z.string().optional().describe("Cron expression, e.g. '*/5 * * * *' (add)"),
|
|
26
|
+
target: z.string().optional().describe("Target session ID, e.g. 'tui', 'telegram' (add)"),
|
|
27
|
+
prompt: z.string().optional().describe("Prompt sent when job fires (add)"),
|
|
28
|
+
oneshot: z.boolean().optional().describe("Delete after first fire (add, default false)"),
|
|
29
|
+
id: z.string().optional().describe("Job ID (remove/enable/disable)"),
|
|
30
|
+
}),
|
|
31
|
+
handler: (input) => {
|
|
32
|
+
switch (input.action) {
|
|
33
|
+
case "add": {
|
|
34
|
+
if (!input.name || !input.cron || !input.prompt) {
|
|
35
|
+
return [
|
|
36
|
+
{ type: "text", text: "Error: 'add' requires name, cron, and prompt." },
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
if (!cron.validate(input.cron)) {
|
|
40
|
+
return [
|
|
41
|
+
{ type: "text", text: `Error: invalid cron expression "${input.cron}".` },
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
const job = store.create({
|
|
45
|
+
name: input.name,
|
|
46
|
+
cron: input.cron,
|
|
47
|
+
target: input.target ?? "tui",
|
|
48
|
+
prompt: input.prompt,
|
|
49
|
+
oneshot: input.oneshot ?? false,
|
|
50
|
+
enabled: true,
|
|
51
|
+
});
|
|
52
|
+
return [{ type: "text", text: `Created job: ${formatJob(job)}` }];
|
|
53
|
+
}
|
|
54
|
+
case "list": {
|
|
55
|
+
const jobs = store.list();
|
|
56
|
+
if (jobs.length === 0) {
|
|
57
|
+
return [{ type: "text", text: "No scheduled jobs." }];
|
|
58
|
+
}
|
|
59
|
+
return [{ type: "text", text: jobs.map(formatJob).join("\n") }];
|
|
60
|
+
}
|
|
61
|
+
case "remove": {
|
|
62
|
+
if (!input.id) {
|
|
63
|
+
return [{ type: "text", text: "Error: 'remove' requires an id." }];
|
|
64
|
+
}
|
|
65
|
+
const deleted = store.delete(input.id);
|
|
66
|
+
if (!deleted) {
|
|
67
|
+
return [{ type: "text", text: `Error: job "${input.id}" not found.` }];
|
|
68
|
+
}
|
|
69
|
+
return [{ type: "text", text: `Removed job "${input.id}".` }];
|
|
70
|
+
}
|
|
71
|
+
case "enable": {
|
|
72
|
+
if (!input.id) {
|
|
73
|
+
return [{ type: "text", text: "Error: 'enable' requires an id." }];
|
|
74
|
+
}
|
|
75
|
+
const job = store.update(input.id, { enabled: true });
|
|
76
|
+
if (!job) {
|
|
77
|
+
return [{ type: "text", text: `Error: job "${input.id}" not found.` }];
|
|
78
|
+
}
|
|
79
|
+
return [{ type: "text", text: `Enabled job "${input.id}".` }];
|
|
80
|
+
}
|
|
81
|
+
case "disable": {
|
|
82
|
+
if (!input.id) {
|
|
83
|
+
return [{ type: "text", text: "Error: 'disable' requires an id." }];
|
|
84
|
+
}
|
|
85
|
+
const job = store.update(input.id, { enabled: false });
|
|
86
|
+
if (!job) {
|
|
87
|
+
return [{ type: "text", text: `Error: job "${input.id}" not found.` }];
|
|
88
|
+
}
|
|
89
|
+
return [{ type: "text", text: `Disabled job "${input.id}".` }];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
render: () => {
|
|
94
|
+
const jobs = store.listEnabled();
|
|
95
|
+
if (jobs.length === 0)
|
|
96
|
+
return null;
|
|
97
|
+
return <Section id="scheduled-jobs">{jobs.map(formatJob).join("\n")}</Section>;
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=schedule-tool.jsx.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-tool.jsx","sourceRoot":"","sources":["../src/schedule-tool.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentickClient } from "@agentick/client";
|
|
2
|
+
import type { CronServiceOptions } from "./types.js";
|
|
3
|
+
import type { JobStore } from "./job-store.js";
|
|
4
|
+
export declare class TriggerWatcher {
|
|
5
|
+
private watcher;
|
|
6
|
+
private readonly triggersDir;
|
|
7
|
+
private readonly client;
|
|
8
|
+
private readonly store;
|
|
9
|
+
private readonly defaultTarget;
|
|
10
|
+
private readonly onProcessed;
|
|
11
|
+
private readonly onError;
|
|
12
|
+
private processing;
|
|
13
|
+
private stopped;
|
|
14
|
+
constructor(triggersDir: string, client: AgentickClient, store: JobStore, options?: Pick<CronServiceOptions, "defaultTarget" | "onTriggerProcessed" | "onError">);
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
stop(): void;
|
|
17
|
+
private _drainPending;
|
|
18
|
+
private _processFile;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=trigger-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-watcher.d.ts","sourceRoot":"","sources":["../src/trigger-watcher.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAW,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwD;IAChF,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,OAAO,CAAS;gBAGtB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,eAAe,GAAG,oBAAoB,GAAG,SAAS,CAAC;IAWlF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,IAAI,IAAI,IAAI;YAQE,aAAa;YAgBb,YAAY;CAiE3B"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { watch, readdirSync, readFileSync, unlinkSync, existsSync, mkdirSync, } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export class TriggerWatcher {
|
|
4
|
+
watcher = null;
|
|
5
|
+
triggersDir;
|
|
6
|
+
client;
|
|
7
|
+
store;
|
|
8
|
+
defaultTarget;
|
|
9
|
+
onProcessed;
|
|
10
|
+
onError;
|
|
11
|
+
processing = new Set();
|
|
12
|
+
stopped = false;
|
|
13
|
+
constructor(triggersDir, client, store, options) {
|
|
14
|
+
this.triggersDir = triggersDir;
|
|
15
|
+
this.client = client;
|
|
16
|
+
this.store = store;
|
|
17
|
+
this.defaultTarget = options?.defaultTarget;
|
|
18
|
+
this.onProcessed = options?.onTriggerProcessed;
|
|
19
|
+
this.onError = options?.onError;
|
|
20
|
+
mkdirSync(triggersDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
async start() {
|
|
23
|
+
this.stopped = false;
|
|
24
|
+
// Drain any pending triggers from before process started
|
|
25
|
+
await this._drainPending();
|
|
26
|
+
// Watch for new triggers
|
|
27
|
+
this.watcher = watch(this.triggersDir, (eventType, filename) => {
|
|
28
|
+
if (this.stopped)
|
|
29
|
+
return;
|
|
30
|
+
if (eventType === "rename" && filename?.endsWith(".json")) {
|
|
31
|
+
this._processFile(filename);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
stop() {
|
|
36
|
+
this.stopped = true;
|
|
37
|
+
if (this.watcher) {
|
|
38
|
+
this.watcher.close();
|
|
39
|
+
this.watcher = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async _drainPending() {
|
|
43
|
+
let files;
|
|
44
|
+
try {
|
|
45
|
+
files = readdirSync(this.triggersDir)
|
|
46
|
+
.filter((f) => f.endsWith(".json"))
|
|
47
|
+
.sort();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
if (this.stopped)
|
|
54
|
+
break;
|
|
55
|
+
await this._processFile(file);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async _processFile(filename) {
|
|
59
|
+
// Deduplicate: fs.watch fires multiple events for the same file
|
|
60
|
+
if (this.processing.has(filename))
|
|
61
|
+
return;
|
|
62
|
+
this.processing.add(filename);
|
|
63
|
+
const filepath = join(this.triggersDir, filename);
|
|
64
|
+
try {
|
|
65
|
+
if (!existsSync(filepath))
|
|
66
|
+
return;
|
|
67
|
+
const raw = readFileSync(filepath, "utf-8");
|
|
68
|
+
const trigger = JSON.parse(raw);
|
|
69
|
+
if (!trigger.target && !this.defaultTarget) {
|
|
70
|
+
this.onError?.(new Error(`Trigger ${filename} has no target and no default configured`), "processFile");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const target = trigger.target || this.defaultTarget;
|
|
74
|
+
// Send as an event-role message — this is a system event, not a user turn
|
|
75
|
+
const session = this.client.session(target);
|
|
76
|
+
const handle = session.send({
|
|
77
|
+
messages: [
|
|
78
|
+
{
|
|
79
|
+
role: "event",
|
|
80
|
+
content: [{ type: "text", text: trigger.prompt }],
|
|
81
|
+
metadata: {
|
|
82
|
+
source: { type: "cron" },
|
|
83
|
+
event_type: "cron_trigger",
|
|
84
|
+
job_id: trigger.jobId,
|
|
85
|
+
job_name: trigger.jobName,
|
|
86
|
+
fired_at: trigger.firedAt,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
// Wait for the model to process — don't delete trigger until delivery confirmed
|
|
92
|
+
await handle.result;
|
|
93
|
+
// Delete trigger file after successful delivery
|
|
94
|
+
try {
|
|
95
|
+
unlinkSync(filepath);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Already cleaned up
|
|
99
|
+
}
|
|
100
|
+
// If oneshot, delete the job
|
|
101
|
+
if (trigger.oneshot) {
|
|
102
|
+
this.store.delete(trigger.jobId);
|
|
103
|
+
}
|
|
104
|
+
this.onProcessed?.(trigger);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
this.onError?.(error instanceof Error ? error : new Error(String(error)), `processFile:${filename}`);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
this.processing.delete(filename);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=trigger-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-watcher.js","sourceRoot":"","sources":["../src/trigger-watcher.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AgentickClient } from "@agentick/client";
|
|
2
|
+
declare module "@agentick/shared" {
|
|
3
|
+
interface MessageSourceTypes {
|
|
4
|
+
cron: {
|
|
5
|
+
type: "cron";
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export interface Job {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
cron: string;
|
|
13
|
+
target: string;
|
|
14
|
+
prompt: string;
|
|
15
|
+
oneshot: boolean;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
lastFiredAt?: string;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export interface Trigger {
|
|
22
|
+
jobId: string;
|
|
23
|
+
jobName: string;
|
|
24
|
+
target: string;
|
|
25
|
+
prompt: string;
|
|
26
|
+
firedAt: string;
|
|
27
|
+
oneshot: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface SchedulerBackend {
|
|
30
|
+
start(): Promise<void>;
|
|
31
|
+
stop(): Promise<void>;
|
|
32
|
+
schedule(job: Job, onFire: () => Promise<void>): void;
|
|
33
|
+
unschedule(jobId: string): void;
|
|
34
|
+
}
|
|
35
|
+
export interface CronServiceOptions {
|
|
36
|
+
dataDir: string;
|
|
37
|
+
client: AgentickClient;
|
|
38
|
+
backend?: SchedulerBackend;
|
|
39
|
+
watchExternalTriggers?: boolean;
|
|
40
|
+
defaultTarget?: string;
|
|
41
|
+
onTriggerProcessed?: (trigger: Trigger) => void;
|
|
42
|
+
onError?: (error: Error, context: string) => void;
|
|
43
|
+
}
|
|
44
|
+
export interface HeartbeatOptions {
|
|
45
|
+
cron?: string;
|
|
46
|
+
target?: string;
|
|
47
|
+
heartbeatFile?: string;
|
|
48
|
+
}
|
|
49
|
+
export type CreateJobInput = Omit<Job, "id" | "createdAt" | "lastFiredAt"> & {
|
|
50
|
+
id?: string;
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,QAAQ,kBAAkB,CAAC;IAChC,UAAU,kBAAkB;QAC1B,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;KACxB;CACF;AAED,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG;IAC3E,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentick/scheduler",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Scheduled jobs, heartbeat, and cron triggers for Agentick agents",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent",
|
|
7
|
+
"ai",
|
|
8
|
+
"cron",
|
|
9
|
+
"heartbeat",
|
|
10
|
+
"scheduler"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Ryan Lindgren",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/agenticklabs/agentick.git",
|
|
17
|
+
"directory": "packages/scheduler"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "src/index.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": "./src/index.ts"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"main": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.build.json",
|
|
40
|
+
"test": "echo \"Tests run from workspace root\"",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
42
|
+
"lint": "oxlint src/",
|
|
43
|
+
"format:check": "oxfmt --check src/",
|
|
44
|
+
"clean": "rm -rf dist tsconfig.build.tsbuildinfo",
|
|
45
|
+
"prepublishOnly": "pnpm build",
|
|
46
|
+
"dev": "tsc --watch"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@agentick/client": "workspace:*",
|
|
50
|
+
"@agentick/core": "workspace:*",
|
|
51
|
+
"@agentick/shared": "workspace:*",
|
|
52
|
+
"node-cron": "^3.0.3",
|
|
53
|
+
"zod": "^4.3.6"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node-cron": "^3.0.11",
|
|
57
|
+
"@types/react": "^19.0.0",
|
|
58
|
+
"react": "^19.0.0",
|
|
59
|
+
"typescript": "^5.8.3"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"react": "^19.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { CronService } from "./cron-service.js";
|
|
2
|
+
export { JobStore } from "./job-store.js";
|
|
3
|
+
export { createNodeCronBackend } from "./node-cron-backend.js";
|
|
4
|
+
export { TriggerWatcher } from "./trigger-watcher.js";
|
|
5
|
+
export { createScheduleTool } from "./schedule-tool.js";
|
|
6
|
+
export { createHeartbeatJob } from "./heartbeat.js";
|
|
7
|
+
export { bindSchedulerStore, getSchedulerStore } from "./bridge.js";
|
|
8
|
+
export type {
|
|
9
|
+
Job,
|
|
10
|
+
Trigger,
|
|
11
|
+
SchedulerBackend,
|
|
12
|
+
CronServiceOptions,
|
|
13
|
+
HeartbeatOptions,
|
|
14
|
+
CreateJobInput,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
export type { NodeCronBackendOptions } from "./node-cron-backend.js";
|