@stevederico/dotbot 0.19.0 → 0.20.1

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.
@@ -1,242 +0,0 @@
1
- import { TaskStore } from './TaskStore.js';
2
-
3
- // Lazy-load mongodb to avoid hard dependency at module evaluation time
4
- let _ObjectId = null;
5
- async function getObjectId() {
6
- if (!_ObjectId) { _ObjectId = (await import('mongodb')).ObjectId; }
7
- return _ObjectId;
8
- }
9
-
10
- /**
11
- * MongoDB implementation of TaskStore
12
- */
13
- export class MongoTaskStore extends TaskStore {
14
- constructor() {
15
- super();
16
- this.collection = null;
17
- }
18
-
19
- /**
20
- * Initialize MongoDB task store
21
- *
22
- * @param {import('mongodb').Db} db - MongoDB database instance
23
- * @param {Object} options - Optional configuration
24
- */
25
- async init(db, options = {}) {
26
- this.collection = db.collection('tasks');
27
-
28
- // Create indexes
29
- await this.collection.createIndex({ userId: 1, status: 1 }).catch(() => {});
30
- await this.collection.createIndex({ userId: 1, category: 1 }).catch(() => {});
31
- await this.collection.createIndex({ userId: 1, priority: 1 }).catch(() => {});
32
- await this.collection.createIndex({ userId: 1, deadline: 1 }).catch(() => {});
33
-
34
- console.log('[tasks] MongoTaskStore initialized');
35
- }
36
-
37
- /**
38
- * Create a new task
39
- */
40
- async createTask({ userId, description, steps = [], category = 'general', priority = 'medium', deadline = null, mode = 'auto' }) {
41
- // Normalize steps to objects
42
- const normalizedSteps = steps.map(step => {
43
- if (typeof step === 'string') {
44
- return {
45
- text: step,
46
- action: step, // Default action is same as text
47
- done: false,
48
- result: null,
49
- startedAt: null,
50
- completedAt: null,
51
- };
52
- }
53
- return {
54
- text: step.text || step.description || '',
55
- action: step.action || step.text || '',
56
- done: step.done || false,
57
- result: step.result || null,
58
- startedAt: step.startedAt || null,
59
- completedAt: step.completedAt || null,
60
- };
61
- });
62
-
63
- const doc = {
64
- userId,
65
- description,
66
- steps: normalizedSteps,
67
- category,
68
- priority,
69
- deadline,
70
- mode, // 'auto' or 'manual'
71
- status: 'pending', // pending, in_progress, completed
72
- currentStep: 0,
73
- progress: 0,
74
- createdAt: new Date(),
75
- updatedAt: new Date(),
76
- lastWorkedAt: null,
77
- };
78
-
79
- const result = await this.collection.insertOne(doc);
80
- return { ...doc, _id: result.insertedId };
81
- }
82
-
83
- /**
84
- * Get tasks for a user
85
- */
86
- async getTasks(userId, filters = {}) {
87
- const query = { userId };
88
-
89
- if (filters.status) query.status = filters.status;
90
- if (filters.category) query.category = filters.category;
91
- if (filters.priority) query.priority = filters.priority;
92
-
93
- const tasks = await this.collection
94
- .find(query)
95
- .sort({ createdAt: -1 })
96
- .toArray();
97
-
98
- // Calculate progress for each task
99
- return tasks.map(task => ({
100
- ...task,
101
- progress: this._calculateProgress(task),
102
- }));
103
- }
104
-
105
- /**
106
- * Get a single task by ID
107
- */
108
- async getTask(userId, taskId) {
109
- const ObjectId = await getObjectId();
110
- const task = await this.collection.findOne({
111
- _id: new ObjectId(taskId),
112
- userId,
113
- });
114
-
115
- if (!task) return null;
116
-
117
- return {
118
- ...task,
119
- progress: this._calculateProgress(task),
120
- };
121
- }
122
-
123
- /**
124
- * Update a task
125
- */
126
- async updateTask(userId, taskId, updates) {
127
- const ObjectId = await getObjectId();
128
- const validUpdates = {};
129
- const allowedFields = [
130
- 'description', 'steps', 'category', 'priority', 'deadline',
131
- 'mode', 'status', 'currentStep', 'lastWorkedAt'
132
- ];
133
-
134
- for (const field of allowedFields) {
135
- if (updates[field] !== undefined) {
136
- validUpdates[field] = updates[field];
137
- }
138
- }
139
-
140
- // Recalculate progress if steps changed
141
- if (updates.steps) {
142
- validUpdates.progress = this._calculateProgressFromSteps(updates.steps);
143
- }
144
-
145
- validUpdates.updatedAt = new Date();
146
-
147
- const result = await this.collection.updateOne(
148
- { _id: new ObjectId(taskId), userId },
149
- { $set: validUpdates }
150
- );
151
-
152
- return result;
153
- }
154
-
155
- /**
156
- * Delete a task
157
- */
158
- async deleteTask(userId, taskId) {
159
- const ObjectId = await getObjectId();
160
- const result = await this.collection.deleteOne({
161
- _id: new ObjectId(taskId),
162
- userId,
163
- });
164
- return result;
165
- }
166
-
167
- /**
168
- * Search tasks by text
169
- */
170
- async searchTasks(userId, query) {
171
- const regex = new RegExp(query, 'i');
172
- const tasks = await this.collection
173
- .find({
174
- userId,
175
- $or: [
176
- { description: regex },
177
- { 'steps.text': regex },
178
- ],
179
- })
180
- .sort({ createdAt: -1 })
181
- .toArray();
182
-
183
- return tasks.map(task => ({
184
- ...task,
185
- progress: this._calculateProgress(task),
186
- }));
187
- }
188
-
189
- /**
190
- * Get task statistics
191
- */
192
- async getTaskStats(userId) {
193
- const tasks = await this.collection.find({ userId }).toArray();
194
-
195
- const stats = {
196
- total: tasks.length,
197
- pending: tasks.filter(g => g.status === 'pending').length,
198
- in_progress: tasks.filter(g => g.status === 'in_progress').length,
199
- completed: tasks.filter(g => g.status === 'completed').length,
200
- by_category: {},
201
- by_priority: {},
202
- overdue: 0,
203
- };
204
-
205
- const now = new Date();
206
-
207
- for (const task of tasks) {
208
- // Count by category
209
- const cat = task.category || 'general';
210
- stats.by_category[cat] = (stats.by_category[cat] || 0) + 1;
211
-
212
- // Count by priority
213
- const pri = task.priority || 'medium';
214
- stats.by_priority[pri] = (stats.by_priority[pri] || 0) + 1;
215
-
216
- // Count overdue
217
- if (task.deadline && new Date(task.deadline) < now && task.status !== 'completed') {
218
- stats.overdue++;
219
- }
220
- }
221
-
222
- return stats;
223
- }
224
-
225
- /**
226
- * Calculate progress percentage from task
227
- * @private
228
- */
229
- _calculateProgress(task) {
230
- return this._calculateProgressFromSteps(task.steps || []);
231
- }
232
-
233
- /**
234
- * Calculate progress percentage from steps array
235
- * @private
236
- */
237
- _calculateProgressFromSteps(steps) {
238
- if (!steps || steps.length === 0) return 0;
239
- const doneCount = steps.filter(s => s.done).length;
240
- return Math.round((doneCount / steps.length) * 100);
241
- }
242
- }
@@ -1,158 +0,0 @@
1
- import { TriggerStore } from './TriggerStore.js';
2
-
3
- // Lazy-load mongodb to avoid hard dependency at module evaluation time
4
- let _ObjectId = null;
5
- async function getObjectId() {
6
- if (!_ObjectId) { _ObjectId = (await import('mongodb')).ObjectId; }
7
- return _ObjectId;
8
- }
9
-
10
- /**
11
- * MongoDB implementation of TriggerStore
12
- */
13
- export class MongoTriggerStore extends TriggerStore {
14
- constructor() {
15
- super();
16
- this.collection = null;
17
- }
18
-
19
- /**
20
- * Initialize MongoDB trigger store
21
- *
22
- * @param {import('mongodb').Db} db - MongoDB database instance
23
- * @param {Object} options - Optional configuration
24
- */
25
- async init(db, options = {}) {
26
- this.collection = db.collection('triggers');
27
-
28
- // Create indexes
29
- await this.collection.createIndex({ userId: 1, eventType: 1 }).catch(() => {});
30
- await this.collection.createIndex({ userId: 1, enabled: 1 }).catch(() => {});
31
-
32
- console.log('[triggers] MongoTriggerStore initialized');
33
- }
34
-
35
- /**
36
- * Create an event trigger
37
- */
38
- async createTrigger({ userId, eventType, prompt, cooldownMs = 0, metadata = {}, enabled = true }) {
39
- const doc = {
40
- userId,
41
- eventType,
42
- prompt,
43
- cooldownMs,
44
- metadata,
45
- enabled,
46
- lastFiredAt: null,
47
- fireCount: 0,
48
- createdAt: new Date(),
49
- updatedAt: new Date(),
50
- };
51
-
52
- const result = await this.collection.insertOne(doc);
53
- return { ...doc, _id: result.insertedId };
54
- }
55
-
56
- /**
57
- * List triggers for a user
58
- */
59
- async listTriggers(userId, filters = {}) {
60
- const query = { userId };
61
-
62
- if (filters.enabled !== undefined) {
63
- query.enabled = filters.enabled;
64
- }
65
- if (filters.eventType) {
66
- query.eventType = filters.eventType;
67
- }
68
-
69
- return await this.collection
70
- .find(query)
71
- .sort({ createdAt: -1 })
72
- .toArray();
73
- }
74
-
75
- /**
76
- * Find enabled triggers matching userId and eventType, filtering out
77
- * those still within cooldown period
78
- */
79
- async findMatchingTriggers(userId, eventType, metadata = {}) {
80
- const now = Date.now();
81
-
82
- // Find all enabled triggers for this user and event type
83
- const triggers = await this.collection
84
- .find({ userId, eventType, enabled: true })
85
- .toArray();
86
-
87
- // Filter by cooldown
88
- const activeTriggers = triggers.filter(trigger => {
89
- // No cooldown → always active
90
- if (!trigger.cooldownMs) return true;
91
-
92
- // Never fired → active
93
- if (!trigger.lastFiredAt) return true;
94
-
95
- // Check if cooldown period has passed
96
- const lastFiredTime = new Date(trigger.lastFiredAt).getTime();
97
- return (lastFiredTime + trigger.cooldownMs) <= now;
98
- });
99
-
100
- // Filter by metadata if trigger has metadata requirements
101
- const matchedTriggers = activeTriggers.filter(trigger => {
102
- if (!trigger.metadata || Object.keys(trigger.metadata).length === 0) {
103
- return true; // No metadata requirements
104
- }
105
-
106
- // Check if event metadata matches trigger metadata
107
- for (const [key, value] of Object.entries(trigger.metadata)) {
108
- if (metadata[key] !== value) return false;
109
- }
110
- return true;
111
- });
112
-
113
- return matchedTriggers;
114
- }
115
-
116
- /**
117
- * Toggle a trigger on/off
118
- */
119
- async toggleTrigger(userId, triggerId, enabled) {
120
- const ObjectId = await getObjectId();
121
- const result = await this.collection.updateOne(
122
- { _id: new ObjectId(triggerId), userId },
123
- {
124
- $set: {
125
- enabled,
126
- updatedAt: new Date()
127
- }
128
- }
129
- );
130
- return result;
131
- }
132
-
133
- /**
134
- * Delete a trigger
135
- */
136
- async deleteTrigger(userId, triggerId) {
137
- const ObjectId = await getObjectId();
138
- const result = await this.collection.deleteOne({
139
- _id: new ObjectId(triggerId),
140
- userId,
141
- });
142
- return result;
143
- }
144
-
145
- /**
146
- * Record that a trigger has fired
147
- */
148
- async markTriggerFired(triggerId) {
149
- const ObjectId = await getObjectId();
150
- await this.collection.updateOne(
151
- { _id: new ObjectId(triggerId) },
152
- {
153
- $set: { lastFiredAt: new Date() },
154
- $inc: { fireCount: 1 }
155
- }
156
- );
157
- }
158
- }