@agentforge-ai/cli 0.5.0 → 0.5.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.
- package/dist/default/convex/agents.ts +8 -8
- package/dist/default/convex/apiKeys.ts +7 -7
- package/dist/default/convex/cronJobs.ts +12 -12
- package/dist/default/convex/files.ts +7 -7
- package/dist/default/convex/folders.ts +11 -11
- package/dist/default/convex/heartbeat.ts +18 -18
- package/dist/default/convex/mastraIntegration.ts +2 -1
- package/dist/default/convex/mcpConnections.ts +5 -5
- package/dist/default/convex/messages.ts +4 -4
- package/dist/default/convex/projects.ts +10 -10
- package/dist/default/convex/schema.ts +150 -83
- package/dist/default/convex/sessions.ts +12 -12
- package/dist/default/convex/settings.ts +3 -3
- package/dist/default/convex/skills.ts +8 -8
- package/dist/default/convex/threads.ts +9 -9
- package/dist/default/convex/usage.ts +16 -16
- package/dist/default/convex/vault.ts +47 -33
- package/package.json +1 -1
- package/templates/default/convex/agents.ts +8 -8
- package/templates/default/convex/apiKeys.ts +7 -7
- package/templates/default/convex/cronJobs.ts +12 -12
- package/templates/default/convex/files.ts +7 -7
- package/templates/default/convex/folders.ts +11 -11
- package/templates/default/convex/heartbeat.ts +18 -18
- package/templates/default/convex/mastraIntegration.ts +2 -1
- package/templates/default/convex/mcpConnections.ts +5 -5
- package/templates/default/convex/messages.ts +4 -4
- package/templates/default/convex/projects.ts +10 -10
- package/templates/default/convex/schema.ts +150 -83
- package/templates/default/convex/sessions.ts +12 -12
- package/templates/default/convex/settings.ts +3 -3
- package/templates/default/convex/skills.ts +8 -8
- package/templates/default/convex/threads.ts +9 -9
- package/templates/default/convex/usage.ts +16 -16
- package/templates/default/convex/vault.ts +47 -33
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v } from "convex/values";
|
|
2
|
-
import { mutation, query,
|
|
2
|
+
import { mutation, query, internalMutation } from "./_generated/server";
|
|
3
3
|
|
|
4
4
|
// ============================================================
|
|
5
5
|
// SECURE VAULT - Encrypted secrets management
|
|
@@ -35,10 +35,20 @@ function maskSecret(value: string): string {
|
|
|
35
35
|
return value.substring(0, 6) + "..." + value.substring(value.length - 4);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
//
|
|
38
|
+
// Encryption key from Convex environment variable.
|
|
39
|
+
// Set VAULT_ENCRYPTION_KEY in your Convex dashboard under Settings > Environment Variables.
|
|
40
|
+
// If not set, a default key is used (NOT SECURE for production).
|
|
41
|
+
function getEncryptionKey(): string {
|
|
42
|
+
return process.env.VAULT_ENCRYPTION_KEY ?? "agentforge-default-key-set-env-var";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// XOR-based encoding with per-entry IV for database obfuscation.
|
|
46
|
+
// For production deployments with sensitive data, consider integrating
|
|
47
|
+
// with a proper KMS (e.g., AWS KMS, Cloudflare Workers Secrets).
|
|
40
48
|
function encodeSecret(value: string, key: string): { encrypted: string; iv: string } {
|
|
41
|
-
const iv = Array.from({ length: 16 }, () =>
|
|
49
|
+
const iv = Array.from({ length: 16 }, () =>
|
|
50
|
+
Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
|
|
51
|
+
).join("");
|
|
42
52
|
const combined = key + iv;
|
|
43
53
|
let encrypted = "";
|
|
44
54
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -52,15 +62,14 @@ function decodeSecret(encrypted: string, iv: string, key: string): string {
|
|
|
52
62
|
const combined = key + iv;
|
|
53
63
|
let decoded = "";
|
|
54
64
|
for (let i = 0; i < encrypted.length; i += 4) {
|
|
55
|
-
const charCode =
|
|
65
|
+
const charCode =
|
|
66
|
+
parseInt(encrypted.substring(i, i + 4), 16) ^
|
|
67
|
+
combined.charCodeAt((i / 4) % combined.length);
|
|
56
68
|
decoded += String.fromCharCode(charCode);
|
|
57
69
|
}
|
|
58
70
|
return decoded;
|
|
59
71
|
}
|
|
60
72
|
|
|
61
|
-
// The encryption key should come from environment variables in production
|
|
62
|
-
const VAULT_ENCRYPTION_KEY = "agentforge-vault-key-change-in-production-2026";
|
|
63
|
-
|
|
64
73
|
// ---- Queries ----
|
|
65
74
|
|
|
66
75
|
export const list = query({
|
|
@@ -69,11 +78,21 @@ export const list = query({
|
|
|
69
78
|
category: v.optional(v.string()),
|
|
70
79
|
},
|
|
71
80
|
handler: async (ctx, args) => {
|
|
72
|
-
let
|
|
81
|
+
let entries;
|
|
73
82
|
if (args.userId) {
|
|
74
|
-
|
|
83
|
+
entries = await ctx.db
|
|
84
|
+
.query("vault")
|
|
85
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
86
|
+
.order("desc")
|
|
87
|
+
.collect();
|
|
88
|
+
} else {
|
|
89
|
+
entries = await ctx.db.query("vault").order("desc").collect();
|
|
75
90
|
}
|
|
76
|
-
|
|
91
|
+
|
|
92
|
+
if (args.category) {
|
|
93
|
+
entries = entries.filter((e) => e.category === args.category);
|
|
94
|
+
}
|
|
95
|
+
|
|
77
96
|
// Never return encrypted values - only masked
|
|
78
97
|
return entries.map((entry) => ({
|
|
79
98
|
_id: entry._id,
|
|
@@ -99,13 +118,13 @@ export const getAuditLog = query({
|
|
|
99
118
|
handler: async (ctx, args) => {
|
|
100
119
|
const limit = args.limit ?? 50;
|
|
101
120
|
if (args.vaultEntryId) {
|
|
102
|
-
return ctx.db
|
|
121
|
+
return await ctx.db
|
|
103
122
|
.query("vaultAuditLog")
|
|
104
123
|
.withIndex("byVaultEntryId", (q) => q.eq("vaultEntryId", args.vaultEntryId!))
|
|
105
124
|
.order("desc")
|
|
106
125
|
.take(limit);
|
|
107
126
|
}
|
|
108
|
-
return ctx.db.query("vaultAuditLog").order("desc").take(limit);
|
|
127
|
+
return await ctx.db.query("vaultAuditLog").order("desc").take(limit);
|
|
109
128
|
},
|
|
110
129
|
});
|
|
111
130
|
|
|
@@ -116,12 +135,13 @@ export const store = mutation({
|
|
|
116
135
|
name: v.string(),
|
|
117
136
|
category: v.string(),
|
|
118
137
|
provider: v.optional(v.string()),
|
|
119
|
-
value: v.string(),
|
|
138
|
+
value: v.string(),
|
|
120
139
|
userId: v.optional(v.string()),
|
|
121
140
|
expiresAt: v.optional(v.number()),
|
|
122
141
|
},
|
|
123
142
|
handler: async (ctx, args) => {
|
|
124
|
-
const
|
|
143
|
+
const key = getEncryptionKey();
|
|
144
|
+
const { encrypted, iv } = encodeSecret(args.value, key);
|
|
125
145
|
const masked = maskSecret(args.value);
|
|
126
146
|
const now = Date.now();
|
|
127
147
|
|
|
@@ -134,14 +154,12 @@ export const store = mutation({
|
|
|
134
154
|
maskedValue: masked,
|
|
135
155
|
isActive: true,
|
|
136
156
|
expiresAt: args.expiresAt,
|
|
137
|
-
lastAccessedAt: undefined,
|
|
138
157
|
accessCount: 0,
|
|
139
158
|
userId: args.userId,
|
|
140
159
|
createdAt: now,
|
|
141
160
|
updatedAt: now,
|
|
142
161
|
});
|
|
143
162
|
|
|
144
|
-
// Audit log
|
|
145
163
|
await ctx.db.insert("vaultAuditLog", {
|
|
146
164
|
vaultEntryId: id,
|
|
147
165
|
action: "created",
|
|
@@ -163,7 +181,8 @@ export const storeFromChat = mutation({
|
|
|
163
181
|
userId: v.optional(v.string()),
|
|
164
182
|
},
|
|
165
183
|
handler: async (ctx, args) => {
|
|
166
|
-
const
|
|
184
|
+
const key = getEncryptionKey();
|
|
185
|
+
const { encrypted, iv } = encodeSecret(args.value, key);
|
|
167
186
|
const masked = maskSecret(args.value);
|
|
168
187
|
const now = Date.now();
|
|
169
188
|
|
|
@@ -181,7 +200,6 @@ export const storeFromChat = mutation({
|
|
|
181
200
|
updatedAt: now,
|
|
182
201
|
});
|
|
183
202
|
|
|
184
|
-
// Audit log with auto_captured source
|
|
185
203
|
await ctx.db.insert("vaultAuditLog", {
|
|
186
204
|
vaultEntryId: id,
|
|
187
205
|
action: "auto_captured",
|
|
@@ -207,14 +225,15 @@ export const update = mutation({
|
|
|
207
225
|
const existing = await ctx.db.get(args.id);
|
|
208
226
|
if (!existing) throw new Error("Vault entry not found");
|
|
209
227
|
|
|
210
|
-
const updates:
|
|
228
|
+
const updates: Record<string, unknown> = { updatedAt: Date.now() };
|
|
211
229
|
|
|
212
230
|
if (args.name !== undefined) updates.name = args.name;
|
|
213
231
|
if (args.isActive !== undefined) updates.isActive = args.isActive;
|
|
214
232
|
if (args.expiresAt !== undefined) updates.expiresAt = args.expiresAt;
|
|
215
233
|
|
|
216
234
|
if (args.value) {
|
|
217
|
-
const
|
|
235
|
+
const key = getEncryptionKey();
|
|
236
|
+
const { encrypted, iv } = encodeSecret(args.value, key);
|
|
218
237
|
updates.encryptedValue = encrypted;
|
|
219
238
|
updates.iv = iv;
|
|
220
239
|
updates.maskedValue = maskSecret(args.value);
|
|
@@ -241,7 +260,6 @@ export const remove = mutation({
|
|
|
241
260
|
const existing = await ctx.db.get(args.id);
|
|
242
261
|
if (!existing) throw new Error("Vault entry not found");
|
|
243
262
|
|
|
244
|
-
// Log deletion before removing
|
|
245
263
|
await ctx.db.insert("vaultAuditLog", {
|
|
246
264
|
vaultEntryId: args.id,
|
|
247
265
|
action: "deleted",
|
|
@@ -254,8 +272,8 @@ export const remove = mutation({
|
|
|
254
272
|
},
|
|
255
273
|
});
|
|
256
274
|
|
|
257
|
-
// Retrieve decrypted value (
|
|
258
|
-
export const retrieveSecret =
|
|
275
|
+
// Internal-only: Retrieve decrypted value (only callable from other Convex functions)
|
|
276
|
+
export const retrieveSecret = internalMutation({
|
|
259
277
|
args: {
|
|
260
278
|
id: v.id("vault"),
|
|
261
279
|
userId: v.optional(v.string()),
|
|
@@ -265,13 +283,11 @@ export const retrieveSecret = mutation({
|
|
|
265
283
|
if (!entry) throw new Error("Vault entry not found");
|
|
266
284
|
if (!entry.isActive) throw new Error("Vault entry is disabled");
|
|
267
285
|
|
|
268
|
-
// Update access tracking
|
|
269
286
|
await ctx.db.patch(args.id, {
|
|
270
287
|
lastAccessedAt: Date.now(),
|
|
271
288
|
accessCount: entry.accessCount + 1,
|
|
272
289
|
});
|
|
273
290
|
|
|
274
|
-
// Audit log
|
|
275
291
|
await ctx.db.insert("vaultAuditLog", {
|
|
276
292
|
vaultEntryId: args.id,
|
|
277
293
|
action: "accessed",
|
|
@@ -280,14 +296,13 @@ export const retrieveSecret = mutation({
|
|
|
280
296
|
timestamp: Date.now(),
|
|
281
297
|
});
|
|
282
298
|
|
|
283
|
-
|
|
284
|
-
const decrypted = decodeSecret(entry.encryptedValue, entry.iv,
|
|
299
|
+
const key = getEncryptionKey();
|
|
300
|
+
const decrypted = decodeSecret(entry.encryptedValue, entry.iv, key);
|
|
285
301
|
return decrypted;
|
|
286
302
|
},
|
|
287
303
|
});
|
|
288
304
|
|
|
289
305
|
// ---- Secret Detection Utility ----
|
|
290
|
-
// This is exported for use by the chat message handler
|
|
291
306
|
|
|
292
307
|
export const detectSecrets = query({
|
|
293
308
|
args: { text: v.string() },
|
|
@@ -329,7 +344,7 @@ export const censorMessage = mutation({
|
|
|
329
344
|
},
|
|
330
345
|
handler: async (ctx, args) => {
|
|
331
346
|
let censoredText = args.text;
|
|
332
|
-
const storedSecrets: Array<{ name: string; masked: string; id:
|
|
347
|
+
const storedSecrets: Array<{ name: string; masked: string; id: unknown }> = [];
|
|
333
348
|
|
|
334
349
|
for (const { pattern, category, provider, name } of SECRET_PATTERNS) {
|
|
335
350
|
const regex = new RegExp(pattern, "g");
|
|
@@ -338,12 +353,11 @@ export const censorMessage = mutation({
|
|
|
338
353
|
const secretValue = match[0];
|
|
339
354
|
const masked = maskSecret(secretValue);
|
|
340
355
|
|
|
341
|
-
// Replace in censored text
|
|
342
356
|
censoredText = censoredText.replace(secretValue, `[REDACTED: ${masked}]`);
|
|
343
357
|
|
|
344
|
-
// Auto-store if enabled
|
|
345
358
|
if (args.autoStore !== false) {
|
|
346
|
-
const
|
|
359
|
+
const key = getEncryptionKey();
|
|
360
|
+
const { encrypted, iv } = encodeSecret(secretValue, key);
|
|
347
361
|
const now = Date.now();
|
|
348
362
|
|
|
349
363
|
const id = await ctx.db.insert("vault", {
|
package/package.json
CHANGED
|
@@ -11,10 +11,10 @@ export const list = query({
|
|
|
11
11
|
if (args.userId) {
|
|
12
12
|
return await ctx.db
|
|
13
13
|
.query("agents")
|
|
14
|
-
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
15
|
-
.
|
|
14
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
15
|
+
.collect();
|
|
16
16
|
}
|
|
17
|
-
return await ctx.db.query("agents").
|
|
17
|
+
return await ctx.db.query("agents").collect();
|
|
18
18
|
},
|
|
19
19
|
});
|
|
20
20
|
|
|
@@ -24,7 +24,7 @@ export const get = query({
|
|
|
24
24
|
handler: async (ctx, args) => {
|
|
25
25
|
return await ctx.db
|
|
26
26
|
.query("agents")
|
|
27
|
-
.withIndex("byAgentId", (q) => q.eq("id", args.id))
|
|
27
|
+
.withIndex("byAgentId", (q) => q.eq("id", args.id!))
|
|
28
28
|
.first();
|
|
29
29
|
},
|
|
30
30
|
});
|
|
@@ -35,11 +35,11 @@ export const listActive = query({
|
|
|
35
35
|
userId: v.optional(v.string()),
|
|
36
36
|
},
|
|
37
37
|
handler: async (ctx, args) => {
|
|
38
|
-
|
|
38
|
+
const activeQuery = ctx.db
|
|
39
39
|
.query("agents")
|
|
40
40
|
.withIndex("byIsActive", (q) => q.eq("isActive", true));
|
|
41
41
|
|
|
42
|
-
const agents = await
|
|
42
|
+
const agents = await activeQuery.collect();
|
|
43
43
|
|
|
44
44
|
if (args.userId) {
|
|
45
45
|
return agents.filter((agent) => agent.userId === args.userId);
|
|
@@ -117,7 +117,7 @@ export const remove = mutation({
|
|
|
117
117
|
handler: async (ctx, args) => {
|
|
118
118
|
const agent = await ctx.db
|
|
119
119
|
.query("agents")
|
|
120
|
-
.withIndex("byAgentId", (q) => q.eq("id", args.id))
|
|
120
|
+
.withIndex("byAgentId", (q) => q.eq("id", args.id!))
|
|
121
121
|
.first();
|
|
122
122
|
|
|
123
123
|
if (!agent) {
|
|
@@ -135,7 +135,7 @@ export const toggleActive = mutation({
|
|
|
135
135
|
handler: async (ctx, args) => {
|
|
136
136
|
const agent = await ctx.db
|
|
137
137
|
.query("agents")
|
|
138
|
-
.withIndex("byAgentId", (q) => q.eq("id", args.id))
|
|
138
|
+
.withIndex("byAgentId", (q) => q.eq("id", args.id!))
|
|
139
139
|
.first();
|
|
140
140
|
|
|
141
141
|
if (!agent) {
|
|
@@ -11,8 +11,8 @@ export const list = query({
|
|
|
11
11
|
if (args.provider) {
|
|
12
12
|
const keys = await ctx.db
|
|
13
13
|
.query("apiKeys")
|
|
14
|
-
.withIndex("byProvider", (q) => q.eq("provider", args.provider))
|
|
15
|
-
.
|
|
14
|
+
.withIndex("byProvider", (q) => q.eq("provider", args.provider!))
|
|
15
|
+
.collect();
|
|
16
16
|
|
|
17
17
|
if (args.userId) {
|
|
18
18
|
return keys.filter((k) => k.userId === args.userId);
|
|
@@ -23,11 +23,11 @@ export const list = query({
|
|
|
23
23
|
if (args.userId) {
|
|
24
24
|
return await ctx.db
|
|
25
25
|
.query("apiKeys")
|
|
26
|
-
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
27
|
-
.
|
|
26
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
27
|
+
.collect();
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
return await ctx.db.query("apiKeys").
|
|
30
|
+
return await ctx.db.query("apiKeys").collect();
|
|
31
31
|
},
|
|
32
32
|
});
|
|
33
33
|
|
|
@@ -48,8 +48,8 @@ export const getActiveForProvider = query({
|
|
|
48
48
|
handler: async (ctx, args) => {
|
|
49
49
|
const keys = await ctx.db
|
|
50
50
|
.query("apiKeys")
|
|
51
|
-
.withIndex("byProvider", (q) => q.eq("provider", args.provider))
|
|
52
|
-
.
|
|
51
|
+
.withIndex("byProvider", (q) => q.eq("provider", args.provider!))
|
|
52
|
+
.collect();
|
|
53
53
|
|
|
54
54
|
const activeKeys = keys.filter((k) => k.isActive);
|
|
55
55
|
|
|
@@ -12,8 +12,8 @@ export const list = query({
|
|
|
12
12
|
if (args.isEnabled !== undefined) {
|
|
13
13
|
const jobs = await ctx.db
|
|
14
14
|
.query("cronJobs")
|
|
15
|
-
.withIndex("byIsEnabled", (q) => q.eq("isEnabled", args.isEnabled))
|
|
16
|
-
.
|
|
15
|
+
.withIndex("byIsEnabled", (q) => q.eq("isEnabled", args.isEnabled!))
|
|
16
|
+
.collect();
|
|
17
17
|
|
|
18
18
|
if (args.userId) {
|
|
19
19
|
return jobs.filter((j) => j.userId === args.userId);
|
|
@@ -27,18 +27,18 @@ export const list = query({
|
|
|
27
27
|
if (args.agentId) {
|
|
28
28
|
return await ctx.db
|
|
29
29
|
.query("cronJobs")
|
|
30
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
31
|
-
.
|
|
30
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
31
|
+
.collect();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if (args.userId) {
|
|
35
35
|
return await ctx.db
|
|
36
36
|
.query("cronJobs")
|
|
37
|
-
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
38
|
-
.
|
|
37
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
38
|
+
.collect();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
return await ctx.db.query("cronJobs").
|
|
41
|
+
return await ctx.db.query("cronJobs").collect();
|
|
42
42
|
},
|
|
43
43
|
});
|
|
44
44
|
|
|
@@ -58,7 +58,7 @@ export const getDueJobs = query({
|
|
|
58
58
|
const jobs = await ctx.db
|
|
59
59
|
.query("cronJobs")
|
|
60
60
|
.withIndex("byNextRun")
|
|
61
|
-
.
|
|
61
|
+
.collect();
|
|
62
62
|
|
|
63
63
|
return jobs.filter((j) => j.isEnabled && j.nextRun && j.nextRun <= now);
|
|
64
64
|
},
|
|
@@ -162,8 +162,8 @@ export const remove = mutation({
|
|
|
162
162
|
// Delete all run history
|
|
163
163
|
const runs = await ctx.db
|
|
164
164
|
.query("cronJobRuns")
|
|
165
|
-
.withIndex("byCronJobId", (q) => q.eq("cronJobId", args.id))
|
|
166
|
-
.
|
|
165
|
+
.withIndex("byCronJobId", (q) => q.eq("cronJobId", args.id!))
|
|
166
|
+
.collect();
|
|
167
167
|
|
|
168
168
|
for (const run of runs) {
|
|
169
169
|
await ctx.db.delete(run._id);
|
|
@@ -209,8 +209,8 @@ export const getRunHistory = query({
|
|
|
209
209
|
handler: async (ctx, args) => {
|
|
210
210
|
const runs = await ctx.db
|
|
211
211
|
.query("cronJobRuns")
|
|
212
|
-
.withIndex("byCronJobId", (q) => q.eq("cronJobId", args.cronJobId))
|
|
213
|
-
.
|
|
212
|
+
.withIndex("byCronJobId", (q) => q.eq("cronJobId", args.cronJobId!))
|
|
213
|
+
.collect();
|
|
214
214
|
|
|
215
215
|
// Sort by startedAt descending
|
|
216
216
|
runs.sort((a, b) => b.startedAt - a.startedAt);
|
|
@@ -12,25 +12,25 @@ export const list = query({
|
|
|
12
12
|
if (args.folderId) {
|
|
13
13
|
return await ctx.db
|
|
14
14
|
.query("files")
|
|
15
|
-
.withIndex("byFolderId", (q) => q.eq("folderId", args.folderId))
|
|
16
|
-
.
|
|
15
|
+
.withIndex("byFolderId", (q) => q.eq("folderId", args.folderId!))
|
|
16
|
+
.collect();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
if (args.projectId) {
|
|
20
20
|
return await ctx.db
|
|
21
21
|
.query("files")
|
|
22
|
-
.withIndex("byProjectId", (q) => q.eq("projectId", args.projectId))
|
|
23
|
-
.
|
|
22
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.projectId!))
|
|
23
|
+
.collect();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
if (args.userId) {
|
|
27
27
|
return await ctx.db
|
|
28
28
|
.query("files")
|
|
29
|
-
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
30
|
-
.
|
|
29
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
30
|
+
.collect();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
return await ctx.db.query("files").
|
|
33
|
+
return await ctx.db.query("files").collect();
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -12,25 +12,25 @@ export const list = query({
|
|
|
12
12
|
if (args.projectId) {
|
|
13
13
|
return await ctx.db
|
|
14
14
|
.query("folders")
|
|
15
|
-
.withIndex("byProjectId", (q) => q.eq("projectId", args.projectId))
|
|
16
|
-
.
|
|
15
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.projectId!))
|
|
16
|
+
.collect();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
if (args.parentId) {
|
|
20
20
|
return await ctx.db
|
|
21
21
|
.query("folders")
|
|
22
|
-
.withIndex("byParentId", (q) => q.eq("parentId", args.parentId))
|
|
23
|
-
.
|
|
22
|
+
.withIndex("byParentId", (q) => q.eq("parentId", args.parentId!))
|
|
23
|
+
.collect();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
if (args.userId) {
|
|
27
27
|
return await ctx.db
|
|
28
28
|
.query("folders")
|
|
29
|
-
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
30
|
-
.
|
|
29
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
30
|
+
.collect();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
return await ctx.db.query("folders").
|
|
33
|
+
return await ctx.db.query("folders").collect();
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -85,8 +85,8 @@ export const remove = mutation({
|
|
|
85
85
|
// Delete all files in the folder
|
|
86
86
|
const files = await ctx.db
|
|
87
87
|
.query("files")
|
|
88
|
-
.withIndex("byFolderId", (q) => q.eq("folderId", args.id))
|
|
89
|
-
.
|
|
88
|
+
.withIndex("byFolderId", (q) => q.eq("folderId", args.id!))
|
|
89
|
+
.collect();
|
|
90
90
|
|
|
91
91
|
for (const file of files) {
|
|
92
92
|
await ctx.db.delete(file._id);
|
|
@@ -95,8 +95,8 @@ export const remove = mutation({
|
|
|
95
95
|
// Delete all subfolders recursively
|
|
96
96
|
const subfolders = await ctx.db
|
|
97
97
|
.query("folders")
|
|
98
|
-
.withIndex("byParentId", (q) => q.eq("parentId", args.id))
|
|
99
|
-
.
|
|
98
|
+
.withIndex("byParentId", (q) => q.eq("parentId", args.id!))
|
|
99
|
+
.collect();
|
|
100
100
|
|
|
101
101
|
for (const subfolder of subfolders) {
|
|
102
102
|
// Recursive delete (will be called via mutation)
|
|
@@ -37,8 +37,8 @@ export const get = query({
|
|
|
37
37
|
handler: async (ctx, args) => {
|
|
38
38
|
const heartbeats = await ctx.db
|
|
39
39
|
.query("heartbeats")
|
|
40
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
41
|
-
.
|
|
40
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
41
|
+
.collect();
|
|
42
42
|
|
|
43
43
|
if (args.threadId) {
|
|
44
44
|
return heartbeats.find((h) => h.threadId === args.threadId);
|
|
@@ -57,14 +57,14 @@ export const listActive = query({
|
|
|
57
57
|
const heartbeats = await ctx.db
|
|
58
58
|
.query("heartbeats")
|
|
59
59
|
.withIndex("byStatus", (q) => q.eq("status", "active"))
|
|
60
|
-
.
|
|
60
|
+
.collect();
|
|
61
61
|
|
|
62
62
|
if (args.userId) {
|
|
63
63
|
// Filter by userId if provided
|
|
64
64
|
const userAgents = await ctx.db
|
|
65
65
|
.query("agents")
|
|
66
|
-
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
67
|
-
.
|
|
66
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
67
|
+
.collect();
|
|
68
68
|
|
|
69
69
|
const userAgentIds = new Set(userAgents.map((a) => a.id));
|
|
70
70
|
return heartbeats.filter((h) => userAgentIds.has(h.agentId));
|
|
@@ -82,7 +82,7 @@ export const getDueForCheck = query({
|
|
|
82
82
|
const heartbeats = await ctx.db
|
|
83
83
|
.query("heartbeats")
|
|
84
84
|
.withIndex("byNextCheck")
|
|
85
|
-
.
|
|
85
|
+
.collect();
|
|
86
86
|
|
|
87
87
|
return heartbeats.filter((h) => h.nextCheck <= now && h.status === "active");
|
|
88
88
|
},
|
|
@@ -107,10 +107,10 @@ export const upsert = mutation({
|
|
|
107
107
|
// Check if heartbeat exists
|
|
108
108
|
const existing = await ctx.db
|
|
109
109
|
.query("heartbeats")
|
|
110
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
110
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
111
111
|
.filter((q) =>
|
|
112
112
|
args.threadId
|
|
113
|
-
? q.eq(q.field("threadId"), args.threadId)
|
|
113
|
+
? q.eq(q.field("threadId"), args.threadId!)
|
|
114
114
|
: q.eq(q.field("threadId"), undefined)
|
|
115
115
|
)
|
|
116
116
|
.first();
|
|
@@ -146,10 +146,10 @@ export const updateStatus = mutation({
|
|
|
146
146
|
handler: async (ctx, args) => {
|
|
147
147
|
const heartbeat = await ctx.db
|
|
148
148
|
.query("heartbeats")
|
|
149
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
149
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
150
150
|
.filter((q) =>
|
|
151
151
|
args.threadId
|
|
152
|
-
? q.eq(q.field("threadId"), args.threadId)
|
|
152
|
+
? q.eq(q.field("threadId"), args.threadId!)
|
|
153
153
|
: q.eq(q.field("threadId"), undefined)
|
|
154
154
|
)
|
|
155
155
|
.first();
|
|
@@ -178,10 +178,10 @@ export const addPendingTask = mutation({
|
|
|
178
178
|
handler: async (ctx, args) => {
|
|
179
179
|
const heartbeat = await ctx.db
|
|
180
180
|
.query("heartbeats")
|
|
181
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
181
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
182
182
|
.filter((q) =>
|
|
183
183
|
args.threadId
|
|
184
|
-
? q.eq(q.field("threadId"), args.threadId)
|
|
184
|
+
? q.eq(q.field("threadId"), args.threadId!)
|
|
185
185
|
: q.eq(q.field("threadId"), undefined)
|
|
186
186
|
)
|
|
187
187
|
.first();
|
|
@@ -210,10 +210,10 @@ export const removePendingTask = mutation({
|
|
|
210
210
|
handler: async (ctx, args) => {
|
|
211
211
|
const heartbeat = await ctx.db
|
|
212
212
|
.query("heartbeats")
|
|
213
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
213
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
214
214
|
.filter((q) =>
|
|
215
215
|
args.threadId
|
|
216
|
-
? q.eq(q.field("threadId"), args.threadId)
|
|
216
|
+
? q.eq(q.field("threadId"), args.threadId!)
|
|
217
217
|
: q.eq(q.field("threadId"), undefined)
|
|
218
218
|
)
|
|
219
219
|
.first();
|
|
@@ -242,10 +242,10 @@ export const updateContext = mutation({
|
|
|
242
242
|
handler: async (ctx, args) => {
|
|
243
243
|
const heartbeat = await ctx.db
|
|
244
244
|
.query("heartbeats")
|
|
245
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
245
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
246
246
|
.filter((q) =>
|
|
247
247
|
args.threadId
|
|
248
|
-
? q.eq(q.field("threadId"), args.threadId)
|
|
248
|
+
? q.eq(q.field("threadId"), args.threadId!)
|
|
249
249
|
: q.eq(q.field("threadId"), undefined)
|
|
250
250
|
)
|
|
251
251
|
.first();
|
|
@@ -353,10 +353,10 @@ export const remove = mutation({
|
|
|
353
353
|
handler: async (ctx, args) => {
|
|
354
354
|
const heartbeat = await ctx.db
|
|
355
355
|
.query("heartbeats")
|
|
356
|
-
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
356
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
357
357
|
.filter((q) =>
|
|
358
358
|
args.threadId
|
|
359
|
-
? q.eq(q.field("threadId"), args.threadId)
|
|
359
|
+
? q.eq(q.field("threadId"), args.threadId!)
|
|
360
360
|
: q.eq(q.field("threadId"), undefined)
|
|
361
361
|
)
|
|
362
362
|
.first();
|
|
@@ -54,6 +54,7 @@ export const executeAgent = action({
|
|
|
54
54
|
|
|
55
55
|
try {
|
|
56
56
|
// Import Mastra dynamically (Node.js runtime)
|
|
57
|
+
// @ts-expect-error - Mastra is installed at runtime in the user's project
|
|
57
58
|
const { Agent } = await import("@mastra/core/agent");
|
|
58
59
|
|
|
59
60
|
// Format model string for Mastra
|
|
@@ -83,7 +84,7 @@ export const executeAgent = action({
|
|
|
83
84
|
.join("\n");
|
|
84
85
|
|
|
85
86
|
// Execute agent
|
|
86
|
-
const result = await mastraAgent.generate(args.prompt, {
|
|
87
|
+
const result: any = await mastraAgent.generate(args.prompt, {
|
|
87
88
|
...(args.stream && { stream: args.stream }),
|
|
88
89
|
context: context || undefined,
|
|
89
90
|
});
|
|
@@ -11,8 +11,8 @@ export const list = query({
|
|
|
11
11
|
if (args.isEnabled !== undefined) {
|
|
12
12
|
const connections = await ctx.db
|
|
13
13
|
.query("mcpConnections")
|
|
14
|
-
.withIndex("byIsEnabled", (q) => q.eq("isEnabled", args.isEnabled))
|
|
15
|
-
.
|
|
14
|
+
.withIndex("byIsEnabled", (q) => q.eq("isEnabled", args.isEnabled!))
|
|
15
|
+
.collect();
|
|
16
16
|
|
|
17
17
|
if (args.userId) {
|
|
18
18
|
return connections.filter((c) => c.userId === args.userId);
|
|
@@ -23,11 +23,11 @@ export const list = query({
|
|
|
23
23
|
if (args.userId) {
|
|
24
24
|
return await ctx.db
|
|
25
25
|
.query("mcpConnections")
|
|
26
|
-
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
27
|
-
.
|
|
26
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
27
|
+
.collect();
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
return await ctx.db.query("mcpConnections").
|
|
30
|
+
return await ctx.db.query("mcpConnections").collect();
|
|
31
31
|
},
|
|
32
32
|
});
|
|
33
33
|
|
|
@@ -57,8 +57,8 @@ export const list = query({
|
|
|
57
57
|
handler: async (ctx, args) => {
|
|
58
58
|
const messages = await ctx.db
|
|
59
59
|
.query("messages")
|
|
60
|
-
.withIndex("
|
|
61
|
-
.
|
|
60
|
+
.withIndex("byThread", (q) => q.eq("threadId", args.threadId!))
|
|
61
|
+
.collect();
|
|
62
62
|
return messages;
|
|
63
63
|
},
|
|
64
64
|
});
|
|
@@ -78,8 +78,8 @@ export const clearThread = mutation({
|
|
|
78
78
|
handler: async (ctx, args) => {
|
|
79
79
|
const messages = await ctx.db
|
|
80
80
|
.query("messages")
|
|
81
|
-
.withIndex("
|
|
82
|
-
.
|
|
81
|
+
.withIndex("byThread", (q) => q.eq("threadId", args.threadId!))
|
|
82
|
+
.collect();
|
|
83
83
|
|
|
84
84
|
for (const message of messages) {
|
|
85
85
|
await ctx.db.delete(message._id);
|