@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,178 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: List skills
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
userId: v.optional(v.string()),
|
|
8
|
+
category: v.optional(v.string()),
|
|
9
|
+
isInstalled: v.optional(v.boolean()),
|
|
10
|
+
},
|
|
11
|
+
handler: async (ctx, args) => {
|
|
12
|
+
if (args.category) {
|
|
13
|
+
const skills = await ctx.db
|
|
14
|
+
.query("skills")
|
|
15
|
+
.withIndex("byCategory", (q) => q.eq("category", args.category!))
|
|
16
|
+
.collect();
|
|
17
|
+
|
|
18
|
+
if (args.userId) {
|
|
19
|
+
return skills.filter((s) => s.userId === args.userId);
|
|
20
|
+
}
|
|
21
|
+
if (args.isInstalled !== undefined) {
|
|
22
|
+
return skills.filter((s) => s.isInstalled === args.isInstalled);
|
|
23
|
+
}
|
|
24
|
+
return skills;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (args.isInstalled !== undefined) {
|
|
28
|
+
const skills = await ctx.db
|
|
29
|
+
.query("skills")
|
|
30
|
+
.withIndex("byIsInstalled", (q) => q.eq("isInstalled", args.isInstalled!))
|
|
31
|
+
.collect();
|
|
32
|
+
|
|
33
|
+
if (args.userId) {
|
|
34
|
+
return skills.filter((s) => s.userId === args.userId);
|
|
35
|
+
}
|
|
36
|
+
return skills;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (args.userId) {
|
|
40
|
+
return await ctx.db
|
|
41
|
+
.query("skills")
|
|
42
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
43
|
+
.collect();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return await ctx.db.query("skills").collect();
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Query: Get skill by ID
|
|
51
|
+
export const get = query({
|
|
52
|
+
args: { id: v.id("skills") },
|
|
53
|
+
handler: async (ctx, args) => {
|
|
54
|
+
return await ctx.db.get(args.id);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Query: Get installed skills
|
|
59
|
+
export const listInstalled = query({
|
|
60
|
+
args: {
|
|
61
|
+
userId: v.optional(v.string()),
|
|
62
|
+
},
|
|
63
|
+
handler: async (ctx, args) => {
|
|
64
|
+
const skills = await ctx.db
|
|
65
|
+
.query("skills")
|
|
66
|
+
.withIndex("byIsInstalled", (q) => q.eq("isInstalled", true))
|
|
67
|
+
.collect();
|
|
68
|
+
|
|
69
|
+
if (args.userId) {
|
|
70
|
+
return skills.filter((s) => s.userId === args.userId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return skills;
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Mutation: Create skill
|
|
78
|
+
export const create = mutation({
|
|
79
|
+
args: {
|
|
80
|
+
name: v.string(),
|
|
81
|
+
displayName: v.string(),
|
|
82
|
+
description: v.string(),
|
|
83
|
+
category: v.string(),
|
|
84
|
+
version: v.string(),
|
|
85
|
+
author: v.optional(v.string()),
|
|
86
|
+
repository: v.optional(v.string()),
|
|
87
|
+
documentation: v.optional(v.string()),
|
|
88
|
+
code: v.string(),
|
|
89
|
+
schema: v.optional(v.any()),
|
|
90
|
+
userId: v.optional(v.string()),
|
|
91
|
+
},
|
|
92
|
+
handler: async (ctx, args) => {
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
const skillId = await ctx.db.insert("skills", {
|
|
95
|
+
...args,
|
|
96
|
+
isInstalled: false,
|
|
97
|
+
isEnabled: false,
|
|
98
|
+
createdAt: now,
|
|
99
|
+
updatedAt: now,
|
|
100
|
+
});
|
|
101
|
+
return skillId;
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Mutation: Install skill
|
|
106
|
+
export const install = mutation({
|
|
107
|
+
args: { id: v.id("skills") },
|
|
108
|
+
handler: async (ctx, args) => {
|
|
109
|
+
await ctx.db.patch(args.id, {
|
|
110
|
+
isInstalled: true,
|
|
111
|
+
isEnabled: true,
|
|
112
|
+
installedAt: Date.now(),
|
|
113
|
+
updatedAt: Date.now(),
|
|
114
|
+
});
|
|
115
|
+
return args.id;
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Mutation: Uninstall skill
|
|
120
|
+
export const uninstall = mutation({
|
|
121
|
+
args: { id: v.id("skills") },
|
|
122
|
+
handler: async (ctx, args) => {
|
|
123
|
+
await ctx.db.patch(args.id, {
|
|
124
|
+
isInstalled: false,
|
|
125
|
+
isEnabled: false,
|
|
126
|
+
updatedAt: Date.now(),
|
|
127
|
+
});
|
|
128
|
+
return args.id;
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Mutation: Toggle skill enabled status
|
|
133
|
+
export const toggleEnabled = mutation({
|
|
134
|
+
args: { id: v.id("skills") },
|
|
135
|
+
handler: async (ctx, args) => {
|
|
136
|
+
const skill = await ctx.db.get(args.id);
|
|
137
|
+
|
|
138
|
+
if (!skill) {
|
|
139
|
+
throw new Error(`Skill not found`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
await ctx.db.patch(args.id, {
|
|
143
|
+
isEnabled: !skill.isEnabled,
|
|
144
|
+
updatedAt: Date.now(),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return { success: true, isEnabled: !skill.isEnabled };
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Mutation: Update skill
|
|
152
|
+
export const update = mutation({
|
|
153
|
+
args: {
|
|
154
|
+
id: v.id("skills"),
|
|
155
|
+
displayName: v.optional(v.string()),
|
|
156
|
+
description: v.optional(v.string()),
|
|
157
|
+
version: v.optional(v.string()),
|
|
158
|
+
code: v.optional(v.string()),
|
|
159
|
+
schema: v.optional(v.any()),
|
|
160
|
+
},
|
|
161
|
+
handler: async (ctx, args) => {
|
|
162
|
+
const { id, ...updates } = args;
|
|
163
|
+
await ctx.db.patch(id, {
|
|
164
|
+
...updates,
|
|
165
|
+
updatedAt: Date.now(),
|
|
166
|
+
});
|
|
167
|
+
return id;
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Mutation: Delete skill
|
|
172
|
+
export const remove = mutation({
|
|
173
|
+
args: { id: v.id("skills") },
|
|
174
|
+
handler: async (ctx, args) => {
|
|
175
|
+
await ctx.db.delete(args.id);
|
|
176
|
+
return { success: true };
|
|
177
|
+
},
|
|
178
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: Get all threads
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
userId: v.optional(v.string()),
|
|
8
|
+
agentId: v.optional(v.string()),
|
|
9
|
+
projectId: v.optional(v.id("projects")),
|
|
10
|
+
},
|
|
11
|
+
handler: async (ctx, args) => {
|
|
12
|
+
if (args.projectId) {
|
|
13
|
+
return await ctx.db
|
|
14
|
+
.query("threads")
|
|
15
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.projectId!))
|
|
16
|
+
.collect();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (args.agentId) {
|
|
20
|
+
return await ctx.db
|
|
21
|
+
.query("threads")
|
|
22
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
23
|
+
.collect();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (args.userId) {
|
|
27
|
+
return await ctx.db
|
|
28
|
+
.query("threads")
|
|
29
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
30
|
+
.collect();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return await ctx.db.query("threads").collect();
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Query: Get thread by ID
|
|
38
|
+
export const get = query({
|
|
39
|
+
args: { id: v.id("threads") },
|
|
40
|
+
handler: async (ctx, args) => {
|
|
41
|
+
return await ctx.db.get(args.id);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Mutation: Create a new thread
|
|
46
|
+
export const create = mutation({
|
|
47
|
+
args: {
|
|
48
|
+
name: v.optional(v.string()),
|
|
49
|
+
agentId: v.string(),
|
|
50
|
+
userId: v.optional(v.string()),
|
|
51
|
+
projectId: v.optional(v.id("projects")),
|
|
52
|
+
metadata: v.optional(v.any()),
|
|
53
|
+
},
|
|
54
|
+
handler: async (ctx, args) => {
|
|
55
|
+
const now = Date.now();
|
|
56
|
+
const threadId = await ctx.db.insert("threads", {
|
|
57
|
+
...args,
|
|
58
|
+
createdAt: now,
|
|
59
|
+
updatedAt: now,
|
|
60
|
+
});
|
|
61
|
+
return threadId;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Mutation: Update thread
|
|
66
|
+
export const update = mutation({
|
|
67
|
+
args: {
|
|
68
|
+
id: v.id("threads"),
|
|
69
|
+
name: v.optional(v.string()),
|
|
70
|
+
metadata: v.optional(v.any()),
|
|
71
|
+
},
|
|
72
|
+
handler: async (ctx, args) => {
|
|
73
|
+
const { id, ...updates } = args;
|
|
74
|
+
await ctx.db.patch(id, {
|
|
75
|
+
...updates,
|
|
76
|
+
updatedAt: Date.now(),
|
|
77
|
+
});
|
|
78
|
+
return id;
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Mutation: Delete thread
|
|
83
|
+
export const remove = mutation({
|
|
84
|
+
args: { id: v.id("threads") },
|
|
85
|
+
handler: async (ctx, args) => {
|
|
86
|
+
// Delete all messages in the thread
|
|
87
|
+
const messages = await ctx.db
|
|
88
|
+
.query("messages")
|
|
89
|
+
.withIndex("byThread", (q) => q.eq("threadId", args.id!))
|
|
90
|
+
.collect();
|
|
91
|
+
|
|
92
|
+
for (const message of messages) {
|
|
93
|
+
await ctx.db.delete(message._id);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Delete the thread
|
|
97
|
+
await ctx.db.delete(args.id);
|
|
98
|
+
return { success: true };
|
|
99
|
+
},
|
|
100
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: Get usage records
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
userId: v.optional(v.string()),
|
|
8
|
+
agentId: v.optional(v.string()),
|
|
9
|
+
provider: v.optional(v.string()),
|
|
10
|
+
startTime: v.optional(v.number()),
|
|
11
|
+
endTime: v.optional(v.number()),
|
|
12
|
+
},
|
|
13
|
+
handler: async (ctx, args) => {
|
|
14
|
+
let records;
|
|
15
|
+
|
|
16
|
+
if (args.provider) {
|
|
17
|
+
records = await ctx.db
|
|
18
|
+
.query("usage")
|
|
19
|
+
.withIndex("byProvider", (q) => q.eq("provider", args.provider!))
|
|
20
|
+
.collect();
|
|
21
|
+
} else if (args.agentId) {
|
|
22
|
+
records = await ctx.db
|
|
23
|
+
.query("usage")
|
|
24
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
25
|
+
.collect();
|
|
26
|
+
} else if (args.userId) {
|
|
27
|
+
records = await ctx.db
|
|
28
|
+
.query("usage")
|
|
29
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
30
|
+
.collect();
|
|
31
|
+
} else {
|
|
32
|
+
records = await ctx.db.query("usage").collect();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Filter by time range if provided
|
|
36
|
+
if (args.startTime) {
|
|
37
|
+
records = records.filter((r) => r.timestamp >= args.startTime!);
|
|
38
|
+
}
|
|
39
|
+
if (args.endTime) {
|
|
40
|
+
records = records.filter((r) => r.timestamp <= args.endTime!);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return records;
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Query: Get usage statistics
|
|
48
|
+
export const getStats = query({
|
|
49
|
+
args: {
|
|
50
|
+
userId: v.optional(v.string()),
|
|
51
|
+
agentId: v.optional(v.string()),
|
|
52
|
+
startTime: v.optional(v.number()),
|
|
53
|
+
endTime: v.optional(v.number()),
|
|
54
|
+
},
|
|
55
|
+
handler: async (ctx, args) => {
|
|
56
|
+
let records;
|
|
57
|
+
|
|
58
|
+
if (args.agentId) {
|
|
59
|
+
records = await ctx.db
|
|
60
|
+
.query("usage")
|
|
61
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId!))
|
|
62
|
+
.collect();
|
|
63
|
+
} else if (args.userId) {
|
|
64
|
+
records = await ctx.db
|
|
65
|
+
.query("usage")
|
|
66
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
67
|
+
.collect();
|
|
68
|
+
} else {
|
|
69
|
+
records = await ctx.db.query("usage").collect();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Filter by time range
|
|
73
|
+
if (args.startTime) {
|
|
74
|
+
records = records.filter((r) => r.timestamp >= args.startTime!);
|
|
75
|
+
}
|
|
76
|
+
if (args.endTime) {
|
|
77
|
+
records = records.filter((r) => r.timestamp <= args.endTime!);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Calculate statistics
|
|
81
|
+
const totalTokens = records.reduce((sum, r) => sum + r.totalTokens, 0);
|
|
82
|
+
const totalCost = records.reduce((sum, r) => sum + (r.cost || 0), 0);
|
|
83
|
+
const totalRequests = records.length;
|
|
84
|
+
|
|
85
|
+
const byProvider: Record<string, { tokens: number; cost: number; requests: number }> = {};
|
|
86
|
+
const byModel: Record<string, { tokens: number; cost: number; requests: number }> = {};
|
|
87
|
+
|
|
88
|
+
for (const record of records) {
|
|
89
|
+
// By provider
|
|
90
|
+
if (!byProvider[record.provider]) {
|
|
91
|
+
byProvider[record.provider] = { tokens: 0, cost: 0, requests: 0 };
|
|
92
|
+
}
|
|
93
|
+
byProvider[record.provider].tokens += record.totalTokens;
|
|
94
|
+
byProvider[record.provider].cost += record.cost || 0;
|
|
95
|
+
byProvider[record.provider].requests += 1;
|
|
96
|
+
|
|
97
|
+
// By model
|
|
98
|
+
if (!byModel[record.model]) {
|
|
99
|
+
byModel[record.model] = { tokens: 0, cost: 0, requests: 0 };
|
|
100
|
+
}
|
|
101
|
+
byModel[record.model].tokens += record.totalTokens;
|
|
102
|
+
byModel[record.model].cost += record.cost || 0;
|
|
103
|
+
byModel[record.model].requests += 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
totalTokens,
|
|
108
|
+
totalCost,
|
|
109
|
+
totalRequests,
|
|
110
|
+
byProvider,
|
|
111
|
+
byModel,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Query: Get usage by time period
|
|
117
|
+
export const getByTimePeriod = query({
|
|
118
|
+
args: {
|
|
119
|
+
userId: v.optional(v.string()),
|
|
120
|
+
period: v.union(v.literal("day"), v.literal("week"), v.literal("month")),
|
|
121
|
+
},
|
|
122
|
+
handler: async (ctx, args) => {
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
let startTime: number;
|
|
125
|
+
|
|
126
|
+
switch (args.period) {
|
|
127
|
+
case "day":
|
|
128
|
+
startTime = now - 24 * 60 * 60 * 1000;
|
|
129
|
+
break;
|
|
130
|
+
case "week":
|
|
131
|
+
startTime = now - 7 * 24 * 60 * 60 * 1000;
|
|
132
|
+
break;
|
|
133
|
+
case "month":
|
|
134
|
+
startTime = now - 30 * 24 * 60 * 60 * 1000;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let records;
|
|
139
|
+
if (args.userId) {
|
|
140
|
+
records = await ctx.db
|
|
141
|
+
.query("usage")
|
|
142
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId!))
|
|
143
|
+
.collect();
|
|
144
|
+
} else {
|
|
145
|
+
records = await ctx.db.query("usage").collect();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
records = records.filter((r) => r.timestamp >= startTime);
|
|
149
|
+
|
|
150
|
+
return records;
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Mutation: Record usage
|
|
155
|
+
export const record = mutation({
|
|
156
|
+
args: {
|
|
157
|
+
agentId: v.string(),
|
|
158
|
+
sessionId: v.optional(v.string()),
|
|
159
|
+
provider: v.string(),
|
|
160
|
+
model: v.string(),
|
|
161
|
+
promptTokens: v.number(),
|
|
162
|
+
completionTokens: v.number(),
|
|
163
|
+
totalTokens: v.number(),
|
|
164
|
+
cost: v.optional(v.number()),
|
|
165
|
+
userId: v.optional(v.string()),
|
|
166
|
+
},
|
|
167
|
+
handler: async (ctx, args) => {
|
|
168
|
+
const usageId = await ctx.db.insert("usage", {
|
|
169
|
+
...args,
|
|
170
|
+
timestamp: Date.now(),
|
|
171
|
+
});
|
|
172
|
+
return usageId;
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Mutation: Delete old usage records
|
|
177
|
+
export const cleanup = mutation({
|
|
178
|
+
args: {
|
|
179
|
+
olderThan: v.number(), // Timestamp
|
|
180
|
+
},
|
|
181
|
+
handler: async (ctx, args) => {
|
|
182
|
+
const records = await ctx.db
|
|
183
|
+
.query("usage")
|
|
184
|
+
.withIndex("byTimestamp")
|
|
185
|
+
.collect();
|
|
186
|
+
|
|
187
|
+
const toDelete = records.filter((r) => r.timestamp < args.olderThan);
|
|
188
|
+
|
|
189
|
+
for (const record of toDelete) {
|
|
190
|
+
await ctx.db.delete(record._id);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { deleted: toDelete.length };
|
|
194
|
+
},
|
|
195
|
+
});
|