@agentforge-ai/cli 0.4.3 → 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 +204 -0
- package/dist/default/convex/apiKeys.ts +133 -0
- package/dist/default/convex/cronJobs.ts +224 -0
- package/dist/default/convex/files.ts +103 -0
- package/dist/default/convex/folders.ts +110 -0
- package/dist/default/convex/heartbeat.ts +371 -0
- package/dist/default/convex/logs.ts +66 -0
- package/dist/default/convex/mastraIntegration.ts +185 -0
- package/dist/default/convex/mcpConnections.ts +127 -0
- package/dist/default/convex/messages.ts +90 -0
- package/dist/default/convex/projects.ts +114 -0
- package/dist/default/convex/schema.ts +150 -83
- package/dist/default/convex/sessions.ts +174 -0
- package/dist/default/convex/settings.ts +79 -0
- package/dist/default/convex/skills.ts +178 -0
- package/dist/default/convex/threads.ts +100 -0
- package/dist/default/convex/usage.ts +195 -0
- package/dist/default/convex/vault.ts +397 -0
- package/dist/default/dashboard/app/main.tsx +7 -3
- package/dist/default/dashboard/app/routes/agents.tsx +103 -161
- package/dist/default/dashboard/app/routes/chat.tsx +163 -317
- package/dist/default/dashboard/app/routes/connections.tsx +247 -386
- package/dist/default/dashboard/app/routes/cron.tsx +127 -286
- package/dist/default/dashboard/app/routes/files.tsx +184 -167
- package/dist/default/dashboard/app/routes/index.tsx +63 -96
- package/dist/default/dashboard/app/routes/projects.tsx +106 -225
- package/dist/default/dashboard/app/routes/sessions.tsx +87 -253
- package/dist/default/dashboard/app/routes/settings.tsx +316 -532
- package/dist/default/dashboard/app/routes/skills.tsx +329 -216
- package/dist/default/dashboard/app/routes/usage.tsx +107 -150
- package/dist/default/dashboard/tsconfig.json +3 -2
- package/dist/default/dashboard/vite.config.ts +6 -0
- package/dist/index.js +256 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/convex/agents.ts +204 -0
- package/templates/default/convex/apiKeys.ts +133 -0
- package/templates/default/convex/cronJobs.ts +224 -0
- package/templates/default/convex/files.ts +103 -0
- package/templates/default/convex/folders.ts +110 -0
- package/templates/default/convex/heartbeat.ts +371 -0
- package/templates/default/convex/logs.ts +66 -0
- package/templates/default/convex/mastraIntegration.ts +185 -0
- package/templates/default/convex/mcpConnections.ts +127 -0
- package/templates/default/convex/messages.ts +90 -0
- package/templates/default/convex/projects.ts +114 -0
- package/templates/default/convex/schema.ts +150 -83
- package/templates/default/convex/sessions.ts +174 -0
- package/templates/default/convex/settings.ts +79 -0
- package/templates/default/convex/skills.ts +178 -0
- package/templates/default/convex/threads.ts +100 -0
- package/templates/default/convex/usage.ts +195 -0
- package/templates/default/convex/vault.ts +397 -0
- package/templates/default/dashboard/app/main.tsx +7 -3
- package/templates/default/dashboard/app/routes/agents.tsx +103 -161
- package/templates/default/dashboard/app/routes/chat.tsx +163 -317
- package/templates/default/dashboard/app/routes/connections.tsx +247 -386
- package/templates/default/dashboard/app/routes/cron.tsx +127 -286
- package/templates/default/dashboard/app/routes/files.tsx +184 -167
- package/templates/default/dashboard/app/routes/index.tsx +63 -96
- package/templates/default/dashboard/app/routes/projects.tsx +106 -225
- package/templates/default/dashboard/app/routes/sessions.tsx +87 -253
- package/templates/default/dashboard/app/routes/settings.tsx +316 -532
- package/templates/default/dashboard/app/routes/skills.tsx +329 -216
- package/templates/default/dashboard/app/routes/usage.tsx +107 -150
- package/templates/default/dashboard/tsconfig.json +3 -2
- package/templates/default/dashboard/vite.config.ts +6 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { action } from "./_generated/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
import { api } from "./_generated/api";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mastra Integration Actions for Convex
|
|
7
|
+
*
|
|
8
|
+
* These actions run in the Node.js runtime and can use Mastra
|
|
9
|
+
* to execute agents and manage workflows.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Action: Execute agent with Mastra
|
|
13
|
+
export const executeAgent = action({
|
|
14
|
+
args: {
|
|
15
|
+
agentId: v.string(),
|
|
16
|
+
prompt: v.string(),
|
|
17
|
+
threadId: v.optional(v.id("threads")),
|
|
18
|
+
userId: v.optional(v.string()),
|
|
19
|
+
stream: v.optional(v.boolean()),
|
|
20
|
+
},
|
|
21
|
+
handler: async (ctx, args) => {
|
|
22
|
+
// Get agent configuration from database
|
|
23
|
+
const agent = await ctx.runQuery(api.agents.get, { id: args.agentId });
|
|
24
|
+
|
|
25
|
+
if (!agent) {
|
|
26
|
+
throw new Error(`Agent ${args.agentId} not found`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Create or get thread
|
|
30
|
+
let threadId = args.threadId;
|
|
31
|
+
if (!threadId) {
|
|
32
|
+
threadId = await ctx.runMutation(api.threads.create, {
|
|
33
|
+
agentId: args.agentId,
|
|
34
|
+
userId: args.userId,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Add user message to thread
|
|
39
|
+
await ctx.runMutation(api.messages.add, {
|
|
40
|
+
threadId,
|
|
41
|
+
role: "user",
|
|
42
|
+
content: args.prompt,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Create session
|
|
46
|
+
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
47
|
+
await ctx.runMutation(api.sessions.create, {
|
|
48
|
+
sessionId,
|
|
49
|
+
threadId,
|
|
50
|
+
agentId: args.agentId,
|
|
51
|
+
userId: args.userId,
|
|
52
|
+
channel: "dashboard",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Import Mastra dynamically (Node.js runtime)
|
|
57
|
+
// @ts-expect-error - Mastra is installed at runtime in the user's project
|
|
58
|
+
const { Agent } = await import("@mastra/core/agent");
|
|
59
|
+
|
|
60
|
+
// Format model string for Mastra
|
|
61
|
+
const modelString = agent.model.includes("/")
|
|
62
|
+
? agent.model
|
|
63
|
+
: `${agent.provider}/${agent.model}`;
|
|
64
|
+
|
|
65
|
+
// Create Mastra agent
|
|
66
|
+
const mastraAgent = new Agent({
|
|
67
|
+
id: agent.id,
|
|
68
|
+
name: agent.name,
|
|
69
|
+
instructions: agent.instructions,
|
|
70
|
+
model: modelString,
|
|
71
|
+
tools: agent.tools || {},
|
|
72
|
+
...(agent.temperature && { temperature: agent.temperature }),
|
|
73
|
+
...(agent.maxTokens && { maxTokens: agent.maxTokens }),
|
|
74
|
+
...(agent.topP && { topP: agent.topP }),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Get conversation history for context
|
|
78
|
+
const messages = await ctx.runQuery(api.messages.list, { threadId });
|
|
79
|
+
|
|
80
|
+
// Build context from message history
|
|
81
|
+
const context = messages
|
|
82
|
+
.slice(-10) // Last 10 messages for context
|
|
83
|
+
.map((m) => `${m.role}: ${m.content}`)
|
|
84
|
+
.join("\n");
|
|
85
|
+
|
|
86
|
+
// Execute agent
|
|
87
|
+
const result: any = await mastraAgent.generate(args.prompt, {
|
|
88
|
+
...(args.stream && { stream: args.stream }),
|
|
89
|
+
context: context || undefined,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Extract response content
|
|
93
|
+
const responseContent = typeof result === "string"
|
|
94
|
+
? result
|
|
95
|
+
: result.text || result.content || JSON.stringify(result);
|
|
96
|
+
|
|
97
|
+
// Add assistant message to thread
|
|
98
|
+
await ctx.runMutation(api.messages.add, {
|
|
99
|
+
threadId,
|
|
100
|
+
role: "assistant",
|
|
101
|
+
content: responseContent,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Update session status
|
|
105
|
+
await ctx.runMutation(api.sessions.updateStatus, {
|
|
106
|
+
sessionId,
|
|
107
|
+
status: "completed",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Record usage (if available in result)
|
|
111
|
+
if (result.usage) {
|
|
112
|
+
await ctx.runMutation(api.usage.record, {
|
|
113
|
+
agentId: args.agentId,
|
|
114
|
+
sessionId,
|
|
115
|
+
provider: agent.provider,
|
|
116
|
+
model: agent.model,
|
|
117
|
+
promptTokens: result.usage.promptTokens || 0,
|
|
118
|
+
completionTokens: result.usage.completionTokens || 0,
|
|
119
|
+
totalTokens: result.usage.totalTokens || 0,
|
|
120
|
+
cost: result.usage.cost,
|
|
121
|
+
userId: args.userId,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
threadId,
|
|
128
|
+
sessionId,
|
|
129
|
+
response: responseContent,
|
|
130
|
+
usage: result.usage,
|
|
131
|
+
};
|
|
132
|
+
} catch (error: any) {
|
|
133
|
+
// Update session status to error
|
|
134
|
+
await ctx.runMutation(api.sessions.updateStatus, {
|
|
135
|
+
sessionId,
|
|
136
|
+
status: "error",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Add error message
|
|
140
|
+
await ctx.runMutation(api.messages.add, {
|
|
141
|
+
threadId,
|
|
142
|
+
role: "assistant",
|
|
143
|
+
content: `Error: ${error.message}`,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Action: Stream agent response
|
|
152
|
+
export const streamAgent = action({
|
|
153
|
+
args: {
|
|
154
|
+
agentId: v.string(),
|
|
155
|
+
prompt: v.string(),
|
|
156
|
+
threadId: v.id("threads"),
|
|
157
|
+
userId: v.optional(v.string()),
|
|
158
|
+
},
|
|
159
|
+
handler: async (ctx, args) => {
|
|
160
|
+
// Similar to executeAgent but with streaming support
|
|
161
|
+
// This would require WebSocket or SSE implementation
|
|
162
|
+
// For now, return a placeholder
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
message: "Streaming support coming soon",
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Action: Execute workflow with multiple agents
|
|
171
|
+
export const executeWorkflow = action({
|
|
172
|
+
args: {
|
|
173
|
+
workflowId: v.string(),
|
|
174
|
+
input: v.any(),
|
|
175
|
+
userId: v.optional(v.string()),
|
|
176
|
+
},
|
|
177
|
+
handler: async (ctx, args) => {
|
|
178
|
+
// Placeholder for workflow execution
|
|
179
|
+
// This would orchestrate multiple agents in sequence or parallel
|
|
180
|
+
return {
|
|
181
|
+
success: true,
|
|
182
|
+
message: "Workflow execution coming soon",
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: List MCP connections
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
userId: v.optional(v.string()),
|
|
8
|
+
isEnabled: v.optional(v.boolean()),
|
|
9
|
+
},
|
|
10
|
+
handler: async (ctx, args) => {
|
|
11
|
+
if (args.isEnabled !== undefined) {
|
|
12
|
+
const connections = await ctx.db
|
|
13
|
+
.query("mcpConnections")
|
|
14
|
+
.withIndex("byIsEnabled", (q) => q.eq("isEnabled", args.isEnabled!))
|
|
15
|
+
.collect();
|
|
16
|
+
|
|
17
|
+
if (args.userId) {
|
|
18
|
+
return connections.filter((c) => c.userId === args.userId);
|
|
19
|
+
}
|
|
20
|
+
return connections;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (args.userId) {
|
|
24
|
+
return await ctx.db
|
|
25
|
+
.query("mcpConnections")
|
|
26
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
27
|
+
.collect();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return await ctx.db.query("mcpConnections").collect();
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Query: Get MCP connection by ID
|
|
35
|
+
export const get = query({
|
|
36
|
+
args: { id: v.id("mcpConnections") },
|
|
37
|
+
handler: async (ctx, args) => {
|
|
38
|
+
return await ctx.db.get(args.id);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Mutation: Create MCP connection
|
|
43
|
+
export const create = mutation({
|
|
44
|
+
args: {
|
|
45
|
+
name: v.string(),
|
|
46
|
+
serverUrl: v.string(),
|
|
47
|
+
protocol: v.string(),
|
|
48
|
+
credentials: v.optional(v.any()),
|
|
49
|
+
capabilities: v.optional(v.any()),
|
|
50
|
+
userId: v.optional(v.string()),
|
|
51
|
+
},
|
|
52
|
+
handler: async (ctx, args) => {
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
const connectionId = await ctx.db.insert("mcpConnections", {
|
|
55
|
+
...args,
|
|
56
|
+
isConnected: false,
|
|
57
|
+
isEnabled: true,
|
|
58
|
+
createdAt: now,
|
|
59
|
+
updatedAt: now,
|
|
60
|
+
});
|
|
61
|
+
return connectionId;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Mutation: Update MCP connection
|
|
66
|
+
export const update = mutation({
|
|
67
|
+
args: {
|
|
68
|
+
id: v.id("mcpConnections"),
|
|
69
|
+
name: v.optional(v.string()),
|
|
70
|
+
serverUrl: v.optional(v.string()),
|
|
71
|
+
credentials: v.optional(v.any()),
|
|
72
|
+
capabilities: v.optional(v.any()),
|
|
73
|
+
isEnabled: v.optional(v.boolean()),
|
|
74
|
+
},
|
|
75
|
+
handler: async (ctx, args) => {
|
|
76
|
+
const { id, ...updates } = args;
|
|
77
|
+
await ctx.db.patch(id, {
|
|
78
|
+
...updates,
|
|
79
|
+
updatedAt: Date.now(),
|
|
80
|
+
});
|
|
81
|
+
return id;
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Mutation: Update connection status
|
|
86
|
+
export const updateStatus = mutation({
|
|
87
|
+
args: {
|
|
88
|
+
id: v.id("mcpConnections"),
|
|
89
|
+
isConnected: v.boolean(),
|
|
90
|
+
},
|
|
91
|
+
handler: async (ctx, args) => {
|
|
92
|
+
await ctx.db.patch(args.id, {
|
|
93
|
+
isConnected: args.isConnected,
|
|
94
|
+
lastConnectedAt: args.isConnected ? Date.now() : undefined,
|
|
95
|
+
updatedAt: Date.now(),
|
|
96
|
+
});
|
|
97
|
+
return args.id;
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Mutation: Toggle MCP connection enabled status
|
|
102
|
+
export const toggleEnabled = mutation({
|
|
103
|
+
args: { id: v.id("mcpConnections") },
|
|
104
|
+
handler: async (ctx, args) => {
|
|
105
|
+
const connection = await ctx.db.get(args.id);
|
|
106
|
+
|
|
107
|
+
if (!connection) {
|
|
108
|
+
throw new Error(`MCP connection not found`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
await ctx.db.patch(args.id, {
|
|
112
|
+
isEnabled: !connection.isEnabled,
|
|
113
|
+
updatedAt: Date.now(),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return { success: true, isEnabled: !connection.isEnabled };
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Mutation: Delete MCP connection
|
|
121
|
+
export const remove = mutation({
|
|
122
|
+
args: { id: v.id("mcpConnections") },
|
|
123
|
+
handler: async (ctx, args) => {
|
|
124
|
+
await ctx.db.delete(args.id);
|
|
125
|
+
return { success: true };
|
|
126
|
+
},
|
|
127
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { mutation, query } from "./_generated/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
|
|
4
|
+
// Mutation: Add a message to a thread
|
|
5
|
+
export const add = mutation({
|
|
6
|
+
args: {
|
|
7
|
+
threadId: v.id("threads"),
|
|
8
|
+
role: v.union(
|
|
9
|
+
v.literal("user"),
|
|
10
|
+
v.literal("assistant"),
|
|
11
|
+
v.literal("system"),
|
|
12
|
+
v.literal("tool")
|
|
13
|
+
),
|
|
14
|
+
content: v.string(),
|
|
15
|
+
tool_calls: v.optional(v.any()),
|
|
16
|
+
},
|
|
17
|
+
handler: async (ctx, args) => {
|
|
18
|
+
const messageId = await ctx.db.insert("messages", {
|
|
19
|
+
...args,
|
|
20
|
+
createdAt: Date.now(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Update thread's updatedAt timestamp
|
|
24
|
+
await ctx.db.patch(args.threadId, {
|
|
25
|
+
updatedAt: Date.now(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return messageId;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Mutation: Create a message (alias for add)
|
|
33
|
+
export const create = mutation({
|
|
34
|
+
args: {
|
|
35
|
+
threadId: v.id("threads"),
|
|
36
|
+
role: v.union(
|
|
37
|
+
v.literal("user"),
|
|
38
|
+
v.literal("assistant"),
|
|
39
|
+
v.literal("system"),
|
|
40
|
+
v.literal("tool")
|
|
41
|
+
),
|
|
42
|
+
content: v.string(),
|
|
43
|
+
tool_calls: v.optional(v.any()),
|
|
44
|
+
},
|
|
45
|
+
handler: async (ctx, args) => {
|
|
46
|
+
const messageId = await ctx.db.insert("messages", {
|
|
47
|
+
...args,
|
|
48
|
+
createdAt: Date.now(),
|
|
49
|
+
});
|
|
50
|
+
return messageId;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Query: Get messages by thread
|
|
55
|
+
export const list = query({
|
|
56
|
+
args: { threadId: v.id("threads") },
|
|
57
|
+
handler: async (ctx, args) => {
|
|
58
|
+
const messages = await ctx.db
|
|
59
|
+
.query("messages")
|
|
60
|
+
.withIndex("byThread", (q) => q.eq("threadId", args.threadId!))
|
|
61
|
+
.collect();
|
|
62
|
+
return messages;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Mutation: Delete a message
|
|
67
|
+
export const remove = mutation({
|
|
68
|
+
args: { id: v.id("messages") },
|
|
69
|
+
handler: async (ctx, args) => {
|
|
70
|
+
await ctx.db.delete(args.id);
|
|
71
|
+
return { success: true };
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Mutation: Clear all messages in a thread
|
|
76
|
+
export const clearThread = mutation({
|
|
77
|
+
args: { threadId: v.id("threads") },
|
|
78
|
+
handler: async (ctx, args) => {
|
|
79
|
+
const messages = await ctx.db
|
|
80
|
+
.query("messages")
|
|
81
|
+
.withIndex("byThread", (q) => q.eq("threadId", args.threadId!))
|
|
82
|
+
.collect();
|
|
83
|
+
|
|
84
|
+
for (const message of messages) {
|
|
85
|
+
await ctx.db.delete(message._id);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { success: true, deleted: messages.length };
|
|
89
|
+
},
|
|
90
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: List projects
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
userId: v.optional(v.string()),
|
|
8
|
+
},
|
|
9
|
+
handler: async (ctx, args) => {
|
|
10
|
+
if (args.userId) {
|
|
11
|
+
return await ctx.db
|
|
12
|
+
.query("projects")
|
|
13
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
14
|
+
.collect();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return await ctx.db.query("projects").collect();
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Query: Get project by ID
|
|
22
|
+
export const get = query({
|
|
23
|
+
args: { id: v.id("projects") },
|
|
24
|
+
handler: async (ctx, args) => {
|
|
25
|
+
return await ctx.db.get(args.id);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Mutation: Create project
|
|
30
|
+
export const create = mutation({
|
|
31
|
+
args: {
|
|
32
|
+
name: v.string(),
|
|
33
|
+
description: v.optional(v.string()),
|
|
34
|
+
userId: v.optional(v.string()),
|
|
35
|
+
settings: v.optional(v.any()),
|
|
36
|
+
},
|
|
37
|
+
handler: async (ctx, args) => {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const projectId = await ctx.db.insert("projects", {
|
|
40
|
+
...args,
|
|
41
|
+
createdAt: now,
|
|
42
|
+
updatedAt: now,
|
|
43
|
+
});
|
|
44
|
+
return projectId;
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Mutation: Update project
|
|
49
|
+
export const update = mutation({
|
|
50
|
+
args: {
|
|
51
|
+
id: v.id("projects"),
|
|
52
|
+
name: v.optional(v.string()),
|
|
53
|
+
description: v.optional(v.string()),
|
|
54
|
+
settings: v.optional(v.any()),
|
|
55
|
+
},
|
|
56
|
+
handler: async (ctx, args) => {
|
|
57
|
+
const { id, ...updates } = args;
|
|
58
|
+
await ctx.db.patch(id, {
|
|
59
|
+
...updates,
|
|
60
|
+
updatedAt: Date.now(),
|
|
61
|
+
});
|
|
62
|
+
return id;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Mutation: Delete project
|
|
67
|
+
export const remove = mutation({
|
|
68
|
+
args: { id: v.id("projects") },
|
|
69
|
+
handler: async (ctx, args) => {
|
|
70
|
+
// Delete all threads in the project
|
|
71
|
+
const threads = await ctx.db
|
|
72
|
+
.query("threads")
|
|
73
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.id!))
|
|
74
|
+
.collect();
|
|
75
|
+
|
|
76
|
+
for (const thread of threads) {
|
|
77
|
+
// Delete messages in thread
|
|
78
|
+
const messages = await ctx.db
|
|
79
|
+
.query("messages")
|
|
80
|
+
.withIndex("byThread", (q) => q.eq("threadId", thread._id))
|
|
81
|
+
.collect();
|
|
82
|
+
|
|
83
|
+
for (const message of messages) {
|
|
84
|
+
await ctx.db.delete(message._id);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await ctx.db.delete(thread._id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Delete all files in the project
|
|
91
|
+
const files = await ctx.db
|
|
92
|
+
.query("files")
|
|
93
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.id!))
|
|
94
|
+
.collect();
|
|
95
|
+
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
await ctx.db.delete(file._id);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Delete all folders in the project
|
|
101
|
+
const folders = await ctx.db
|
|
102
|
+
.query("folders")
|
|
103
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.id!))
|
|
104
|
+
.collect();
|
|
105
|
+
|
|
106
|
+
for (const folder of folders) {
|
|
107
|
+
await ctx.db.delete(folder._id);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Delete the project itself
|
|
111
|
+
await ctx.db.delete(args.id);
|
|
112
|
+
return { success: true };
|
|
113
|
+
},
|
|
114
|
+
});
|