@hbarefoot/engram 1.0.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/LICENSE +21 -0
- package/README.md +535 -0
- package/bin/engram.js +421 -0
- package/dashboard/dist/assets/index-BHkLa5w_.css +1 -0
- package/dashboard/dist/assets/index-D9QR_Cnu.js +45 -0
- package/dashboard/dist/index.html +14 -0
- package/dashboard/package.json +21 -0
- package/package.json +76 -0
- package/src/config/index.js +150 -0
- package/src/embed/index.js +249 -0
- package/src/export/static.js +396 -0
- package/src/extract/rules.js +233 -0
- package/src/extract/secrets.js +114 -0
- package/src/index.js +54 -0
- package/src/memory/consolidate.js +420 -0
- package/src/memory/context.js +346 -0
- package/src/memory/feedback.js +197 -0
- package/src/memory/recall.js +350 -0
- package/src/memory/store.js +626 -0
- package/src/server/mcp.js +668 -0
- package/src/server/rest.js +499 -0
- package/src/utils/id.js +9 -0
- package/src/utils/logger.js +79 -0
- package/src/utils/time.js +296 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time utility functions for temporal queries
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const DAY_MS = 86400000;
|
|
6
|
+
const WEEK_MS = DAY_MS * 7;
|
|
7
|
+
const MONTH_MS = DAY_MS * 30;
|
|
8
|
+
const YEAR_MS = DAY_MS * 365;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get start of day for a timestamp
|
|
12
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
13
|
+
* @returns {number} Start of day timestamp
|
|
14
|
+
*/
|
|
15
|
+
function startOfDay(timestamp) {
|
|
16
|
+
const date = new Date(timestamp);
|
|
17
|
+
date.setHours(0, 0, 0, 0);
|
|
18
|
+
return date.getTime();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get end of day for a timestamp
|
|
23
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
24
|
+
* @returns {number} End of day timestamp
|
|
25
|
+
*/
|
|
26
|
+
function endOfDay(timestamp) {
|
|
27
|
+
const date = new Date(timestamp);
|
|
28
|
+
date.setHours(23, 59, 59, 999);
|
|
29
|
+
return date.getTime();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get start of week (Sunday) for a timestamp
|
|
34
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
35
|
+
* @returns {number} Start of week timestamp
|
|
36
|
+
*/
|
|
37
|
+
function startOfWeek(timestamp) {
|
|
38
|
+
const date = new Date(timestamp);
|
|
39
|
+
const day = date.getDay();
|
|
40
|
+
date.setDate(date.getDate() - day);
|
|
41
|
+
date.setHours(0, 0, 0, 0);
|
|
42
|
+
return date.getTime();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get end of week (Saturday) for a timestamp
|
|
47
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
48
|
+
* @returns {number} End of week timestamp
|
|
49
|
+
*/
|
|
50
|
+
function endOfWeek(timestamp) {
|
|
51
|
+
const date = new Date(timestamp);
|
|
52
|
+
const day = date.getDay();
|
|
53
|
+
date.setDate(date.getDate() + (6 - day));
|
|
54
|
+
date.setHours(23, 59, 59, 999);
|
|
55
|
+
return date.getTime();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get start of month for a timestamp
|
|
60
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
61
|
+
* @returns {number} Start of month timestamp
|
|
62
|
+
*/
|
|
63
|
+
function startOfMonth(timestamp) {
|
|
64
|
+
const date = new Date(timestamp);
|
|
65
|
+
date.setDate(1);
|
|
66
|
+
date.setHours(0, 0, 0, 0);
|
|
67
|
+
return date.getTime();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get end of month for a timestamp
|
|
72
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
73
|
+
* @returns {number} End of month timestamp
|
|
74
|
+
*/
|
|
75
|
+
function endOfMonth(timestamp) {
|
|
76
|
+
const date = new Date(timestamp);
|
|
77
|
+
date.setMonth(date.getMonth() + 1);
|
|
78
|
+
date.setDate(0);
|
|
79
|
+
date.setHours(23, 59, 59, 999);
|
|
80
|
+
return date.getTime();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get start of year for a timestamp
|
|
85
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
86
|
+
* @returns {number} Start of year timestamp
|
|
87
|
+
*/
|
|
88
|
+
function startOfYear(timestamp) {
|
|
89
|
+
const date = new Date(timestamp);
|
|
90
|
+
date.setMonth(0, 1);
|
|
91
|
+
date.setHours(0, 0, 0, 0);
|
|
92
|
+
return date.getTime();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get end of year for a timestamp
|
|
97
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
98
|
+
* @returns {number} End of year timestamp
|
|
99
|
+
*/
|
|
100
|
+
function endOfYear(timestamp) {
|
|
101
|
+
const date = new Date(timestamp);
|
|
102
|
+
date.setMonth(11, 31);
|
|
103
|
+
date.setHours(23, 59, 59, 999);
|
|
104
|
+
return date.getTime();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Parse a relative time string into a timestamp
|
|
109
|
+
* @param {string} input - Relative time string (e.g., "3 days ago", "last week")
|
|
110
|
+
* @returns {number|null} Unix timestamp in ms, or null if not parseable
|
|
111
|
+
*/
|
|
112
|
+
export function parseRelativeTime(input) {
|
|
113
|
+
if (!input || typeof input !== 'string') {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
const normalized = input.toLowerCase().trim();
|
|
119
|
+
|
|
120
|
+
// Check for ISO date format first
|
|
121
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(input)) {
|
|
122
|
+
const parsed = new Date(input);
|
|
123
|
+
if (!isNaN(parsed.getTime())) {
|
|
124
|
+
return parsed.getTime();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Special keywords
|
|
129
|
+
const keywords = {
|
|
130
|
+
'now': now,
|
|
131
|
+
'today': startOfDay(now),
|
|
132
|
+
'yesterday': startOfDay(now - DAY_MS),
|
|
133
|
+
'tomorrow': startOfDay(now + DAY_MS)
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (keywords[normalized] !== undefined) {
|
|
137
|
+
return keywords[normalized];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Relative patterns: "X days/weeks/months ago"
|
|
141
|
+
const agoMatch = normalized.match(/^(\d+)\s*(day|days|week|weeks|month|months|year|years)\s*ago$/);
|
|
142
|
+
if (agoMatch) {
|
|
143
|
+
const amount = parseInt(agoMatch[1], 10);
|
|
144
|
+
const unit = agoMatch[2].replace(/s$/, ''); // Remove trailing 's'
|
|
145
|
+
|
|
146
|
+
const unitMs = {
|
|
147
|
+
'day': DAY_MS,
|
|
148
|
+
'week': WEEK_MS,
|
|
149
|
+
'month': MONTH_MS,
|
|
150
|
+
'year': YEAR_MS
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
return now - (amount * unitMs[unit]);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// "last X" patterns
|
|
157
|
+
const lastMatch = normalized.match(/^last\s*(day|week|month|year)$/);
|
|
158
|
+
if (lastMatch) {
|
|
159
|
+
const unit = lastMatch[1];
|
|
160
|
+
switch (unit) {
|
|
161
|
+
case 'day':
|
|
162
|
+
return startOfDay(now - DAY_MS);
|
|
163
|
+
case 'week':
|
|
164
|
+
return startOfWeek(now - WEEK_MS);
|
|
165
|
+
case 'month':
|
|
166
|
+
return startOfMonth(now - MONTH_MS);
|
|
167
|
+
case 'year':
|
|
168
|
+
return startOfYear(now - YEAR_MS);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Parse a period shorthand into a time range
|
|
177
|
+
* @param {string} period - Period shorthand (e.g., "today", "this_week", "last_month")
|
|
178
|
+
* @returns {Object|null} Object with start and end timestamps, or null if not parseable
|
|
179
|
+
*/
|
|
180
|
+
export function parsePeriod(period) {
|
|
181
|
+
if (!period || typeof period !== 'string') {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const now = Date.now();
|
|
186
|
+
|
|
187
|
+
const periods = {
|
|
188
|
+
'today': {
|
|
189
|
+
start: startOfDay(now),
|
|
190
|
+
end: endOfDay(now),
|
|
191
|
+
description: 'today'
|
|
192
|
+
},
|
|
193
|
+
'yesterday': {
|
|
194
|
+
start: startOfDay(now - DAY_MS),
|
|
195
|
+
end: endOfDay(now - DAY_MS),
|
|
196
|
+
description: 'yesterday'
|
|
197
|
+
},
|
|
198
|
+
'this_week': {
|
|
199
|
+
start: startOfWeek(now),
|
|
200
|
+
end: now,
|
|
201
|
+
description: 'this week'
|
|
202
|
+
},
|
|
203
|
+
'last_week': {
|
|
204
|
+
start: startOfWeek(now - WEEK_MS),
|
|
205
|
+
end: endOfWeek(now - WEEK_MS),
|
|
206
|
+
description: 'last week'
|
|
207
|
+
},
|
|
208
|
+
'this_month': {
|
|
209
|
+
start: startOfMonth(now),
|
|
210
|
+
end: now,
|
|
211
|
+
description: 'this month'
|
|
212
|
+
},
|
|
213
|
+
'last_month': {
|
|
214
|
+
start: startOfMonth(now - MONTH_MS),
|
|
215
|
+
end: endOfMonth(now - MONTH_MS),
|
|
216
|
+
description: 'last month'
|
|
217
|
+
},
|
|
218
|
+
'this_year': {
|
|
219
|
+
start: startOfYear(now),
|
|
220
|
+
end: now,
|
|
221
|
+
description: 'this year'
|
|
222
|
+
},
|
|
223
|
+
'last_year': {
|
|
224
|
+
start: startOfYear(now - YEAR_MS),
|
|
225
|
+
end: endOfYear(now - YEAR_MS),
|
|
226
|
+
description: 'last year'
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
return periods[period] || null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Parse a time filter object into start/end timestamps
|
|
235
|
+
* @param {Object} timeFilter - Time filter object
|
|
236
|
+
* @param {string} [timeFilter.after] - Start time (ISO date or relative)
|
|
237
|
+
* @param {string} [timeFilter.before] - End time (ISO date or relative)
|
|
238
|
+
* @param {string} [timeFilter.period] - Period shorthand
|
|
239
|
+
* @returns {Object} Object with start, end, and description
|
|
240
|
+
*/
|
|
241
|
+
export function parseTimeFilter(timeFilter) {
|
|
242
|
+
if (!timeFilter) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
|
|
248
|
+
// If period is specified, use that
|
|
249
|
+
if (timeFilter.period) {
|
|
250
|
+
return parsePeriod(timeFilter.period);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Parse after/before
|
|
254
|
+
let start = null;
|
|
255
|
+
let end = null;
|
|
256
|
+
let description = '';
|
|
257
|
+
|
|
258
|
+
if (timeFilter.after) {
|
|
259
|
+
start = parseRelativeTime(timeFilter.after);
|
|
260
|
+
description = `after ${timeFilter.after}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (timeFilter.before) {
|
|
264
|
+
end = parseRelativeTime(timeFilter.before);
|
|
265
|
+
if (description) {
|
|
266
|
+
description += ` and before ${timeFilter.before}`;
|
|
267
|
+
} else {
|
|
268
|
+
description = `before ${timeFilter.before}`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Default end to now if only after is specified
|
|
273
|
+
if (start && !end) {
|
|
274
|
+
end = now;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Default start to beginning of time if only before is specified
|
|
278
|
+
if (!start && end) {
|
|
279
|
+
start = 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (start === null && end === null) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return { start, end, description };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Format a timestamp as ISO string
|
|
291
|
+
* @param {number} timestamp - Unix timestamp in ms
|
|
292
|
+
* @returns {string} ISO date string
|
|
293
|
+
*/
|
|
294
|
+
export function formatTimestamp(timestamp) {
|
|
295
|
+
return new Date(timestamp).toISOString();
|
|
296
|
+
}
|