augure 0.7.1 → 0.9.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.
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ noopLogger
4
+ } from "./chunk-62OPINAX.js";
5
+
6
+ // ../scheduler/dist/cron.js
7
+ import { createTask, validate } from "node-cron";
8
+ var CronScheduler = class {
9
+ jobs = /* @__PURE__ */ new Map();
10
+ tasks = /* @__PURE__ */ new Map();
11
+ timers = /* @__PURE__ */ new Map();
12
+ handlers = [];
13
+ persistChain = Promise.resolve();
14
+ running = false;
15
+ store;
16
+ log;
17
+ constructor(storeOrOpts) {
18
+ if (storeOrOpts && "save" in storeOrOpts) {
19
+ this.store = storeOrOpts;
20
+ this.log = noopLogger;
21
+ } else if (storeOrOpts) {
22
+ const opts = storeOrOpts;
23
+ this.store = opts.store;
24
+ this.log = opts.logger ?? noopLogger;
25
+ } else {
26
+ this.log = noopLogger;
27
+ }
28
+ }
29
+ onJobTrigger(handler) {
30
+ this.handlers.push(handler);
31
+ }
32
+ addJob(job) {
33
+ if (!job.cron && !job.runAt) {
34
+ throw new Error(`Job ${job.id} must have either cron or runAt`);
35
+ }
36
+ if (job.cron && !validate(job.cron)) {
37
+ throw new Error(`Invalid cron expression: ${job.cron}`);
38
+ }
39
+ if (job.runAt && isNaN(Date.parse(job.runAt))) {
40
+ throw new Error(`Invalid runAt date: ${job.runAt}`);
41
+ }
42
+ this.jobs.set(job.id, job);
43
+ this.log.info(`Added job ${job.id} (${job.cron ? `cron: ${job.cron}` : `runAt: ${job.runAt}`})`);
44
+ if (job.enabled && job.cron) {
45
+ const task = createTask(job.cron, () => {
46
+ this.log.info(`Cron fired for job ${job.id}`);
47
+ this.executeHandlers(job).catch((err) => this.log.error(`Cron job ${job.id} handler failed:`, err));
48
+ });
49
+ if (this.running) {
50
+ task.start();
51
+ this.log.debug(`Started cron task for ${job.id} immediately (scheduler already running)`);
52
+ }
53
+ this.tasks.set(job.id, task);
54
+ }
55
+ if (job.enabled && job.runAt && !job.cron) {
56
+ if (this.running) {
57
+ this.scheduleOneShot(job);
58
+ } else {
59
+ this.log.warn(`Scheduler not running \u2014 one-shot job ${job.id} will be scheduled on start()`);
60
+ }
61
+ }
62
+ this.persist();
63
+ }
64
+ removeJob(id) {
65
+ const task = this.tasks.get(id);
66
+ if (task) {
67
+ task.stop();
68
+ this.tasks.delete(id);
69
+ }
70
+ const timer = this.timers.get(id);
71
+ if (timer) {
72
+ clearTimeout(timer);
73
+ this.timers.delete(id);
74
+ }
75
+ this.jobs.delete(id);
76
+ this.log.debug(`Removed job ${id}`);
77
+ this.persist();
78
+ }
79
+ listJobs() {
80
+ return Array.from(this.jobs.values());
81
+ }
82
+ async triggerJob(id) {
83
+ const job = this.jobs.get(id);
84
+ if (!job) {
85
+ throw new Error(`Job not found: ${id}`);
86
+ }
87
+ await this.executeHandlers(job);
88
+ }
89
+ async loadPersistedJobs() {
90
+ if (!this.store)
91
+ return;
92
+ const jobs = await this.store.load();
93
+ this.log.info(`Loading ${jobs.length} persisted jobs`);
94
+ for (const job of jobs) {
95
+ if (job.runAt && Date.parse(job.runAt) <= Date.now()) {
96
+ this.log.debug(`Skipping expired one-shot job ${job.id} (runAt: ${job.runAt})`);
97
+ continue;
98
+ }
99
+ this.addJob(job);
100
+ }
101
+ }
102
+ start() {
103
+ this.running = true;
104
+ this.log.info(`Starting with ${this.tasks.size} cron tasks and ${this.handlers.length} handlers`);
105
+ for (const [id, task] of this.tasks) {
106
+ task.start();
107
+ this.log.debug(`Started cron task: ${id}`);
108
+ }
109
+ for (const job of this.jobs.values()) {
110
+ if (job.enabled && job.runAt && !job.cron) {
111
+ this.scheduleOneShot(job);
112
+ }
113
+ }
114
+ }
115
+ stop() {
116
+ this.log.info("Scheduler stopped");
117
+ this.running = false;
118
+ for (const task of this.tasks.values()) {
119
+ task.stop();
120
+ }
121
+ for (const timer of this.timers.values()) {
122
+ clearTimeout(timer);
123
+ }
124
+ this.timers.clear();
125
+ }
126
+ scheduleOneShot(job) {
127
+ const delayMs = Date.parse(job.runAt) - Date.now();
128
+ if (delayMs <= 0) {
129
+ this.log.warn(`One-shot job ${job.id} already expired (delay: ${delayMs}ms), skipping`);
130
+ return;
131
+ }
132
+ this.log.info(`Scheduled one-shot job ${job.id} in ${Math.round(delayMs / 1e3)}s (${job.runAt})`);
133
+ const timer = setTimeout(() => {
134
+ this.log.info(`One-shot job ${job.id} firing now`);
135
+ this.timers.delete(job.id);
136
+ this.executeHandlers(job).then(() => this.removeJob(job.id)).catch((err) => this.log.error(`One-shot job ${job.id} handler failed:`, err));
137
+ }, delayMs);
138
+ this.timers.set(job.id, timer);
139
+ }
140
+ persist() {
141
+ if (!this.store)
142
+ return;
143
+ const jobs = this.listJobs();
144
+ this.persistChain = this.persistChain.then(() => this.store.save(jobs));
145
+ }
146
+ async executeHandlers(job) {
147
+ this.log.debug(`Executing ${this.handlers.length} handlers for job ${job.id}`);
148
+ for (const handler of this.handlers) {
149
+ await handler(job);
150
+ }
151
+ }
152
+ };
153
+
154
+ // ../scheduler/dist/jobs.js
155
+ import { readFile, writeFile, mkdir } from "fs/promises";
156
+ import { dirname } from "path";
157
+ var JobStore = class {
158
+ filePath;
159
+ constructor(filePath) {
160
+ this.filePath = filePath;
161
+ }
162
+ async load() {
163
+ try {
164
+ const raw = await readFile(this.filePath, "utf-8");
165
+ return JSON.parse(raw);
166
+ } catch {
167
+ return [];
168
+ }
169
+ }
170
+ async save(jobs) {
171
+ await mkdir(dirname(this.filePath), { recursive: true });
172
+ await writeFile(this.filePath, JSON.stringify(jobs, null, 2), "utf-8");
173
+ }
174
+ };
175
+
176
+ // ../scheduler/dist/heartbeat.js
177
+ var HEARTBEAT_PROMPT = `You are a monitoring agent. Your job is to review the user's memory and decide if any proactive action is needed right now.
178
+
179
+ Review the memory context below and determine:
180
+ 1. Are there any time-sensitive tasks or reminders?
181
+ 2. Should the user be notified about something?
182
+ 3. Are there any scheduled checks that need to run?
183
+
184
+ If action is needed, respond with:
185
+ ACTION: <description of what to do>
186
+
187
+ If no action is needed, respond with:
188
+ ACTION: none
189
+
190
+ Be concise. Only suggest actions that are clearly needed based on the memory context.`;
191
+ var Heartbeat = class {
192
+ config;
193
+ timer;
194
+ log;
195
+ constructor(config) {
196
+ this.config = config;
197
+ this.log = config.logger ?? noopLogger;
198
+ }
199
+ async tick() {
200
+ this.log.debug("Heartbeat tick");
201
+ const memoryContent = await this.loadMemory();
202
+ const messages = [
203
+ { role: "system", content: HEARTBEAT_PROMPT },
204
+ {
205
+ role: "user",
206
+ content: `Current time: ${(/* @__PURE__ */ new Date()).toISOString()}
207
+
208
+ ## Memory
209
+ ${memoryContent}`
210
+ }
211
+ ];
212
+ const response = await this.config.llm.chat(messages);
213
+ const action = this.parseAction(response.content);
214
+ if (action && action.toLowerCase() !== "none") {
215
+ this.log.debug(`Heartbeat action: ${action}`);
216
+ await this.config.onAction(action);
217
+ } else {
218
+ this.log.debug("Heartbeat: no action needed");
219
+ }
220
+ }
221
+ start() {
222
+ this.timer = setInterval(() => {
223
+ this.tick().catch((err) => this.log.error("Heartbeat error:", err));
224
+ }, this.config.intervalMs);
225
+ }
226
+ stop() {
227
+ if (this.timer) {
228
+ clearInterval(this.timer);
229
+ this.timer = void 0;
230
+ }
231
+ }
232
+ parseAction(content) {
233
+ const match = content.match(/ACTION:\s*(.+)/i);
234
+ return match?.[1]?.trim();
235
+ }
236
+ async loadMemory() {
237
+ try {
238
+ const exists = await this.config.memory.exists("observations.md");
239
+ if (exists) {
240
+ return this.config.memory.read("observations.md");
241
+ }
242
+ } catch {
243
+ }
244
+ return "(no memory available)";
245
+ }
246
+ };
247
+
248
+ // ../scheduler/dist/interval.js
249
+ var UNITS = {
250
+ s: 1e3,
251
+ m: 6e4,
252
+ h: 36e5
253
+ };
254
+ function parseInterval(input) {
255
+ const match = input.match(/^(\d+)([smh])$/);
256
+ if (!match) {
257
+ throw new Error(`Invalid interval format: "${input}". Expected: <number><s|m|h> (e.g. "30m")`);
258
+ }
259
+ const value = parseInt(match[1], 10);
260
+ const unit = match[2];
261
+ if (value <= 0) {
262
+ throw new Error(`Interval must be positive, got: ${value}`);
263
+ }
264
+ return value * UNITS[unit];
265
+ }
266
+
267
+ export {
268
+ CronScheduler,
269
+ JobStore,
270
+ Heartbeat,
271
+ parseInterval
272
+ };
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../types/dist/logger.js
4
+ var noop = () => {
5
+ };
6
+ var noopLogger = {
7
+ debug: noop,
8
+ info: noop,
9
+ warn: noop,
10
+ error: noop,
11
+ child: () => noopLogger
12
+ };
13
+
14
+ export {
15
+ noopLogger
16
+ };