@superinterface/server 1.0.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.
Files changed (217) hide show
  1. package/README.md +36 -0
  2. package/eslint.config.mjs +31 -0
  3. package/next.config.ts +7 -0
  4. package/package.json +176 -0
  5. package/prisma/Account.prisma +18 -0
  6. package/prisma/ApiKey.prisma +13 -0
  7. package/prisma/Assistant.prisma +32 -0
  8. package/prisma/AssistantHandler.prisma +12 -0
  9. package/prisma/Avatar.prisma +10 -0
  10. package/prisma/ClientToolHandler.prisma +13 -0
  11. package/prisma/CodeInterpreterTool.prisma +7 -0
  12. package/prisma/ComputerUseTool.prisma +12 -0
  13. package/prisma/CreateTaskHandler.prisma +10 -0
  14. package/prisma/DeleteTaskHandler.prisma +10 -0
  15. package/prisma/FileSearchTool.prisma +9 -0
  16. package/prisma/FirecrawlHandler.prisma +13 -0
  17. package/prisma/Function.prisma +13 -0
  18. package/prisma/Handler.prisma +19 -0
  19. package/prisma/HttpTransport.prisma +12 -0
  20. package/prisma/IconAvatar.prisma +8 -0
  21. package/prisma/ImageAvatar.prisma +8 -0
  22. package/prisma/ImageGenerationTool.prisma +11 -0
  23. package/prisma/InitialMessage.prisma +17 -0
  24. package/prisma/Invitation.prisma +15 -0
  25. package/prisma/ListTasksHandler.prisma +10 -0
  26. package/prisma/Log.prisma +21 -0
  27. package/prisma/McpServer.prisma +14 -0
  28. package/prisma/Message.prisma +30 -0
  29. package/prisma/ModelProvider.prisma +15 -0
  30. package/prisma/Organization.prisma +10 -0
  31. package/prisma/OrganizationApiKey.prisma +12 -0
  32. package/prisma/OrganizationInvitation.prisma +15 -0
  33. package/prisma/OrganizationUserRole.prisma +14 -0
  34. package/prisma/ReplicateHandler.prisma +14 -0
  35. package/prisma/RequestHandler.prisma +15 -0
  36. package/prisma/Run.prisma +36 -0
  37. package/prisma/RunStep.prisma +30 -0
  38. package/prisma/Session.prisma +7 -0
  39. package/prisma/SseTransport.prisma +12 -0
  40. package/prisma/StdioTransport.prisma +11 -0
  41. package/prisma/Task.prisma +15 -0
  42. package/prisma/Thread.prisma +20 -0
  43. package/prisma/Tool.prisma +13 -0
  44. package/prisma/UpdateTaskHandler.prisma +10 -0
  45. package/prisma/User.prisma +16 -0
  46. package/prisma/UserRole.prisma +14 -0
  47. package/prisma/VerificationToken.prisma +7 -0
  48. package/prisma/WebSearchTool.prisma +7 -0
  49. package/prisma/Workspace.prisma +14 -0
  50. package/prisma/enums/ApiKeyType.prisma +4 -0
  51. package/prisma/enums/AvatarType.prisma +5 -0
  52. package/prisma/enums/ClientToolHandlerType.prisma +3 -0
  53. package/prisma/enums/ComputerUseToolEnvironment.prisma +6 -0
  54. package/prisma/enums/FirecrawlHandlerType.prisma +6 -0
  55. package/prisma/enums/HandlerType.prisma +11 -0
  56. package/prisma/enums/IconAvatarName.prisma +14 -0
  57. package/prisma/enums/ImageGenerationToolOutputFormat.prisma +5 -0
  58. package/prisma/enums/ImageGenerationToolQuality.prisma +6 -0
  59. package/prisma/enums/ImageGenerationToolSize.prisma +6 -0
  60. package/prisma/enums/LogLevel.prisma +5 -0
  61. package/prisma/enums/LogRequestMethod.prisma +7 -0
  62. package/prisma/enums/LogRequestRoute.prisma +6 -0
  63. package/prisma/enums/MessageRole.prisma +4 -0
  64. package/prisma/enums/MessageStatus.prisma +5 -0
  65. package/prisma/enums/MethodType.prisma +7 -0
  66. package/prisma/enums/ModelProviderType.prisma +13 -0
  67. package/prisma/enums/OrganizationUserRoleType.prisma +3 -0
  68. package/prisma/enums/ReplicateHandlerType.prisma +3 -0
  69. package/prisma/enums/RunStatus.prisma +10 -0
  70. package/prisma/enums/RunStepStatus.prisma +7 -0
  71. package/prisma/enums/RunStepType.prisma +4 -0
  72. package/prisma/enums/StorageProviderType.prisma +7 -0
  73. package/prisma/enums/ToolType.prisma +7 -0
  74. package/prisma/enums/TransportType.prisma +5 -0
  75. package/prisma/enums/TruncationType.prisma +5 -0
  76. package/prisma/enums/UserRoleType.prisma +3 -0
  77. package/prisma/migrations/20251006235143_initial_setup/migration.sql +986 -0
  78. package/prisma/migrations/20251007163926_add_truncation_type/migration.sql +6 -0
  79. package/prisma/migrations/20251007190703_add_organizations/migration.sql +97 -0
  80. package/prisma/migrations/migration_lock.toml +3 -0
  81. package/prisma/schema.prisma +13 -0
  82. package/prisma.config.ts +6 -0
  83. package/scripts/cli.ts +84 -0
  84. package/scripts/commands/organizations/api-keys/create.ts +159 -0
  85. package/scripts/commands/organizations/create.ts +82 -0
  86. package/scripts/utils/env.ts +31 -0
  87. package/scripts/utils/errors.ts +6 -0
  88. package/src/app/api/api-keys/[apiKeyId]/route.ts +178 -0
  89. package/src/app/api/api-keys/route.ts +147 -0
  90. package/src/app/api/assistants/[assistantId]/functions/[functionId]/route.ts +245 -0
  91. package/src/app/api/assistants/[assistantId]/functions/route.ts +157 -0
  92. package/src/app/api/assistants/[assistantId]/initial-messages/route.ts +127 -0
  93. package/src/app/api/assistants/[assistantId]/mcp-servers/[mcpServerId]/route.ts +243 -0
  94. package/src/app/api/assistants/[assistantId]/mcp-servers/route.ts +163 -0
  95. package/src/app/api/assistants/[assistantId]/route.ts +336 -0
  96. package/src/app/api/assistants/route.ts +196 -0
  97. package/src/app/api/files/[fileId]/contents/route.ts +145 -0
  98. package/src/app/api/files/route.ts +117 -0
  99. package/src/app/api/messages/lib/getWorkspaceId.ts +12 -0
  100. package/src/app/api/messages/lib/initialMessagesResponse.ts +90 -0
  101. package/src/app/api/messages/lib/serializeThread.ts +22 -0
  102. package/src/app/api/messages/route.ts +710 -0
  103. package/src/app/api/providers/[modelProviderId]/assistants/route.ts +68 -0
  104. package/src/app/api/providers/[modelProviderId]/models/route.ts +68 -0
  105. package/src/app/api/providers/[modelProviderId]/route.ts +202 -0
  106. package/src/app/api/providers/route.ts +105 -0
  107. package/src/app/api/tasks/[taskId]/route.ts +213 -0
  108. package/src/app/api/tasks/callback/route.ts +280 -0
  109. package/src/app/api/tasks/route.ts +121 -0
  110. package/src/app/api/threads/runs/submit-client-tool-outputs/route.ts +54 -0
  111. package/src/app/api/workspaces/[workspaceId]/route.ts +137 -0
  112. package/src/app/api/workspaces/route.ts +139 -0
  113. package/src/app/layout.tsx +9 -0
  114. package/src/app/page.tsx +3 -0
  115. package/src/lib/apiKeys/formatApiKeyName.ts +13 -0
  116. package/src/lib/apiKeys/getApiKey.ts +25 -0
  117. package/src/lib/apiKeys/serializeApiKey.ts +11 -0
  118. package/src/lib/apiKeys/workspaceAccessWhere.ts +21 -0
  119. package/src/lib/assistants/assistantClientAdapter/buildGetOpenaiAssistant.ts +96 -0
  120. package/src/lib/assistants/assistantClientAdapter/index.ts +165 -0
  121. package/src/lib/assistants/formatName.ts +9 -0
  122. package/src/lib/assistants/serializeApiAssistant.ts +40 -0
  123. package/src/lib/assistants/serializeAssistant.ts +31 -0
  124. package/src/lib/assistants/storageAssistantId.ts +29 -0
  125. package/src/lib/avatars/defaultAvatar.ts +15 -0
  126. package/src/lib/avatars/serializeAvatar.ts +26 -0
  127. package/src/lib/cache/cacheHeaders.ts +5 -0
  128. package/src/lib/computerCalls/handleComputerCall/index.ts +173 -0
  129. package/src/lib/errors/index.ts +10 -0
  130. package/src/lib/errors/serializeError.ts +11 -0
  131. package/src/lib/functions/createFunction.ts +32 -0
  132. package/src/lib/functions/functionSchema.ts +201 -0
  133. package/src/lib/functions/handleFunction/getFunction.ts +43 -0
  134. package/src/lib/functions/handleFunction/handleAssistant.ts +399 -0
  135. package/src/lib/functions/handleFunction/handleClientTool.ts +127 -0
  136. package/src/lib/functions/handleFunction/handleFirecrawl.ts +212 -0
  137. package/src/lib/functions/handleFunction/handleReplicate.ts +109 -0
  138. package/src/lib/functions/handleFunction/handleRequest.ts +272 -0
  139. package/src/lib/functions/handleFunction/index.ts +474 -0
  140. package/src/lib/functions/handleFunction/tasks/handleCreateTask.ts +58 -0
  141. package/src/lib/functions/handleFunction/tasks/handleDeleteTask.ts +62 -0
  142. package/src/lib/functions/handleFunction/tasks/handleListTasks.ts +53 -0
  143. package/src/lib/functions/handleFunction/tasks/handleUpdateTask.ts +70 -0
  144. package/src/lib/functions/interpolateFunctionValue.ts +26 -0
  145. package/src/lib/functions/serializeApiFunction.ts +32 -0
  146. package/src/lib/functions/updateFunction.ts +42 -0
  147. package/src/lib/handlers/handlerPrismaInput.tsx +141 -0
  148. package/src/lib/handlers/serializeApiHandler.ts +94 -0
  149. package/src/lib/iconAvatars/serializeIconAvatar.ts +9 -0
  150. package/src/lib/imageAvatars/serializeImageAvatar.ts +9 -0
  151. package/src/lib/initialMessages/schema.ts +11 -0
  152. package/src/lib/initialMessages/serializeApiInitialMessage.ts +15 -0
  153. package/src/lib/initialMessages/updateInitialMessages.ts +33 -0
  154. package/src/lib/logs/createLog.ts +17 -0
  155. package/src/lib/mcpServers/closeMcpConnection.ts +16 -0
  156. package/src/lib/mcpServers/connectMcpServer.ts +117 -0
  157. package/src/lib/mcpServers/getToolCallMcpServer.ts +62 -0
  158. package/src/lib/mcpServers/headers.ts +113 -0
  159. package/src/lib/mcpServers/mcpServerSchema.ts +77 -0
  160. package/src/lib/mcpServers/serializeApiMcpServer.ts +51 -0
  161. package/src/lib/mcpServers/url.ts +98 -0
  162. package/src/lib/messages/content.ts +60 -0
  163. package/src/lib/messages/textContent.ts +13 -0
  164. package/src/lib/metadata/serializeMetadata.ts +34 -0
  165. package/src/lib/misc/isJSON.ts +9 -0
  166. package/src/lib/misc/merge/customizer.ts +8 -0
  167. package/src/lib/misc/merge/index.ts +6 -0
  168. package/src/lib/modelProviders/buildAzureOpenaiClientAdapter.ts +33 -0
  169. package/src/lib/modelProviders/buildOpenaiClient.ts +14 -0
  170. package/src/lib/modelProviders/buildOpenaiClientAdapter.ts +30 -0
  171. package/src/lib/modelProviders/clientAdapter.ts +121 -0
  172. package/src/lib/modelProviders/isModelProviderValid.ts +23 -0
  173. package/src/lib/modelProviders/modelProviderConfigs.ts +221 -0
  174. package/src/lib/modelProviders/serializeModelProvider.ts +19 -0
  175. package/src/lib/models/getModels.ts +27 -0
  176. package/src/lib/models/serializeApiModel.ts +5 -0
  177. package/src/lib/openai/getOpenaiAssistants.ts +30 -0
  178. package/src/lib/organizationApiKeys/getOrganizationApiKey.ts +23 -0
  179. package/src/lib/prisma/index.ts +29 -0
  180. package/src/lib/redis/index.ts +3 -0
  181. package/src/lib/runs/createRunOpts.ts +80 -0
  182. package/src/lib/storageProviders/getStorageProviderAssistants.ts +19 -0
  183. package/src/lib/storageProviders/getStorageProviderType.ts +21 -0
  184. package/src/lib/storageProviders/isOpenaiAssistantsStorageProvider.ts +8 -0
  185. package/src/lib/storageProviders/isResponsesStorageProvider.ts +8 -0
  186. package/src/lib/storageProviders/openaiAssistantsStorageProviderTypes.ts +6 -0
  187. package/src/lib/storageProviders/responsesStorageProviderTypes.ts +6 -0
  188. package/src/lib/storageProviders/serializeApiStorageProviderAssistant.ts +14 -0
  189. package/src/lib/tasks/cancelScheduledTask.ts +13 -0
  190. package/src/lib/tasks/getNextOccurrence.ts +77 -0
  191. package/src/lib/tasks/getTaskToolKey.ts +49 -0
  192. package/src/lib/tasks/parseTaskToolArgs.ts +101 -0
  193. package/src/lib/tasks/scheduleSchema.ts +34 -0
  194. package/src/lib/tasks/scheduleTask.ts +37 -0
  195. package/src/lib/tasks/schemas.ts +25 -0
  196. package/src/lib/tasks/serializeTask.ts +14 -0
  197. package/src/lib/tasks/validateSchedule.ts +23 -0
  198. package/src/lib/themes/defaultTheme.ts +7 -0
  199. package/src/lib/themes/serializeAccentColor.ts +9 -0
  200. package/src/lib/themes/serializeTheme/index.ts +25 -0
  201. package/src/lib/themes/serializeTheme/serializeScaling.ts +14 -0
  202. package/src/lib/threads/createThread/index.ts +185 -0
  203. package/src/lib/threads/createThread/initialMessages.ts +36 -0
  204. package/src/lib/threads/managedOpenaiThreadId.ts +72 -0
  205. package/src/lib/threads/storageThreadId.ts +49 -0
  206. package/src/lib/threads/validThreadId.ts +10 -0
  207. package/src/lib/toolCalls/handleToolCall.ts +121 -0
  208. package/src/lib/toolCalls/messagesToOutput.ts +8 -0
  209. package/src/lib/toolCalls/streamOutput.ts +106 -0
  210. package/src/lib/tools/tools/index.ts +316 -0
  211. package/src/lib/upstash/qstash.ts +5 -0
  212. package/src/lib/upstash/upstashWorkflowClient.ts +5 -0
  213. package/src/lib/workspaces/serializeApiWorkspace.ts +12 -0
  214. package/src/types/index.ts +133 -0
  215. package/tsconfig.build.json +13 -0
  216. package/tsconfig.json +27 -0
  217. package/types/prisma.d.ts +4 -0
