@agentforge-ai/cli 0.4.2 → 0.5.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/dist/default/README.md +81 -81
- 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 +184 -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/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 +383 -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 +279 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/README.md +81 -81
- 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 +184 -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/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 +383 -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,224 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query, action } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: List cron jobs
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
userId: v.optional(v.string()),
|
|
8
|
+
agentId: v.optional(v.string()),
|
|
9
|
+
isEnabled: v.optional(v.boolean()),
|
|
10
|
+
},
|
|
11
|
+
handler: async (ctx, args) => {
|
|
12
|
+
if (args.isEnabled !== undefined) {
|
|
13
|
+
const jobs = await ctx.db
|
|
14
|
+
.query("cronJobs")
|
|
15
|
+
.withIndex("byIsEnabled", (q) => q.eq("isEnabled", args.isEnabled))
|
|
16
|
+
.take(100).collect();
|
|
17
|
+
|
|
18
|
+
if (args.userId) {
|
|
19
|
+
return jobs.filter((j) => j.userId === args.userId);
|
|
20
|
+
}
|
|
21
|
+
if (args.agentId) {
|
|
22
|
+
return jobs.filter((j) => j.agentId === args.agentId);
|
|
23
|
+
}
|
|
24
|
+
return jobs;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (args.agentId) {
|
|
28
|
+
return await ctx.db
|
|
29
|
+
.query("cronJobs")
|
|
30
|
+
.withIndex("byAgentId", (q) => q.eq("agentId", args.agentId))
|
|
31
|
+
.take(100).collect();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (args.userId) {
|
|
35
|
+
return await ctx.db
|
|
36
|
+
.query("cronJobs")
|
|
37
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
38
|
+
.take(100).collect();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return await ctx.db.query("cronJobs").take(100).collect();
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Query: Get cron job by ID
|
|
46
|
+
export const get = query({
|
|
47
|
+
args: { id: v.id("cronJobs") },
|
|
48
|
+
handler: async (ctx, args) => {
|
|
49
|
+
return await ctx.db.get(args.id);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Query: Get jobs due to run
|
|
54
|
+
export const getDueJobs = query({
|
|
55
|
+
args: {},
|
|
56
|
+
handler: async (ctx) => {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
const jobs = await ctx.db
|
|
59
|
+
.query("cronJobs")
|
|
60
|
+
.withIndex("byNextRun")
|
|
61
|
+
.take(100).collect();
|
|
62
|
+
|
|
63
|
+
return jobs.filter((j) => j.isEnabled && j.nextRun && j.nextRun <= now);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Mutation: Create cron job
|
|
68
|
+
export const create = mutation({
|
|
69
|
+
args: {
|
|
70
|
+
name: v.string(),
|
|
71
|
+
description: v.optional(v.string()),
|
|
72
|
+
schedule: v.string(),
|
|
73
|
+
agentId: v.string(),
|
|
74
|
+
prompt: v.string(),
|
|
75
|
+
userId: v.optional(v.string()),
|
|
76
|
+
metadata: v.optional(v.any()),
|
|
77
|
+
},
|
|
78
|
+
handler: async (ctx, args) => {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
|
|
81
|
+
// TODO: Parse cron expression to calculate nextRun
|
|
82
|
+
// For now, set it to 1 hour from now
|
|
83
|
+
const nextRun = now + 60 * 60 * 1000;
|
|
84
|
+
|
|
85
|
+
const jobId = await ctx.db.insert("cronJobs", {
|
|
86
|
+
...args,
|
|
87
|
+
isEnabled: true,
|
|
88
|
+
nextRun,
|
|
89
|
+
createdAt: now,
|
|
90
|
+
updatedAt: now,
|
|
91
|
+
});
|
|
92
|
+
return jobId;
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Mutation: Update cron job
|
|
97
|
+
export const update = mutation({
|
|
98
|
+
args: {
|
|
99
|
+
id: v.id("cronJobs"),
|
|
100
|
+
name: v.optional(v.string()),
|
|
101
|
+
description: v.optional(v.string()),
|
|
102
|
+
schedule: v.optional(v.string()),
|
|
103
|
+
prompt: v.optional(v.string()),
|
|
104
|
+
isEnabled: v.optional(v.boolean()),
|
|
105
|
+
metadata: v.optional(v.any()),
|
|
106
|
+
},
|
|
107
|
+
handler: async (ctx, args) => {
|
|
108
|
+
const { id, ...updates } = args;
|
|
109
|
+
|
|
110
|
+
// If schedule changed, recalculate nextRun
|
|
111
|
+
if (updates.schedule) {
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
(updates as any).nextRun = now + 60 * 60 * 1000; // TODO: Parse cron
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await ctx.db.patch(id, {
|
|
117
|
+
...updates,
|
|
118
|
+
updatedAt: Date.now(),
|
|
119
|
+
});
|
|
120
|
+
return id;
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Mutation: Toggle cron job enabled status
|
|
125
|
+
export const toggleEnabled = mutation({
|
|
126
|
+
args: { id: v.id("cronJobs") },
|
|
127
|
+
handler: async (ctx, args) => {
|
|
128
|
+
const job = await ctx.db.get(args.id);
|
|
129
|
+
|
|
130
|
+
if (!job) {
|
|
131
|
+
throw new Error(`Cron job not found`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await ctx.db.patch(args.id, {
|
|
135
|
+
isEnabled: !job.isEnabled,
|
|
136
|
+
updatedAt: Date.now(),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return { success: true, isEnabled: !job.isEnabled };
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Mutation: Update last run time
|
|
144
|
+
export const updateLastRun = mutation({
|
|
145
|
+
args: {
|
|
146
|
+
id: v.id("cronJobs"),
|
|
147
|
+
nextRun: v.number(),
|
|
148
|
+
},
|
|
149
|
+
handler: async (ctx, args) => {
|
|
150
|
+
await ctx.db.patch(args.id, {
|
|
151
|
+
lastRun: Date.now(),
|
|
152
|
+
nextRun: args.nextRun,
|
|
153
|
+
});
|
|
154
|
+
return args.id;
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Mutation: Delete cron job
|
|
159
|
+
export const remove = mutation({
|
|
160
|
+
args: { id: v.id("cronJobs") },
|
|
161
|
+
handler: async (ctx, args) => {
|
|
162
|
+
// Delete all run history
|
|
163
|
+
const runs = await ctx.db
|
|
164
|
+
.query("cronJobRuns")
|
|
165
|
+
.withIndex("byCronJobId", (q) => q.eq("cronJobId", args.id))
|
|
166
|
+
.take(100).collect();
|
|
167
|
+
|
|
168
|
+
for (const run of runs) {
|
|
169
|
+
await ctx.db.delete(run._id);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
await ctx.db.delete(args.id);
|
|
173
|
+
return { success: true };
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Mutation: Record cron job run
|
|
178
|
+
export const recordRun = mutation({
|
|
179
|
+
args: {
|
|
180
|
+
cronJobId: v.id("cronJobs"),
|
|
181
|
+
status: v.union(
|
|
182
|
+
v.literal("success"),
|
|
183
|
+
v.literal("failed"),
|
|
184
|
+
v.literal("running")
|
|
185
|
+
),
|
|
186
|
+
output: v.optional(v.string()),
|
|
187
|
+
error: v.optional(v.string()),
|
|
188
|
+
},
|
|
189
|
+
handler: async (ctx, args) => {
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
const runId = await ctx.db.insert("cronJobRuns", {
|
|
192
|
+
cronJobId: args.cronJobId,
|
|
193
|
+
status: args.status,
|
|
194
|
+
startedAt: now,
|
|
195
|
+
...(args.status !== "running" && { completedAt: now }),
|
|
196
|
+
...(args.output && { output: args.output }),
|
|
197
|
+
...(args.error && { error: args.error }),
|
|
198
|
+
});
|
|
199
|
+
return runId;
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Query: Get run history for a cron job
|
|
204
|
+
export const getRunHistory = query({
|
|
205
|
+
args: {
|
|
206
|
+
cronJobId: v.id("cronJobs"),
|
|
207
|
+
limit: v.optional(v.number()),
|
|
208
|
+
},
|
|
209
|
+
handler: async (ctx, args) => {
|
|
210
|
+
const runs = await ctx.db
|
|
211
|
+
.query("cronJobRuns")
|
|
212
|
+
.withIndex("byCronJobId", (q) => q.eq("cronJobId", args.cronJobId))
|
|
213
|
+
.take(100).collect();
|
|
214
|
+
|
|
215
|
+
// Sort by startedAt descending
|
|
216
|
+
runs.sort((a, b) => b.startedAt - a.startedAt);
|
|
217
|
+
|
|
218
|
+
if (args.limit) {
|
|
219
|
+
return runs.slice(0, args.limit);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return runs;
|
|
223
|
+
},
|
|
224
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: List files
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
folderId: v.optional(v.id("folders")),
|
|
8
|
+
projectId: v.optional(v.id("projects")),
|
|
9
|
+
userId: v.optional(v.string()),
|
|
10
|
+
},
|
|
11
|
+
handler: async (ctx, args) => {
|
|
12
|
+
if (args.folderId) {
|
|
13
|
+
return await ctx.db
|
|
14
|
+
.query("files")
|
|
15
|
+
.withIndex("byFolderId", (q) => q.eq("folderId", args.folderId))
|
|
16
|
+
.take(100).collect();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (args.projectId) {
|
|
20
|
+
return await ctx.db
|
|
21
|
+
.query("files")
|
|
22
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.projectId))
|
|
23
|
+
.take(100).collect();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (args.userId) {
|
|
27
|
+
return await ctx.db
|
|
28
|
+
.query("files")
|
|
29
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
30
|
+
.take(100).collect();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return await ctx.db.query("files").take(100).collect();
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Query: Get file by ID
|
|
38
|
+
export const get = query({
|
|
39
|
+
args: { id: v.id("files") },
|
|
40
|
+
handler: async (ctx, args) => {
|
|
41
|
+
return await ctx.db.get(args.id);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Mutation: Create file metadata (file stored in Cloudflare R2)
|
|
46
|
+
export const create = mutation({
|
|
47
|
+
args: {
|
|
48
|
+
name: v.string(),
|
|
49
|
+
originalName: v.string(),
|
|
50
|
+
mimeType: v.string(),
|
|
51
|
+
size: v.number(),
|
|
52
|
+
url: v.string(),
|
|
53
|
+
folderId: v.optional(v.id("folders")),
|
|
54
|
+
projectId: v.optional(v.id("projects")),
|
|
55
|
+
userId: v.optional(v.string()),
|
|
56
|
+
metadata: v.optional(v.any()),
|
|
57
|
+
},
|
|
58
|
+
handler: async (ctx, args) => {
|
|
59
|
+
const fileId = await ctx.db.insert("files", {
|
|
60
|
+
...args,
|
|
61
|
+
uploadedAt: Date.now(),
|
|
62
|
+
});
|
|
63
|
+
return fileId;
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Mutation: Update file metadata
|
|
68
|
+
export const update = mutation({
|
|
69
|
+
args: {
|
|
70
|
+
id: v.id("files"),
|
|
71
|
+
name: v.optional(v.string()),
|
|
72
|
+
folderId: v.optional(v.id("folders")),
|
|
73
|
+
metadata: v.optional(v.any()),
|
|
74
|
+
},
|
|
75
|
+
handler: async (ctx, args) => {
|
|
76
|
+
const { id, ...updates } = args;
|
|
77
|
+
await ctx.db.patch(id, updates);
|
|
78
|
+
return id;
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Mutation: Delete file
|
|
83
|
+
export const remove = mutation({
|
|
84
|
+
args: { id: v.id("files") },
|
|
85
|
+
handler: async (ctx, args) => {
|
|
86
|
+
await ctx.db.delete(args.id);
|
|
87
|
+
return { success: true };
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Mutation: Move file to folder
|
|
92
|
+
export const moveToFolder = mutation({
|
|
93
|
+
args: {
|
|
94
|
+
id: v.id("files"),
|
|
95
|
+
folderId: v.optional(v.id("folders")),
|
|
96
|
+
},
|
|
97
|
+
handler: async (ctx, args) => {
|
|
98
|
+
await ctx.db.patch(args.id, {
|
|
99
|
+
folderId: args.folderId,
|
|
100
|
+
});
|
|
101
|
+
return args.id;
|
|
102
|
+
},
|
|
103
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: List folders
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
parentId: v.optional(v.id("folders")),
|
|
8
|
+
projectId: v.optional(v.id("projects")),
|
|
9
|
+
userId: v.optional(v.string()),
|
|
10
|
+
},
|
|
11
|
+
handler: async (ctx, args) => {
|
|
12
|
+
if (args.projectId) {
|
|
13
|
+
return await ctx.db
|
|
14
|
+
.query("folders")
|
|
15
|
+
.withIndex("byProjectId", (q) => q.eq("projectId", args.projectId))
|
|
16
|
+
.take(100).collect();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (args.parentId) {
|
|
20
|
+
return await ctx.db
|
|
21
|
+
.query("folders")
|
|
22
|
+
.withIndex("byParentId", (q) => q.eq("parentId", args.parentId))
|
|
23
|
+
.take(100).collect();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (args.userId) {
|
|
27
|
+
return await ctx.db
|
|
28
|
+
.query("folders")
|
|
29
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
30
|
+
.take(100).collect();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return await ctx.db.query("folders").take(100).collect();
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Query: Get folder by ID
|
|
38
|
+
export const get = query({
|
|
39
|
+
args: { id: v.id("folders") },
|
|
40
|
+
handler: async (ctx, args) => {
|
|
41
|
+
return await ctx.db.get(args.id);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Mutation: Create folder
|
|
46
|
+
export const create = mutation({
|
|
47
|
+
args: {
|
|
48
|
+
name: v.string(),
|
|
49
|
+
parentId: v.optional(v.id("folders")),
|
|
50
|
+
projectId: v.optional(v.id("projects")),
|
|
51
|
+
userId: v.optional(v.string()),
|
|
52
|
+
},
|
|
53
|
+
handler: async (ctx, args) => {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const folderId = await ctx.db.insert("folders", {
|
|
56
|
+
...args,
|
|
57
|
+
createdAt: now,
|
|
58
|
+
updatedAt: now,
|
|
59
|
+
});
|
|
60
|
+
return folderId;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Mutation: Update folder
|
|
65
|
+
export const update = mutation({
|
|
66
|
+
args: {
|
|
67
|
+
id: v.id("folders"),
|
|
68
|
+
name: v.optional(v.string()),
|
|
69
|
+
parentId: v.optional(v.id("folders")),
|
|
70
|
+
},
|
|
71
|
+
handler: async (ctx, args) => {
|
|
72
|
+
const { id, ...updates } = args;
|
|
73
|
+
await ctx.db.patch(id, {
|
|
74
|
+
...updates,
|
|
75
|
+
updatedAt: Date.now(),
|
|
76
|
+
});
|
|
77
|
+
return id;
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Mutation: Delete folder
|
|
82
|
+
export const remove = mutation({
|
|
83
|
+
args: { id: v.id("folders") },
|
|
84
|
+
handler: async (ctx, args) => {
|
|
85
|
+
// Delete all files in the folder
|
|
86
|
+
const files = await ctx.db
|
|
87
|
+
.query("files")
|
|
88
|
+
.withIndex("byFolderId", (q) => q.eq("folderId", args.id))
|
|
89
|
+
.take(100).collect();
|
|
90
|
+
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
await ctx.db.delete(file._id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Delete all subfolders recursively
|
|
96
|
+
const subfolders = await ctx.db
|
|
97
|
+
.query("folders")
|
|
98
|
+
.withIndex("byParentId", (q) => q.eq("parentId", args.id))
|
|
99
|
+
.take(100).collect();
|
|
100
|
+
|
|
101
|
+
for (const subfolder of subfolders) {
|
|
102
|
+
// Recursive delete (will be called via mutation)
|
|
103
|
+
await ctx.db.delete(subfolder._id);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Delete the folder itself
|
|
107
|
+
await ctx.db.delete(args.id);
|
|
108
|
+
return { success: true };
|
|
109
|
+
},
|
|
110
|
+
});
|