@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,68 @@
1
+ import { headers } from 'next/headers'
2
+ import { ApiKeyType } from '@prisma/client'
3
+ import { NextResponse, type NextRequest } from 'next/server'
4
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
5
+ import { prisma } from '@/lib/prisma'
6
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
7
+ import { getStorageProviderAssistants } from '@/lib/storageProviders/getStorageProviderAssistants'
8
+ import { serializeApiStorageProviderAssistant } from '@/lib/storageProviders/serializeApiStorageProviderAssistant'
9
+
10
+ export const GET = async (
11
+ _request: NextRequest,
12
+ props: { params: Promise<{ modelProviderId: string }> },
13
+ ) => {
14
+ const { modelProviderId } = await props.params
15
+
16
+ const headersList = await headers()
17
+ const authorization = headersList.get('authorization')
18
+ if (!authorization) {
19
+ return NextResponse.json(
20
+ { error: 'No authorization header found' },
21
+ { status: 400 },
22
+ )
23
+ }
24
+
25
+ const privateApiKey = await getApiKey({
26
+ type: ApiKeyType.PRIVATE,
27
+ authorization,
28
+ })
29
+
30
+ if (!privateApiKey) {
31
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
32
+ }
33
+
34
+ const provider = await prisma.modelProvider.findUnique({
35
+ where: {
36
+ id: modelProviderId,
37
+ workspaceId: privateApiKey.workspaceId,
38
+ },
39
+ })
40
+
41
+ if (!provider) {
42
+ return NextResponse.json({ error: 'No provider found' }, { status: 400 })
43
+ }
44
+
45
+ const storageProviderAssistants = await getStorageProviderAssistants({
46
+ modelProvider: provider,
47
+ })
48
+
49
+ return NextResponse.json(
50
+ {
51
+ storageProviderAssistants: storageProviderAssistants.map(
52
+ (storageProviderAssistant) =>
53
+ serializeApiStorageProviderAssistant({
54
+ storageProviderAssistant,
55
+ }),
56
+ ),
57
+ },
58
+ { headers: cacheHeaders },
59
+ )
60
+ }
61
+
62
+ export const OPTIONS = () =>
63
+ NextResponse.json(
64
+ {},
65
+ {
66
+ headers: cacheHeaders,
67
+ },
68
+ )
@@ -0,0 +1,68 @@
1
+ import { headers } from 'next/headers'
2
+ import { ApiKeyType } from '@prisma/client'
3
+ import { NextResponse, type NextRequest } from 'next/server'
4
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
5
+ import { prisma } from '@/lib/prisma'
6
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
7
+ import { getModels } from '@/lib/models/getModels'
8
+ import { serializeApiModel } from '@/lib/models/serializeApiModel'
9
+
10
+ export const GET = async (
11
+ _request: NextRequest,
12
+ props: { params: Promise<{ modelProviderId: string }> },
13
+ ) => {
14
+ const { modelProviderId } = await props.params
15
+
16
+ const headersList = await headers()
17
+ const authorization = headersList.get('authorization')
18
+
19
+ if (!authorization) {
20
+ return NextResponse.json(
21
+ { error: 'No authorization header found' },
22
+ { status: 400 },
23
+ )
24
+ }
25
+
26
+ const privateApiKey = await getApiKey({
27
+ type: ApiKeyType.PRIVATE,
28
+ authorization,
29
+ })
30
+
31
+ if (!privateApiKey) {
32
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
33
+ }
34
+
35
+ const provider = await prisma.modelProvider.findUnique({
36
+ where: {
37
+ id: modelProviderId,
38
+ workspaceId: privateApiKey.workspaceId,
39
+ },
40
+ })
41
+
42
+ if (!provider) {
43
+ return NextResponse.json({ error: 'No provider found' }, { status: 400 })
44
+ }
45
+
46
+ const models = await getModels({
47
+ modelProvider: provider,
48
+ })
49
+
50
+ return NextResponse.json(
51
+ {
52
+ models: models.map((model) =>
53
+ serializeApiModel({
54
+ model,
55
+ }),
56
+ ),
57
+ },
58
+ { headers: cacheHeaders },
59
+ )
60
+ }
61
+
62
+ export const OPTIONS = () =>
63
+ NextResponse.json(
64
+ {},
65
+ {
66
+ headers: cacheHeaders,
67
+ },
68
+ )
@@ -0,0 +1,202 @@
1
+ import { headers } from 'next/headers'
2
+ import { ApiKeyType, ModelProviderType, 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 { serializeModelProvider } from '@/lib/modelProviders/serializeModelProvider'
8
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
9
+ import { validate } from 'uuid'
10
+
11
+ export const GET = async (
12
+ _request: NextRequest,
13
+ props: { params: Promise<{ modelProviderId: string }> },
14
+ ) => {
15
+ const { modelProviderId } = await props.params
16
+
17
+ const headersList = await headers()
18
+ const authorization = headersList.get('authorization')
19
+ if (!authorization) {
20
+ return NextResponse.json(
21
+ { error: 'No authorization header found' },
22
+ { status: 400 },
23
+ )
24
+ }
25
+
26
+ const privateApiKey = await getApiKey({
27
+ type: ApiKeyType.PRIVATE,
28
+ authorization,
29
+ })
30
+
31
+ if (!privateApiKey) {
32
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
33
+ }
34
+
35
+ if (!validate(modelProviderId)) {
36
+ return NextResponse.json({ error: 'Invalid provider id' }, { status: 400 })
37
+ }
38
+
39
+ const provider = await prisma.modelProvider.findFirst({
40
+ where: {
41
+ id: modelProviderId,
42
+ workspaceId: privateApiKey.workspaceId,
43
+ },
44
+ })
45
+
46
+ if (!provider) {
47
+ return NextResponse.json({ error: 'No provider found' }, { status: 400 })
48
+ }
49
+
50
+ return NextResponse.json(
51
+ {
52
+ provider: serializeModelProvider({ provider }),
53
+ },
54
+ { headers: cacheHeaders },
55
+ )
56
+ }
57
+
58
+ export const PATCH = async (
59
+ request: NextRequest,
60
+ props: { params: Promise<{ modelProviderId: string }> },
61
+ ) => {
62
+ const { modelProviderId } = await props.params
63
+
64
+ const headersList = await headers()
65
+ const authorization = headersList.get('authorization')
66
+ if (!authorization) {
67
+ return NextResponse.json(
68
+ { error: 'No authorization header found' },
69
+ { status: 400 },
70
+ )
71
+ }
72
+
73
+ const privateApiKey = await getApiKey({
74
+ authorization,
75
+ type: ApiKeyType.PRIVATE,
76
+ })
77
+
78
+ if (!privateApiKey) {
79
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
80
+ }
81
+
82
+ if (!validate(modelProviderId)) {
83
+ return NextResponse.json({ error: 'Invalid provider id' }, { status: 400 })
84
+ }
85
+
86
+ const body = await request.json()
87
+ const schema = z.object({
88
+ type: z.nativeEnum(ModelProviderType).optional(),
89
+ name: z.string().optional(),
90
+ apiKey: z.string().optional(),
91
+ endpoint: z.string().optional().nullable(),
92
+ apiVersion: z.string().optional().nullable(),
93
+ })
94
+
95
+ const parsed = schema.safeParse(body)
96
+
97
+ if (!parsed.success) {
98
+ return NextResponse.json({ error: 'Invalid payload' }, { status: 400 })
99
+ }
100
+
101
+ const { type, apiKey: providerKey, endpoint, apiVersion, name } = parsed.data
102
+ const updateData: Prisma.ModelProviderUpdateInput = {
103
+ ...(type ? { type } : {}),
104
+ ...(name !== undefined ? { name } : {}),
105
+ ...(providerKey !== undefined ? { apiKey: providerKey } : {}),
106
+ ...(endpoint !== undefined ? { endpoint } : {}),
107
+ ...(apiVersion !== undefined ? { apiVersion } : {}),
108
+ }
109
+
110
+ const existingProvider = await prisma.modelProvider.findFirst({
111
+ where: {
112
+ id: modelProviderId,
113
+ workspaceId: privateApiKey.workspaceId,
114
+ },
115
+ })
116
+
117
+ if (!existingProvider) {
118
+ return NextResponse.json({ error: 'No provider found' }, { status: 400 })
119
+ }
120
+
121
+ const provider = await prisma.modelProvider.update({
122
+ where: { id: modelProviderId },
123
+ data: updateData,
124
+ })
125
+
126
+ return NextResponse.json(
127
+ {
128
+ provider: serializeModelProvider({ provider }),
129
+ },
130
+ { headers: cacheHeaders },
131
+ )
132
+ }
133
+
134
+ export const DELETE = async (
135
+ _request: NextRequest,
136
+ props: { params: Promise<{ modelProviderId: string }> },
137
+ ) => {
138
+ const { modelProviderId } = await props.params
139
+
140
+ const headersList = await headers()
141
+ const authorization = headersList.get('authorization')
142
+ if (!authorization) {
143
+ return NextResponse.json(
144
+ { error: 'No authorization header found' },
145
+ { status: 400 },
146
+ )
147
+ }
148
+
149
+ const privateApiKey = await getApiKey({
150
+ authorization,
151
+ type: ApiKeyType.PRIVATE,
152
+ })
153
+
154
+ if (!privateApiKey) {
155
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
156
+ }
157
+
158
+ if (!validate(modelProviderId)) {
159
+ return NextResponse.json({ error: 'Invalid provider id' }, { status: 400 })
160
+ }
161
+
162
+ const assistantsUsingProvider = await prisma.assistant.count({
163
+ where: {
164
+ modelProviderId: modelProviderId,
165
+ workspaceId: privateApiKey.workspaceId,
166
+ },
167
+ })
168
+
169
+ if (assistantsUsingProvider > 0) {
170
+ return NextResponse.json({ error: 'Provider in use' }, { status: 400 })
171
+ }
172
+
173
+ const existingProvider = await prisma.modelProvider.findFirst({
174
+ where: {
175
+ id: modelProviderId,
176
+ workspaceId: privateApiKey.workspaceId,
177
+ },
178
+ })
179
+
180
+ if (!existingProvider) {
181
+ return NextResponse.json({ error: 'No provider found' }, { status: 400 })
182
+ }
183
+
184
+ const provider = await prisma.modelProvider.delete({
185
+ where: { id: modelProviderId },
186
+ })
187
+
188
+ return NextResponse.json(
189
+ {
190
+ provider: serializeModelProvider({ provider }),
191
+ },
192
+ { headers: cacheHeaders },
193
+ )
194
+ }
195
+
196
+ export const OPTIONS = () =>
197
+ NextResponse.json(
198
+ {},
199
+ {
200
+ headers: cacheHeaders,
201
+ },
202
+ )
@@ -0,0 +1,105 @@
1
+ import { headers } from 'next/headers'
2
+ import { ApiKeyType, ModelProviderType } 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 { serializeModelProvider } from '@/lib/modelProviders/serializeModelProvider'
8
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
9
+
10
+ export const GET = async () => {
11
+ const headersList = await headers()
12
+ const authorization = headersList.get('authorization')
13
+ if (!authorization) {
14
+ return NextResponse.json(
15
+ { error: 'No authorization header found' },
16
+ { status: 400 },
17
+ )
18
+ }
19
+
20
+ const privateApiKey = await getApiKey({
21
+ type: ApiKeyType.PRIVATE,
22
+ authorization,
23
+ })
24
+
25
+ if (!privateApiKey) {
26
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
27
+ }
28
+
29
+ const providers = await prisma.modelProvider.findMany({
30
+ where: {
31
+ workspaceId: privateApiKey.workspaceId,
32
+ },
33
+ })
34
+
35
+ return NextResponse.json(
36
+ {
37
+ providers: providers.map((p) => serializeModelProvider({ provider: p })),
38
+ },
39
+ { headers: cacheHeaders },
40
+ )
41
+ }
42
+
43
+ export const POST = async (request: NextRequest) => {
44
+ const headersList = await headers()
45
+ const authorization = headersList.get('authorization')
46
+ if (!authorization) {
47
+ return NextResponse.json(
48
+ { error: 'No authorization header found' },
49
+ { status: 400 },
50
+ )
51
+ }
52
+
53
+ const privateApiKey = await getApiKey({
54
+ authorization,
55
+ type: ApiKeyType.PRIVATE,
56
+ })
57
+
58
+ if (!privateApiKey) {
59
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
60
+ }
61
+
62
+ const body = await request.json()
63
+ const schema = z.object({
64
+ type: z.nativeEnum(ModelProviderType),
65
+ name: z.string().optional(),
66
+ apiKey: z.string().optional(),
67
+ endpoint: z.string().optional().nullable(),
68
+ apiVersion: z.string().optional().nullable(),
69
+ })
70
+
71
+ const parsed = schema.safeParse(body)
72
+
73
+ if (!parsed.success) {
74
+ return NextResponse.json({ error: 'Invalid payload' }, { status: 400 })
75
+ }
76
+
77
+ const { type, name, apiKey: providerKey, endpoint, apiVersion } = parsed.data
78
+ const workspaceId = privateApiKey.workspaceId
79
+
80
+ const provider = await prisma.modelProvider.create({
81
+ data: {
82
+ type: type as ModelProviderType,
83
+ name: name ?? '',
84
+ apiKey: providerKey ?? '',
85
+ endpoint: endpoint ?? null,
86
+ apiVersion: apiVersion ?? null,
87
+ workspaceId,
88
+ },
89
+ })
90
+
91
+ return NextResponse.json(
92
+ {
93
+ provider: serializeModelProvider({ provider }),
94
+ },
95
+ { headers: cacheHeaders },
96
+ )
97
+ }
98
+
99
+ export const OPTIONS = () =>
100
+ NextResponse.json(
101
+ {},
102
+ {
103
+ headers: cacheHeaders,
104
+ },
105
+ )
@@ -0,0 +1,213 @@
1
+ import { headers } from 'next/headers'
2
+ import { ApiKeyType } from '@prisma/client'
3
+ import { NextResponse, type NextRequest } from 'next/server'
4
+ import { z } from 'zod'
5
+ import { validate } from 'uuid'
6
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
7
+ import { prisma } from '@/lib/prisma'
8
+ import { serializeTask } from '@/lib/tasks/serializeTask'
9
+ import { validateSchedule } from '@/lib/tasks/validateSchedule'
10
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
11
+ import { scheduleTask } from '@/lib/tasks/scheduleTask'
12
+ import { cancelScheduledTask } from '@/lib/tasks/cancelScheduledTask'
13
+
14
+ const updateTaskSchema = z.object({
15
+ title: z.string().optional(),
16
+ message: z.string().optional(),
17
+ schedule: z.any().optional(),
18
+ key: z.string().optional(),
19
+ })
20
+
21
+ export const GET = async (
22
+ _request: NextRequest,
23
+ props: { params: Promise<{ taskId: string }> },
24
+ ) => {
25
+ const { taskId } = await props.params
26
+
27
+ const headersList = await headers()
28
+ const authorization = headersList.get('authorization')
29
+ if (!authorization) {
30
+ return NextResponse.json(
31
+ { error: 'No authorization header found' },
32
+ { status: 400 },
33
+ )
34
+ }
35
+
36
+ const privateApiKey = await getApiKey({
37
+ authorization,
38
+ type: ApiKeyType.PRIVATE,
39
+ })
40
+
41
+ if (!privateApiKey) {
42
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
43
+ }
44
+
45
+ if (!taskId) {
46
+ return NextResponse.json({ error: 'No task id found' }, { status: 400 })
47
+ }
48
+
49
+ if (!validate(taskId)) {
50
+ return NextResponse.json({ error: 'Invalid task id' }, { status: 400 })
51
+ }
52
+
53
+ const task = await prisma.task.findFirst({
54
+ where: {
55
+ id: taskId,
56
+ thread: { assistant: { workspaceId: privateApiKey.workspaceId } },
57
+ },
58
+ })
59
+
60
+ if (!task) {
61
+ return NextResponse.json({ error: 'No task found' }, { status: 400 })
62
+ }
63
+
64
+ return NextResponse.json(
65
+ { task: serializeTask({ task }) },
66
+ { headers: cacheHeaders },
67
+ )
68
+ }
69
+
70
+ export const PATCH = async (
71
+ request: NextRequest,
72
+ props: { params: Promise<{ taskId: string }> },
73
+ ) => {
74
+ const { taskId } = await props.params
75
+
76
+ const headersList = await headers()
77
+ const authorization = headersList.get('authorization')
78
+ if (!authorization) {
79
+ return NextResponse.json(
80
+ { error: 'No authorization header found' },
81
+ { status: 400 },
82
+ )
83
+ }
84
+
85
+ const privateApiKey = await getApiKey({
86
+ authorization,
87
+ type: ApiKeyType.PRIVATE,
88
+ })
89
+
90
+ if (!privateApiKey) {
91
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
92
+ }
93
+
94
+ if (!taskId) {
95
+ return NextResponse.json({ error: 'No task id found' }, { status: 400 })
96
+ }
97
+
98
+ if (!validate(taskId)) {
99
+ return NextResponse.json({ error: 'Invalid task id' }, { status: 400 })
100
+ }
101
+
102
+ const parsed = updateTaskSchema.safeParse(await request.json())
103
+
104
+ if (!parsed.success) {
105
+ return NextResponse.json({ error: 'Invalid payload' }, { status: 400 })
106
+ }
107
+
108
+ const updateData = {
109
+ ...(parsed.data.title !== undefined ? { title: parsed.data.title } : {}),
110
+ ...(parsed.data.message !== undefined
111
+ ? { message: parsed.data.message }
112
+ : {}),
113
+ ...(parsed.data.schedule !== undefined
114
+ ? {
115
+ ...(validateSchedule(parsed.data.schedule)
116
+ ? { schedule: parsed.data.schedule }
117
+ : {}),
118
+ }
119
+ : {}),
120
+ ...(parsed.data.key !== undefined ? { key: parsed.data.key } : {}),
121
+ }
122
+
123
+ if (
124
+ parsed.data.schedule !== undefined &&
125
+ !validateSchedule(parsed.data.schedule)
126
+ ) {
127
+ return NextResponse.json({ error: 'Invalid schedule' }, { status: 400 })
128
+ }
129
+
130
+ const existingTask = await prisma.task.findFirst({
131
+ where: {
132
+ id: taskId,
133
+ thread: { assistant: { workspaceId: privateApiKey.workspaceId } },
134
+ },
135
+ })
136
+
137
+ if (!existingTask) {
138
+ return NextResponse.json({ error: 'No task found' }, { status: 400 })
139
+ }
140
+
141
+ const task = await prisma.task.update({
142
+ where: { id: existingTask.id },
143
+ data: updateData,
144
+ })
145
+
146
+ await scheduleTask({ task })
147
+
148
+ return NextResponse.json(
149
+ { task: serializeTask({ task }) },
150
+ { headers: cacheHeaders },
151
+ )
152
+ }
153
+
154
+ export const DELETE = async (
155
+ _request: NextRequest,
156
+ props: { params: Promise<{ taskId: string }> },
157
+ ) => {
158
+ const { taskId } = await props.params
159
+
160
+ const headersList = await headers()
161
+ const authorization = headersList.get('authorization')
162
+ if (!authorization) {
163
+ return NextResponse.json(
164
+ { error: 'No authorization header found' },
165
+ { status: 400 },
166
+ )
167
+ }
168
+
169
+ const privateApiKey = await getApiKey({
170
+ authorization,
171
+ type: ApiKeyType.PRIVATE,
172
+ })
173
+
174
+ if (!privateApiKey) {
175
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
176
+ }
177
+
178
+ if (!taskId) {
179
+ return NextResponse.json({ error: 'No task id found' }, { status: 400 })
180
+ }
181
+
182
+ if (!validate(taskId)) {
183
+ return NextResponse.json({ error: 'Invalid task id' }, { status: 400 })
184
+ }
185
+
186
+ const existingTask = await prisma.task.findFirst({
187
+ where: {
188
+ id: taskId,
189
+ thread: { assistant: { workspaceId: privateApiKey.workspaceId } },
190
+ },
191
+ })
192
+
193
+ if (!existingTask) {
194
+ return NextResponse.json({ error: 'No task found' }, { status: 400 })
195
+ }
196
+
197
+ await cancelScheduledTask({ task: existingTask })
198
+
199
+ const task = await prisma.task.delete({ where: { id: existingTask.id } })
200
+
201
+ return NextResponse.json(
202
+ { task: serializeTask({ task }) },
203
+ { headers: cacheHeaders },
204
+ )
205
+ }
206
+
207
+ export const OPTIONS = () =>
208
+ NextResponse.json(
209
+ {},
210
+ {
211
+ headers: cacheHeaders,
212
+ },
213
+ )