@@ -0,0 +1,280 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { verifySignatureAppRouter } from '@upstash/qstash/nextjs'
3
+ import { createMessageResponse } from '@superinterface/react/server'
4
+ import { LogRequestMethod, LogRequestRoute, LogLevel } from '@prisma/client'
5
+ import { prisma } from '@/lib/prisma'
6
+ import { assistantClientAdapter } from '@/lib/assistants/assistantClientAdapter'
7
+ import { createRunOpts } from '@/lib/runs/createRunOpts'
8
+ import { scheduleTask } from '@/lib/tasks/scheduleTask'
9
+ import { handleToolCall } from '@/lib/toolCalls/handleToolCall'
10
+ import { createLog } from '@/lib/logs/createLog'
11
+ import { map } from 'p-iteration'
12
+ import { storageThreadId as getStorageThreadId } from '@/lib/threads/storageThreadId'
13
+ import { redis } from '@/lib/redis'
14
+ import { isOpenaiAssistantsStorageProvider } from '@/lib/storageProviders/isOpenaiAssistantsStorageProvider'
15
+ import { managedOpenaiThreadId } from '@/lib/threads/managedOpenaiThreadId'
16
+ import { serializeMetadata } from '@/lib/metadata/serializeMetadata'
17
+
18
+ export const maxDuration = 800
19
+
20
+ const postHandler = async (request: Request) => {
21
+ const { taskId } = await request.json()
22
+ const task = await prisma.task.findUnique({
23
+ where: { id: taskId },
24
+ include: {
25
+ thread: {
26
+ include: {
27
+ assistant: {
28
+ include: {
29
+ modelProvider: true,
30
+ tools: {
31
+ include: {
32
+ fileSearchTool: true,
33
+ webSearchTool: true,
34
+ imageGenerationTool: true,
35
+ codeInterpreterTool: true,
36
+ computerUseTool: {
37
+ include: {
38
+ mcpServer: {
39
+ include: {
40
+ stdioTransport: true,
41
+ sseTransport: true,
42
+ httpTransport: true,
43
+ },
44
+ },
45
+ },
46
+ },
47
+ },
48
+ },
49
+ functions: {
50
+ include: {
51
+ handler: {
52
+ include: {
53
+ requestHandler: true,
54
+ firecrawlHandler: true,
55
+ replicateHandler: true,
56
+ clientToolHandler: true,
57
+ assistantHandler: true,
58
+ createTaskHandler: true,
59
+ listTasksHandler: true,
60
+ updateTaskHandler: true,
61
+ deleteTaskHandler: true,
62
+ },
63
+ },
64
+ },
65
+ },
66
+ mcpServers: {
67
+ include: {
68
+ computerUseTool: true,
69
+ stdioTransport: true,
70
+ sseTransport: true,
71
+ httpTransport: true,
72
+ },
73
+ },
74
+ },
75
+ },
76
+ },
77
+ },
78
+ },
79
+ })
80
+
81
+ if (!task) {
82
+ return NextResponse.json({ error: 'Task not found' }, { status: 400 })
83
+ }
84
+
85
+ const assistant = task.thread.assistant
86
+ const thread = task.thread
87
+ const client = assistantClientAdapter({ assistant, prisma })
88
+
89
+ let storageThreadId
90
+
91
+ try {
92
+ storageThreadId = getStorageThreadId({
93
+ thread,
94
+ })
95
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
96
+ } catch (error) {
97
+ createLog({
98
+ log: {
99
+ requestMethod: LogRequestMethod.POST,
100
+ requestRoute: LogRequestRoute.MESSAGES,
101
+ level: LogLevel.ERROR,
102
+ status: 500,
103
+ message: 'Failed to get storage thread id.',
104
+ workspaceId: assistant.workspaceId,
105
+ assistantId: assistant.id,
106
+ threadId: thread.id,
107
+ },
108
+ })
109
+
110
+ return NextResponse.json(
111
+ { error: 'Failed to get storage thread id.' },
112
+ { status: 500 },
113
+ )
114
+ }
115
+
116
+ if (
117
+ !storageThreadId &&
118
+ isOpenaiAssistantsStorageProvider({
119
+ storageProviderType: assistant.storageProviderType,
120
+ })
121
+ ) {
122
+ try {
123
+ storageThreadId = await managedOpenaiThreadId({
124
+ assistant,
125
+ threadId: thread.id,
126
+ })
127
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
+ } catch (error: any) {
129
+ createLog({
130
+ log: {
131
+ requestMethod: LogRequestMethod.POST,
132
+ requestRoute: LogRequestRoute.MESSAGES,
133
+ level: LogLevel.ERROR,
134
+ status: 500,
135
+ message: `Failed to get managed openai thread id: ${error.message}`,
136
+ workspaceId: assistant.workspaceId,
137
+ assistantId: assistant.id,
138
+ threadId: thread.id,
139
+ },
140
+ })
141
+
142
+ return NextResponse.json(
143
+ { error: 'Failed to get managed openai thread id.' },
144
+ { status: 500 },
145
+ )
146
+ }
147
+ }
148
+
149
+ if (!storageThreadId) {
150
+ createLog({
151
+ log: {
152
+ requestMethod: LogRequestMethod.POST,
153
+ requestRoute: LogRequestRoute.MESSAGES,
154
+ level: LogLevel.ERROR,
155
+ status: 500,
156
+ message: 'Invalid thread configuration.',
157
+ workspaceId: assistant.workspaceId,
158
+ assistantId: assistant.id,
159
+ threadId: thread.id,
160
+ },
161
+ })
162
+
163
+ return NextResponse.json(
164
+ { error: 'Invalid thread configuration.' },
165
+ { status: 500 },
166
+ )
167
+ }
168
+
169
+ try {
170
+ await client.beta.threads.messages.create(storageThreadId, {
171
+ role: 'user',
172
+ content: task.message,
173
+ metadata: serializeMetadata({
174
+ variables: {
175
+ superinterfaceCreatedByType: 'TASK',
176
+ superinterfaceCreatedById: task.id,
177
+ },
178
+ workspaceId: assistant.workspaceId,
179
+ }),
180
+ })
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ } catch (error: any) {
183
+ console.log({ error })
184
+ createLog({
185
+ log: {
186
+ requestMethod: LogRequestMethod.POST,
187
+ requestRoute: LogRequestRoute.MESSAGES,
188
+ level: LogLevel.ERROR,
189
+ status: 500,
190
+ message: `Failed to create message: ${error.message}`,
191
+ workspaceId: assistant.workspaceId,
192
+ assistantId: assistant.id,
193
+ threadId: thread.id,
194
+ },
195
+ })
196
+ return NextResponse.json(
197
+ { error: 'Failed to create message' },
198
+ { status: 500 },
199
+ )
200
+ }
201
+
202
+ let createRunStream
203
+ try {
204
+ createRunStream = await client.beta.threads.runs.create(
205
+ storageThreadId,
206
+ await createRunOpts({ assistant, thread }),
207
+ )
208
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
+ } catch (error: any) {
210
+ console.log({ error })
211
+ createLog({
212
+ log: {
213
+ requestMethod: LogRequestMethod.POST,
214
+ requestRoute: LogRequestRoute.MESSAGES,
215
+ level: LogLevel.ERROR,
216
+ status: 500,
217
+ message: `Failed to create run stream: ${error.message}`,
218
+ workspaceId: assistant.workspaceId,
219
+ assistantId: assistant.id,
220
+ threadId: thread.id,
221
+ },
222
+ })
223
+ return NextResponse.json(
224
+ { error: 'Failed to create run stream' },
225
+ { status: 500 },
226
+ )
227
+ }
228
+
229
+ const messageResponse = createMessageResponse({
230
+ client,
231
+ createRunStream,
232
+ handleToolCall: handleToolCall({ assistant, thread }),
233
+ })
234
+
235
+ const decoder = new TextDecoder()
236
+
237
+ const reader = messageResponse.getReader()
238
+ while (true) {
239
+ const { value, done } = await reader.read()
240
+ if (done) break
241
+
242
+ if (!value) continue
243
+
244
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
+ let event: any
246
+
247
+ try {
248
+ event = JSON.parse(decoder.decode(value))
249
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
250
+ } catch (e) {
251
+ continue
252
+ }
253
+
254
+ if (
255
+ event.event === 'thread.run.requires_action' &&
256
+ event.data?.required_action?.type === 'submit_client_tool_outputs'
257
+ ) {
258
+ await map(
259
+ event.data.required_action.submit_client_tool_outputs.tool_calls,
260
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
261
+ async (toolCall: any) => {
262
+ await redis.set(
263
+ `submit-client-tool-outputs:output:${toolCall.id}`,
264
+ 'Client tools cannot be used during tasks',
265
+ { ex: 60 * 60 * 24 * 7 },
266
+ )
267
+ },
268
+ )
269
+ }
270
+ }
271
+
272
+ await scheduleTask({ task })
273
+
274
+ return NextResponse.json({ ok: true })
275
+ }
276
+
277
+ export const POST =
278
+ process.env.NODE_ENV === 'test' || !process.env.QSTASH_CURRENT_SIGNING_KEY
279
+ ? postHandler
280
+ : verifySignatureAppRouter(postHandler)
@@ -0,0 +1,121 @@
1
+ import { headers } from 'next/headers'
2
+ import { ApiKeyType } from '@prisma/client'
3
+ import { NextResponse, type NextRequest } from 'next/server'
4
+ import { createTaskSchema } from '@/lib/tasks/schemas'
5
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
6
+ import { prisma } from '@/lib/prisma'
7
+ import { serializeTask } from '@/lib/tasks/serializeTask'
8
+ import { validateSchedule } from '@/lib/tasks/validateSchedule'
9
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
10
+ import { scheduleTask } from '@/lib/tasks/scheduleTask'
11
+
12
+ export const GET = async (request: NextRequest) => {
13
+ const headersList = await headers()
14
+ const authorization = headersList.get('authorization')
15
+ if (!authorization) {
16
+ return NextResponse.json(
17
+ { error: 'No authorization header found' },
18
+ { status: 400 },
19
+ )
20
+ }
21
+
22
+ const privateApiKey = await getApiKey({
23
+ authorization,
24
+ type: ApiKeyType.PRIVATE,
25
+ })
26
+
27
+ if (!privateApiKey) {
28
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
29
+ }
30
+
31
+ const key = request.nextUrl.searchParams.get('key')
32
+
33
+ const tasks = await prisma.task.findMany({
34
+ where: {
35
+ ...(key ? { key } : {}),
36
+ thread: { assistant: { workspaceId: privateApiKey.workspaceId } },
37
+ },
38
+ orderBy: { createdAt: 'desc' },
39
+ })
40
+
41
+ return NextResponse.json(
42
+ {
43
+ tasks: tasks.map((t) => serializeTask({ task: t })),
44
+ },
45
+ { headers: cacheHeaders },
46
+ )
47
+ }
48
+
49
+ export const POST = async (request: NextRequest) => {
50
+ const headersList = await headers()
51
+ const authorization = headersList.get('authorization')
52
+ if (!authorization) {
53
+ return NextResponse.json(
54
+ { error: 'No authorization header found' },
55
+ { status: 400 },
56
+ )
57
+ }
58
+
59
+ const privateApiKey = await getApiKey({
60
+ authorization,
61
+ type: ApiKeyType.PRIVATE,
62
+ })
63
+
64
+ if (!privateApiKey) {
65
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
66
+ }
67
+
68
+ const parsed = createTaskSchema.safeParse(await request.json())
69
+
70
+ if (!parsed.success) {
71
+ return NextResponse.json({ error: 'Invalid payload' }, { status: 400 })
72
+ }
73
+
74
+ const { title, message, schedule, threadId } = parsed.data
75
+
76
+ if (!validateSchedule(schedule)) {
77
+ return NextResponse.json({ error: 'Invalid schedule' }, { status: 400 })
78
+ }
79
+
80
+ const thread = await prisma.thread.findFirst({
81
+ where: {
82
+ id: threadId,
83
+ assistant: {
84
+ workspaceId: privateApiKey.workspaceId,
85
+ },
86
+ },
87
+ })
88
+
89
+ if (!thread) {
90
+ return NextResponse.json({ error: 'Thread not found' }, { status: 400 })
91
+ }
92
+
93
+ const task = await prisma.task.create({
94
+ data: {
95
+ title,
96
+ message,
97
+ schedule,
98
+ thread: {
99
+ connect: { id: thread.id },
100
+ },
101
+ key: parsed.data.key ?? '',
102
+ },
103
+ })
104
+
105
+ await scheduleTask({ task })
106
+
107
+ return NextResponse.json(
108
+ {
109
+ task: serializeTask({ task }),
110
+ },
111
+ { headers: cacheHeaders },
112
+ )
113
+ }
114
+
115
+ export const OPTIONS = () =>
116
+ NextResponse.json(
117
+ {},
118
+ {
119
+ headers: cacheHeaders,
120
+ },
121
+ )
@@ -0,0 +1,54 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { map } from 'p-iteration'
3
+ import { redis } from '@/lib/redis'
4
+ import { prisma } from '@/lib/prisma'
5
+ import { workspaceAccessWhere as getWorkspaceAccessWhere } from '@/lib/apiKeys/workspaceAccessWhere'
6
+
7
+ export async function POST(request: NextRequest) {
8
+ const body = await request.json()
9
+
10
+ const { assistantId, publicApiKey, toolOutputs } = body
11
+
12
+ const workspaceAccessWhere = await getWorkspaceAccessWhere({
13
+ publicApiKey,
14
+ })
15
+
16
+ if (!workspaceAccessWhere) {
17
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
18
+ }
19
+
20
+ if (!assistantId) {
21
+ return NextResponse.json(
22
+ { error: 'No assistant id found' },
23
+ { status: 400 },
24
+ )
25
+ }
26
+
27
+ const assistant = await prisma.assistant.findFirst({
28
+ where: {
29
+ id: assistantId,
30
+ workspace: workspaceAccessWhere,
31
+ },
32
+ })
33
+
34
+ if (!assistant) {
35
+ return NextResponse.json({ error: 'No assistant found' }, { status: 400 })
36
+ }
37
+
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+ await map(toolOutputs, async (toolOutput: any) => {
40
+ const isPending = await redis.get(
41
+ `submit-client-tool-outputs:pending:${toolOutput.toolCallId}`,
42
+ )
43
+
44
+ if (isPending) {
45
+ await redis.set(
46
+ `submit-client-tool-outputs:output:${toolOutput.toolCallId}`,
47
+ JSON.stringify(toolOutput.output),
48
+ { ex: 60 * 60 * 24 * 7 },
49
+ )
50
+ }
51
+ })
52
+
53
+ return NextResponse.json({ status: 'success' })
54
+ }
@@ -0,0 +1,137 @@
1
+ import { headers } from 'next/headers'
2
+ import { Prisma } from '@prisma/client'
3
+ import { NextResponse, type NextRequest } from 'next/server'
4
+ import { z } from 'zod'
5
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
6
+ import { prisma } from '@/lib/prisma'
7
+ import { validate } from 'uuid'
8
+ import { getOrganizationApiKey } from '@/lib/organizationApiKeys/getOrganizationApiKey'
9
+ import { serializeApiWorkspace } from '@/lib/workspaces/serializeApiWorkspace'
10
+
11
+ export const GET = async (
12
+ _request: NextRequest,
13
+ props: { params: Promise<{ workspaceId: string }> },
14
+ ) => {
15
+ const { workspaceId } = await props.params
16
+
17
+ const headersList = await headers()
18
+ const authorization = headersList.get('authorization')
19
+
20
+ if (!authorization) {
21
+ return NextResponse.json(
22
+ { error: 'No authorization header found' },
23
+ { status: 400 },
24
+ )
25
+ }
26
+
27
+ const organizationApiKey = await getOrganizationApiKey({
28
+ authorization,
29
+ })
30
+
31
+ if (!organizationApiKey) {
32
+ return NextResponse.json(
33
+ { error: 'Invalid organization api key' },
34
+ { status: 400 },
35
+ )
36
+ }
37
+
38
+ if (!validate(workspaceId)) {
39
+ return NextResponse.json({ error: 'Invalid workspace id' }, { status: 400 })
40
+ }
41
+
42
+ const workspace = await prisma.workspace.findFirst({
43
+ where: {
44
+ id: workspaceId,
45
+ organizationId: organizationApiKey.organizationId,
46
+ },
47
+ })
48
+
49
+ if (!workspace) {
50
+ return NextResponse.json({ error: 'No workspace found' }, { status: 400 })
51
+ }
52
+
53
+ return NextResponse.json(
54
+ {
55
+ workspace: serializeApiWorkspace({ workspace }),
56
+ },
57
+ { headers: cacheHeaders },
58
+ )
59
+ }
60
+
61
+ export const PATCH = async (
62
+ request: NextRequest,
63
+ props: { params: Promise<{ workspaceId: string }> },
64
+ ) => {
65
+ const { workspaceId } = await props.params
66
+
67
+ const headersList = await headers()
68
+ const authorization = headersList.get('authorization')
69
+ if (!authorization) {
70
+ return NextResponse.json(
71
+ { error: 'No authorization header found' },
72
+ { status: 400 },
73
+ )
74
+ }
75
+
76
+ const organizationApiKey = await getOrganizationApiKey({
77
+ authorization,
78
+ })
79
+
80
+ if (!organizationApiKey) {
81
+ return NextResponse.json(
82
+ { error: 'Invalid organization api key' },
83
+ { status: 400 },
84
+ )
85
+ }
86
+
87
+ if (!validate(workspaceId)) {
88
+ return NextResponse.json({ error: 'Invalid workspace id' }, { status: 400 })
89
+ }
90
+
91
+ const body = await request.json()
92
+ const schema = z.object({
93
+ name: z.string().optional(),
94
+ })
95
+
96
+ const parsed = schema.safeParse(body)
97
+
98
+ if (!parsed.success) {
99
+ return NextResponse.json({ error: 'Invalid payload' }, { status: 400 })
100
+ }
101
+
102
+ const { name } = parsed.data
103
+ const updateData: Prisma.WorkspaceUpdateInput = {
104
+ ...(name !== undefined ? { name } : {}),
105
+ }
106
+
107
+ const existingWorkspace = await prisma.workspace.findFirst({
108
+ where: {
109
+ id: workspaceId,
110
+ organizationId: organizationApiKey.organizationId,
111
+ },
112
+ })
113
+
114
+ if (!existingWorkspace) {
115
+ return NextResponse.json({ error: 'No workspace found' }, { status: 400 })
116
+ }
117
+
118
+ const workspace = await prisma.workspace.update({
119
+ where: { id: workspaceId },
120
+ data: updateData,
121
+ })
122
+
123
+ return NextResponse.json(
124
+ {
125
+ workspace: serializeApiWorkspace({ workspace }),
126
+ },
127
+ { headers: cacheHeaders },
128
+ )
129
+ }
130
+
131
+ export const OPTIONS = () =>
132
+ NextResponse.json(
133
+ {},
134
+ {
135
+ headers: cacheHeaders,
136
+ },
137
+ )