Compare commits

..

3 Commits

80 changed files with 414 additions and 9697 deletions

View File

@@ -5532,18 +5532,3 @@ export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function VercelIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='0 0 256 222'
xmlns='http://www.w3.org/2000/svg'
preserveAspectRatio='xMidYMid'
>
<g transform='translate(19.2 16.63) scale(0.85)'>
<polygon fill='#fafafa' points='128 0 256 221.705007 0 221.705007' />
</g>
</svg>
)
}

View File

@@ -125,7 +125,6 @@ import {
TTSIcon,
TwilioIcon,
TypeformIcon,
VercelIcon,
VideoIcon,
WealthboxIcon,
WebflowIcon,
@@ -263,7 +262,6 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
twilio_sms: TwilioIcon,
twilio_voice: TwilioIcon,
typeform: TypeformIcon,
vercel: VercelIcon,
video_generator_v2: VideoIcon,
vision_v2: EyeIcon,
wealthbox: WealthboxIcon,

View File

@@ -122,7 +122,6 @@
"twilio_sms",
"twilio_voice",
"typeform",
"vercel",
"video_generator",
"vision",
"wealthbox",

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@
"fumadocs-mdx": "14.1.0",
"fumadocs-ui": "16.2.3",
"lucide-react": "^0.511.0",
"next": "16.1.6",
"next": "16.1.0-canary.21",
"next-themes": "^0.4.6",
"postgres": "^3.4.5",
"react": "19.2.1",

View File

@@ -21,7 +21,7 @@ vi.mock('@/lib/core/utils/urls', () => ({
function setupAuthApiMocks(
options: {
operations?: {
requestPasswordReset?: { success?: boolean; error?: string }
forgetPassword?: { success?: boolean; error?: string }
resetPassword?: { success?: boolean; error?: string }
}
} = {}
@@ -34,11 +34,7 @@ function setupAuthApiMocks(
const { operations = {} } = options
const defaultOperations = {
requestPasswordReset: {
success: true,
error: 'Forget password error',
...operations.requestPasswordReset,
},
forgetPassword: { success: true, error: 'Forget password error', ...operations.forgetPassword },
resetPassword: { success: true, error: 'Reset password error', ...operations.resetPassword },
}
@@ -54,7 +50,7 @@ function setupAuthApiMocks(
vi.doMock('@/lib/auth', () => ({
auth: {
api: {
requestPasswordReset: createAuthMethod(defaultOperations.requestPasswordReset),
forgetPassword: createAuthMethod(defaultOperations.forgetPassword),
resetPassword: createAuthMethod(defaultOperations.resetPassword),
},
},
@@ -73,7 +69,7 @@ describe('Forget Password API Route', () => {
it('should send password reset email successfully with same-origin redirectTo', async () => {
setupAuthApiMocks({
operations: {
requestPasswordReset: { success: true },
forgetPassword: { success: true },
},
})
@@ -91,7 +87,7 @@ describe('Forget Password API Route', () => {
expect(data.success).toBe(true)
const auth = await import('@/lib/auth')
expect(auth.auth.api.requestPasswordReset).toHaveBeenCalledWith({
expect(auth.auth.api.forgetPassword).toHaveBeenCalledWith({
body: {
email: 'test@example.com',
redirectTo: 'https://app.example.com/reset',
@@ -103,7 +99,7 @@ describe('Forget Password API Route', () => {
it('should reject external redirectTo URL', async () => {
setupAuthApiMocks({
operations: {
requestPasswordReset: { success: true },
forgetPassword: { success: true },
},
})
@@ -121,13 +117,13 @@ describe('Forget Password API Route', () => {
expect(data.message).toBe('Redirect URL must be a valid same-origin URL')
const auth = await import('@/lib/auth')
expect(auth.auth.api.requestPasswordReset).not.toHaveBeenCalled()
expect(auth.auth.api.forgetPassword).not.toHaveBeenCalled()
})
it('should send password reset email without redirectTo', async () => {
setupAuthApiMocks({
operations: {
requestPasswordReset: { success: true },
forgetPassword: { success: true },
},
})
@@ -144,7 +140,7 @@ describe('Forget Password API Route', () => {
expect(data.success).toBe(true)
const auth = await import('@/lib/auth')
expect(auth.auth.api.requestPasswordReset).toHaveBeenCalledWith({
expect(auth.auth.api.forgetPassword).toHaveBeenCalledWith({
body: {
email: 'test@example.com',
redirectTo: undefined,
@@ -167,7 +163,7 @@ describe('Forget Password API Route', () => {
expect(data.message).toBe('Email is required')
const auth = await import('@/lib/auth')
expect(auth.auth.api.requestPasswordReset).not.toHaveBeenCalled()
expect(auth.auth.api.forgetPassword).not.toHaveBeenCalled()
})
it('should handle empty email', async () => {
@@ -186,7 +182,7 @@ describe('Forget Password API Route', () => {
expect(data.message).toBe('Please provide a valid email address')
const auth = await import('@/lib/auth')
expect(auth.auth.api.requestPasswordReset).not.toHaveBeenCalled()
expect(auth.auth.api.forgetPassword).not.toHaveBeenCalled()
})
it('should handle auth service error with message', async () => {
@@ -194,7 +190,7 @@ describe('Forget Password API Route', () => {
setupAuthApiMocks({
operations: {
requestPasswordReset: {
forgetPassword: {
success: false,
error: errorMessage,
},
@@ -226,7 +222,7 @@ describe('Forget Password API Route', () => {
vi.doMock('@/lib/auth', () => ({
auth: {
api: {
requestPasswordReset: vi.fn().mockRejectedValue('Unknown error'),
forgetPassword: vi.fn().mockRejectedValue('Unknown error'),
},
},
}))

View File

@@ -43,7 +43,7 @@ export async function POST(request: NextRequest) {
const { email, redirectTo } = validationResult.data
await auth.api.requestPasswordReset({
await auth.api.forgetPassword({
body: {
email,
redirectTo,

View File

@@ -17,7 +17,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
function setupAuthApiMocks(
options: {
operations?: {
requestPasswordReset?: { success?: boolean; error?: string }
forgetPassword?: { success?: boolean; error?: string }
resetPassword?: { success?: boolean; error?: string }
}
} = {}
@@ -30,11 +30,7 @@ function setupAuthApiMocks(
const { operations = {} } = options
const defaultOperations = {
requestPasswordReset: {
success: true,
error: 'Forget password error',
...operations.requestPasswordReset,
},
forgetPassword: { success: true, error: 'Forget password error', ...operations.forgetPassword },
resetPassword: { success: true, error: 'Reset password error', ...operations.resetPassword },
}
@@ -50,7 +46,7 @@ function setupAuthApiMocks(
vi.doMock('@/lib/auth', () => ({
auth: {
api: {
requestPasswordReset: createAuthMethod(defaultOperations.requestPasswordReset),
forgetPassword: createAuthMethod(defaultOperations.forgetPassword),
resetPassword: createAuthMethod(defaultOperations.resetPassword),
},
},

View File

@@ -1,7 +1,7 @@
import { db } from '@sim/db'
import { workflow, workflowFolder } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, isNull, min } from 'drizzle-orm'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
@@ -37,6 +37,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
logger.info(`[${requestId}] Duplicating folder ${sourceFolderId} for user ${session.user.id}`)
// Verify the source folder exists
const sourceFolder = await db
.select()
.from(workflowFolder)
@@ -47,6 +48,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
throw new Error('Source folder not found')
}
// Check if user has permission to access the source folder
const userPermission = await getUserEntityPermissions(
session.user.id,
'workspace',
@@ -59,51 +61,26 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
const targetWorkspaceId = workspaceId || sourceFolder.workspaceId
// Step 1: Duplicate folder structure
const { newFolderId, folderMapping } = await db.transaction(async (tx) => {
const newFolderId = crypto.randomUUID()
const now = new Date()
const targetParentId = parentId ?? sourceFolder.parentId
const folderParentCondition = targetParentId
? eq(workflowFolder.parentId, targetParentId)
: isNull(workflowFolder.parentId)
const workflowParentCondition = targetParentId
? eq(workflow.folderId, targetParentId)
: isNull(workflow.folderId)
const [[folderResult], [workflowResult]] = await Promise.all([
tx
.select({ minSortOrder: min(workflowFolder.sortOrder) })
.from(workflowFolder)
.where(and(eq(workflowFolder.workspaceId, targetWorkspaceId), folderParentCondition)),
tx
.select({ minSortOrder: min(workflow.sortOrder) })
.from(workflow)
.where(and(eq(workflow.workspaceId, targetWorkspaceId), workflowParentCondition)),
])
const minSortOrder = [folderResult?.minSortOrder, workflowResult?.minSortOrder].reduce<
number | null
>((currentMin, candidate) => {
if (candidate == null) return currentMin
if (currentMin == null) return candidate
return Math.min(currentMin, candidate)
}, null)
const sortOrder = minSortOrder != null ? minSortOrder - 1 : 0
// Create the new root folder
await tx.insert(workflowFolder).values({
id: newFolderId,
userId: session.user.id,
workspaceId: targetWorkspaceId,
name,
color: color || sourceFolder.color,
parentId: targetParentId,
sortOrder,
parentId: parentId || sourceFolder.parentId,
sortOrder: sourceFolder.sortOrder,
isExpanded: false,
createdAt: now,
updatedAt: now,
})
// Recursively duplicate child folders
const folderMapping = new Map<string, string>([[sourceFolderId, newFolderId]])
await duplicateFolderStructure(
tx,
@@ -119,6 +96,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
return { newFolderId, folderMapping }
})
// Step 2: Duplicate workflows
const workflowStats = await duplicateWorkflowsInFolderTree(
sourceFolder.workspaceId,
targetWorkspaceId,
@@ -195,6 +173,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
}
}
// Helper to recursively duplicate folder structure
async function duplicateFolderStructure(
tx: any,
sourceFolderId: string,
@@ -205,6 +184,7 @@ async function duplicateFolderStructure(
timestamp: Date,
folderMapping: Map<string, string>
): Promise<void> {
// Get all child folders
const childFolders = await tx
.select()
.from(workflowFolder)
@@ -215,6 +195,7 @@ async function duplicateFolderStructure(
)
)
// Create each child folder and recurse
for (const childFolder of childFolders) {
const newChildFolderId = crypto.randomUUID()
folderMapping.set(childFolder.id, newChildFolderId)
@@ -232,6 +213,7 @@ async function duplicateFolderStructure(
updatedAt: timestamp,
})
// Recurse for this child's children
await duplicateFolderStructure(
tx,
childFolder.id,
@@ -245,6 +227,7 @@ async function duplicateFolderStructure(
}
}
// Helper to duplicate all workflows in a folder tree
async function duplicateWorkflowsInFolderTree(
sourceWorkspaceId: string,
targetWorkspaceId: string,
@@ -254,7 +237,9 @@ async function duplicateWorkflowsInFolderTree(
): Promise<{ total: number; succeeded: number; failed: number }> {
const stats = { total: 0, succeeded: 0, failed: 0 }
// Process each folder in the mapping
for (const [oldFolderId, newFolderId] of folderMapping.entries()) {
// Get workflows in this folder
const workflowsInFolder = await db
.select()
.from(workflow)
@@ -262,6 +247,7 @@ async function duplicateWorkflowsInFolderTree(
stats.total += workflowsInFolder.length
// Duplicate each workflow
for (const sourceWorkflow of workflowsInFolder) {
try {
await duplicateWorkflow({

View File

@@ -10,14 +10,9 @@ import {
mockConsoleLogger,
setupCommonApiMocks,
} from '@sim/testing'
import { drizzleOrmMock } from '@sim/testing/mocks'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('@/lib/audit/log', () => auditMock)
vi.mock('drizzle-orm', () => ({
...drizzleOrmMock,
min: vi.fn((field) => ({ type: 'min', field })),
}))
interface CapturedFolderValues {
name?: string
@@ -29,35 +24,29 @@ interface CapturedFolderValues {
}
function createMockTransaction(mockData: {
selectResults?: Array<Array<{ [key: string]: unknown }>>
selectData?: Array<{ id: string; [key: string]: unknown }>
insertResult?: Array<{ id: string; [key: string]: unknown }>
onInsertValues?: (values: CapturedFolderValues) => void
}) {
const { selectResults = [[], []], insertResult = [], onInsertValues } = mockData
return async (callback: (tx: unknown) => Promise<unknown>) => {
const where = vi.fn()
for (const result of selectResults) {
where.mockReturnValueOnce(result)
}
where.mockReturnValue([])
const { selectData = [], insertResult = [] } = mockData
return vi.fn().mockImplementation(async (callback: (tx: unknown) => Promise<unknown>) => {
const tx = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where,
where: vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockReturnValue(selectData),
}),
}),
}),
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockImplementation((values: CapturedFolderValues) => {
onInsertValues?.(values)
return {
returning: vi.fn().mockReturnValue(insertResult),
}
values: vi.fn().mockReturnValue({
returning: vi.fn().mockReturnValue(insertResult),
}),
}),
}
return await callback(tx)
}
})
}
describe('Folders API Route', () => {
@@ -268,12 +257,25 @@ describe('Folders API Route', () => {
it('should create a new folder successfully', async () => {
mockAuthenticatedUser()
mockTransaction.mockImplementationOnce(
createMockTransaction({
selectResults: [[], []],
insertResult: [mockFolders[0]],
})
)
mockTransaction.mockImplementationOnce(async (callback: any) => {
const tx = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockReturnValue([]), // No existing folders
}),
}),
}),
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockReturnValue({
returning: vi.fn().mockReturnValue([mockFolders[0]]),
}),
}),
}
return await callback(tx)
})
const req = createMockRequest('POST', {
name: 'New Test Folder',
@@ -283,11 +285,12 @@ describe('Folders API Route', () => {
const { POST } = await import('@/app/api/folders/route')
const response = await POST(req)
const responseBody = await response.json()
expect(response.status).toBe(200)
expect(responseBody).toHaveProperty('folder')
expect(responseBody.folder).toMatchObject({
const data = await response.json()
expect(data).toHaveProperty('folder')
expect(data.folder).toMatchObject({
id: 'folder-1',
name: 'Test Folder 1',
workspaceId: 'workspace-123',
@@ -296,17 +299,26 @@ describe('Folders API Route', () => {
it('should create folder with correct sort order', async () => {
mockAuthenticatedUser()
let capturedValues: CapturedFolderValues | null = null
mockTransaction.mockImplementationOnce(
createMockTransaction({
selectResults: [[{ minSortOrder: 5 }], [{ minSortOrder: 2 }]],
insertResult: [{ ...mockFolders[0], sortOrder: 1 }],
onInsertValues: (values) => {
capturedValues = values
},
})
)
mockTransaction.mockImplementationOnce(async (callback: any) => {
const tx = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockReturnValue([{ sortOrder: 5 }]), // Existing folder with sort order 5
}),
}),
}),
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockReturnValue({
returning: vi.fn().mockReturnValue([{ ...mockFolders[0], sortOrder: 6 }]),
}),
}),
}
return await callback(tx)
})
const req = createMockRequest('POST', {
name: 'New Test Folder',
@@ -320,10 +332,8 @@ describe('Folders API Route', () => {
const data = await response.json()
expect(data.folder).toMatchObject({
sortOrder: 1,
sortOrder: 6,
})
expect(capturedValues).not.toBeNull()
expect(capturedValues!.sortOrder).toBe(1)
})
it('should create subfolder with parent reference', async () => {
@@ -331,7 +341,7 @@ describe('Folders API Route', () => {
mockTransaction.mockImplementationOnce(
createMockTransaction({
selectResults: [[], []],
selectData: [], // No existing folders
insertResult: [{ ...mockFolders[1] }],
})
)
@@ -392,12 +402,25 @@ describe('Folders API Route', () => {
mockAuthenticatedUser()
mockGetUserEntityPermissions.mockResolvedValue('write') // Write permissions
mockTransaction.mockImplementationOnce(
createMockTransaction({
selectResults: [[], []],
insertResult: [mockFolders[0]],
})
)
mockTransaction.mockImplementationOnce(async (callback: any) => {
const tx = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockReturnValue([]), // No existing folders
}),
}),
}),
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockReturnValue({
returning: vi.fn().mockReturnValue([mockFolders[0]]),
}),
}),
}
return await callback(tx)
})
const req = createMockRequest('POST', {
name: 'Test Folder',
@@ -417,12 +440,25 @@ describe('Folders API Route', () => {
mockAuthenticatedUser()
mockGetUserEntityPermissions.mockResolvedValue('admin') // Admin permissions
mockTransaction.mockImplementationOnce(
createMockTransaction({
selectResults: [[], []],
insertResult: [mockFolders[0]],
})
)
mockTransaction.mockImplementationOnce(async (callback: any) => {
const tx = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockReturnValue([]), // No existing folders
}),
}),
}),
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockReturnValue({
returning: vi.fn().mockReturnValue([mockFolders[0]]),
}),
}),
}
return await callback(tx)
})
const req = createMockRequest('POST', {
name: 'Test Folder',
@@ -491,15 +527,28 @@ describe('Folders API Route', () => {
let capturedValues: CapturedFolderValues | null = null
mockTransaction.mockImplementationOnce(
createMockTransaction({
selectResults: [[], []],
insertResult: [mockFolders[0]],
onInsertValues: (values) => {
capturedValues = values
},
})
)
mockTransaction.mockImplementationOnce(async (callback: any) => {
const tx = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockReturnValue([]),
}),
}),
}),
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockImplementation((values) => {
capturedValues = values
return {
returning: vi.fn().mockReturnValue([mockFolders[0]]),
}
}),
}),
}
return await callback(tx)
})
const req = createMockRequest('POST', {
name: ' Test Folder With Spaces ',
@@ -518,15 +567,28 @@ describe('Folders API Route', () => {
let capturedValues: CapturedFolderValues | null = null
mockTransaction.mockImplementationOnce(
createMockTransaction({
selectResults: [[], []],
insertResult: [mockFolders[0]],
onInsertValues: (values) => {
capturedValues = values
},
})
)
mockTransaction.mockImplementationOnce(async (callback: any) => {
const tx = {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockReturnValue([]),
}),
}),
}),
}),
insert: vi.fn().mockReturnValue({
values: vi.fn().mockImplementation((values) => {
capturedValues = values
return {
returning: vi.fn().mockReturnValue([mockFolders[0]]),
}
}),
}),
}
return await callback(tx)
})
const req = createMockRequest('POST', {
name: 'Test Folder',

View File

@@ -1,7 +1,7 @@
import { db } from '@sim/db'
import { workflow, workflowFolder } from '@sim/db/schema'
import { workflowFolder } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, asc, eq, isNull, min } from 'drizzle-orm'
import { and, asc, desc, eq, isNull } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
@@ -87,33 +87,19 @@ export async function POST(request: NextRequest) {
if (providedSortOrder !== undefined) {
sortOrder = providedSortOrder
} else {
const folderParentCondition = parentId
? eq(workflowFolder.parentId, parentId)
: isNull(workflowFolder.parentId)
const workflowParentCondition = parentId
? eq(workflow.folderId, parentId)
: isNull(workflow.folderId)
const existingFolders = await tx
.select({ sortOrder: workflowFolder.sortOrder })
.from(workflowFolder)
.where(
and(
eq(workflowFolder.workspaceId, workspaceId),
parentId ? eq(workflowFolder.parentId, parentId) : isNull(workflowFolder.parentId)
)
)
.orderBy(desc(workflowFolder.sortOrder))
.limit(1)
const [[folderResult], [workflowResult]] = await Promise.all([
tx
.select({ minSortOrder: min(workflowFolder.sortOrder) })
.from(workflowFolder)
.where(and(eq(workflowFolder.workspaceId, workspaceId), folderParentCondition)),
tx
.select({ minSortOrder: min(workflow.sortOrder) })
.from(workflow)
.where(and(eq(workflow.workspaceId, workspaceId), workflowParentCondition)),
])
const minSortOrder = [folderResult?.minSortOrder, workflowResult?.minSortOrder].reduce<
number | null
>((currentMin, candidate) => {
if (candidate == null) return currentMin
if (currentMin == null) return candidate
return Math.min(currentMin, candidate)
}, null)
sortOrder = minSortOrder != null ? minSortOrder - 1 : 0
sortOrder = existingFolders.length > 0 ? existingFolders[0].sortOrder + 1 : 0
}
const [folder] = await tx

View File

@@ -1,137 +0,0 @@
/**
* @vitest-environment node
*/
import { auditMock, createMockRequest, mockConsoleLogger, setupCommonApiMocks } from '@sim/testing'
import { drizzleOrmMock } from '@sim/testing/mocks'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mockCheckSessionOrInternalAuth = vi.fn()
const mockGetUserEntityPermissions = vi.fn()
const mockDbSelect = vi.fn()
const mockDbInsert = vi.fn()
const mockWorkflowCreated = vi.fn()
vi.mock('drizzle-orm', () => ({
...drizzleOrmMock,
min: vi.fn((field) => ({ type: 'min', field })),
}))
vi.mock('@/lib/audit/log', () => auditMock)
describe('Workflows API Route - POST ordering', () => {
beforeEach(() => {
vi.resetModules()
vi.clearAllMocks()
setupCommonApiMocks()
mockConsoleLogger()
vi.stubGlobal('crypto', {
randomUUID: vi.fn().mockReturnValue('workflow-new-id'),
})
mockCheckSessionOrInternalAuth.mockResolvedValue({
success: true,
userId: 'user-123',
userName: 'Test User',
userEmail: 'test@example.com',
})
mockGetUserEntityPermissions.mockResolvedValue('write')
vi.doMock('@sim/db', () => ({
db: {
select: (...args: unknown[]) => mockDbSelect(...args),
insert: (...args: unknown[]) => mockDbInsert(...args),
},
}))
vi.doMock('@/lib/auth/hybrid', () => ({
checkSessionOrInternalAuth: (...args: unknown[]) => mockCheckSessionOrInternalAuth(...args),
}))
vi.doMock('@/lib/workspaces/permissions/utils', () => ({
getUserEntityPermissions: (...args: unknown[]) => mockGetUserEntityPermissions(...args),
workspaceExists: vi.fn(),
}))
vi.doMock('@/app/api/workflows/utils', () => ({
verifyWorkspaceMembership: vi.fn(),
}))
vi.doMock('@/lib/core/telemetry', () => ({
PlatformEvents: {
workflowCreated: (...args: unknown[]) => mockWorkflowCreated(...args),
},
}))
})
it('uses top insertion against mixed siblings (folders + workflows)', async () => {
const minResultsQueue: Array<Array<{ minOrder: number }>> = [
[{ minOrder: 5 }],
[{ minOrder: 2 }],
]
mockDbSelect.mockImplementation(() => ({
from: vi.fn().mockReturnValue({
where: vi.fn().mockImplementation(() => Promise.resolve(minResultsQueue.shift() ?? [])),
}),
}))
let insertedValues: Record<string, unknown> | null = null
mockDbInsert.mockReturnValue({
values: vi.fn().mockImplementation((values: Record<string, unknown>) => {
insertedValues = values
return Promise.resolve(undefined)
}),
})
const req = createMockRequest('POST', {
name: 'New Workflow',
description: 'desc',
color: '#3972F6',
workspaceId: 'workspace-123',
folderId: null,
})
const { POST } = await import('@/app/api/workflows/route')
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(200)
expect(data.sortOrder).toBe(1)
expect(insertedValues).not.toBeNull()
expect(insertedValues?.sortOrder).toBe(1)
})
it('defaults to sortOrder 0 when there are no siblings', async () => {
const minResultsQueue: Array<Array<{ minOrder: number }>> = [[], []]
mockDbSelect.mockImplementation(() => ({
from: vi.fn().mockReturnValue({
where: vi.fn().mockImplementation(() => Promise.resolve(minResultsQueue.shift() ?? [])),
}),
}))
let insertedValues: Record<string, unknown> | null = null
mockDbInsert.mockReturnValue({
values: vi.fn().mockImplementation((values: Record<string, unknown>) => {
insertedValues = values
return Promise.resolve(undefined)
}),
})
const req = createMockRequest('POST', {
name: 'New Workflow',
description: 'desc',
color: '#3972F6',
workspaceId: 'workspace-123',
folderId: null,
})
const { POST } = await import('@/app/api/workflows/route')
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(200)
expect(data.sortOrder).toBe(0)
expect(insertedValues?.sortOrder).toBe(0)
})
})

View File

@@ -1,5 +1,5 @@
import { db } from '@sim/db'
import { permissions, workflow, workflowFolder } from '@sim/db/schema'
import { permissions, workflow } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, asc, eq, inArray, isNull, min } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
@@ -162,33 +162,12 @@ export async function POST(req: NextRequest) {
if (providedSortOrder !== undefined) {
sortOrder = providedSortOrder
} else {
const workflowParentCondition = folderId
? eq(workflow.folderId, folderId)
: isNull(workflow.folderId)
const folderParentCondition = folderId
? eq(workflowFolder.parentId, folderId)
: isNull(workflowFolder.parentId)
const [[workflowMinResult], [folderMinResult]] = await Promise.all([
db
.select({ minOrder: min(workflow.sortOrder) })
.from(workflow)
.where(and(eq(workflow.workspaceId, workspaceId), workflowParentCondition)),
db
.select({ minOrder: min(workflowFolder.sortOrder) })
.from(workflowFolder)
.where(and(eq(workflowFolder.workspaceId, workspaceId), folderParentCondition)),
])
const minSortOrder = [workflowMinResult?.minOrder, folderMinResult?.minOrder].reduce<
number | null
>((currentMin, candidate) => {
if (candidate == null) return currentMin
if (currentMin == null) return candidate
return Math.min(currentMin, candidate)
}, null)
sortOrder = minSortOrder != null ? minSortOrder - 1 : 0
const folderCondition = folderId ? eq(workflow.folderId, folderId) : isNull(workflow.folderId)
const [minResult] = await db
.select({ minOrder: min(workflow.sortOrder) })
.from(workflow)
.where(and(eq(workflow.workspaceId, workspaceId), folderCondition))
sortOrder = (minResult?.minOrder ?? 1) - 1
}
await db.insert(workflow).values({

File diff suppressed because it is too large Load Diff

View File

@@ -146,7 +146,6 @@ import { TwilioSMSBlock } from '@/blocks/blocks/twilio'
import { TwilioVoiceBlock } from '@/blocks/blocks/twilio_voice'
import { TypeformBlock } from '@/blocks/blocks/typeform'
import { VariablesBlock } from '@/blocks/blocks/variables'
import { VercelBlock } from '@/blocks/blocks/vercel'
import { VideoGeneratorBlock, VideoGeneratorV2Block } from '@/blocks/blocks/video_generator'
import { VisionBlock, VisionV2Block } from '@/blocks/blocks/vision'
import { WaitBlock } from '@/blocks/blocks/wait'
@@ -331,7 +330,6 @@ export const registry: Record<string, BlockConfig> = {
twilio_sms: TwilioSMSBlock,
twilio_voice: TwilioVoiceBlock,
typeform: TypeformBlock,
vercel: VercelBlock,
variables: VariablesBlock,
video_generator: VideoGeneratorBlock,
video_generator_v2: VideoGeneratorV2Block,

View File

@@ -5532,18 +5532,3 @@ export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function VercelIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='0 0 256 222'
xmlns='http://www.w3.org/2000/svg'
preserveAspectRatio='xMidYMid'
>
<g transform='translate(19.2 16.63) scale(0.85)'>
<polygon fill='#fafafa' points='128 0 256 221.705007 0 221.705007' />
</g>
</svg>
)
}

View File

@@ -1,177 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { mockLogger, queryClient, useFolderStoreMock, useWorkflowRegistryMock } = vi.hoisted(() => ({
mockLogger: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
queryClient: {
cancelQueries: vi.fn().mockResolvedValue(undefined),
invalidateQueries: vi.fn().mockResolvedValue(undefined),
},
useFolderStoreMock: Object.assign(vi.fn(), {
getState: vi.fn(),
setState: vi.fn(),
}),
useWorkflowRegistryMock: Object.assign(vi.fn(), {
getState: vi.fn(),
setState: vi.fn(),
}),
}))
let folderState: {
folders: Record<string, any>
}
let workflowRegistryState: {
workflows: Record<string, any>
}
vi.mock('@sim/logger', () => ({
createLogger: vi.fn(() => mockLogger),
}))
vi.mock('@tanstack/react-query', () => ({
keepPreviousData: {},
useQuery: vi.fn(),
useQueryClient: vi.fn(() => queryClient),
useMutation: vi.fn((options) => options),
}))
vi.mock('@/stores/folders/store', () => ({
useFolderStore: useFolderStoreMock,
}))
vi.mock('@/stores/workflows/registry/store', () => ({
useWorkflowRegistry: useWorkflowRegistryMock,
}))
vi.mock('@/hooks/queries/workflows', () => ({
workflowKeys: {
list: (workspaceId: string | undefined) => ['workflows', 'list', workspaceId ?? ''],
},
}))
import { useCreateFolder, useDuplicateFolderMutation } from '@/hooks/queries/folders'
function getOptimisticFolderByName(name: string) {
return Object.values(folderState.folders).find((folder: any) => folder.name === name) as
| { sortOrder: number }
| undefined
}
describe('folder optimistic top insertion ordering', () => {
beforeEach(() => {
vi.clearAllMocks()
useFolderStoreMock.getState.mockImplementation(() => folderState)
useFolderStoreMock.setState.mockImplementation((updater: any) => {
if (typeof updater === 'function') {
const next = updater(folderState)
if (next) {
folderState = { ...folderState, ...next }
}
return
}
folderState = { ...folderState, ...updater }
})
useWorkflowRegistryMock.getState.mockImplementation(() => workflowRegistryState)
folderState = {
folders: {
'folder-parent-match': {
id: 'folder-parent-match',
name: 'Existing sibling folder',
userId: 'user-1',
workspaceId: 'ws-1',
parentId: 'parent-1',
color: '#808080',
isExpanded: false,
sortOrder: 5,
createdAt: new Date(),
updatedAt: new Date(),
},
'folder-other-parent': {
id: 'folder-other-parent',
name: 'Other parent folder',
userId: 'user-1',
workspaceId: 'ws-1',
parentId: 'parent-2',
color: '#808080',
isExpanded: false,
sortOrder: -100,
createdAt: new Date(),
updatedAt: new Date(),
},
},
}
workflowRegistryState = {
workflows: {
'workflow-parent-match': {
id: 'workflow-parent-match',
name: 'Existing sibling workflow',
workspaceId: 'ws-1',
folderId: 'parent-1',
sortOrder: 2,
},
'workflow-other-parent': {
id: 'workflow-other-parent',
name: 'Other parent workflow',
workspaceId: 'ws-1',
folderId: 'parent-2',
sortOrder: -50,
},
},
}
})
it('creates folders at top of mixed non-root siblings', async () => {
const mutation = useCreateFolder()
await mutation.onMutate({
workspaceId: 'ws-1',
name: 'New child folder',
parentId: 'parent-1',
})
const optimisticFolder = getOptimisticFolderByName('New child folder')
expect(optimisticFolder).toBeDefined()
expect(optimisticFolder?.sortOrder).toBe(1)
})
it('duplicates folders at top of mixed non-root siblings', async () => {
const mutation = useDuplicateFolderMutation()
await mutation.onMutate({
workspaceId: 'ws-1',
id: 'folder-parent-match',
name: 'Duplicated child folder',
parentId: 'parent-1',
})
const optimisticFolder = getOptimisticFolderByName('Duplicated child folder')
expect(optimisticFolder).toBeDefined()
expect(optimisticFolder?.sortOrder).toBe(1)
})
it('uses source parent scope when duplicate parentId is undefined', async () => {
const mutation = useDuplicateFolderMutation()
await mutation.onMutate({
workspaceId: 'ws-1',
id: 'folder-parent-match',
name: 'Duplicated with inherited parent',
// parentId intentionally omitted to mirror duplicate fallback behavior
})
const optimisticFolder = getOptimisticFolderByName('Duplicated with inherited parent') as
| { parentId: string | null; sortOrder: number }
| undefined
expect(optimisticFolder).toBeDefined()
expect(optimisticFolder?.parentId).toBe('parent-1')
expect(optimisticFolder?.sortOrder).toBe(1)
})
})

View File

@@ -5,11 +5,9 @@ import {
createOptimisticMutationHandlers,
generateTempId,
} from '@/hooks/queries/utils/optimistic-mutation'
import { getTopInsertionSortOrder } from '@/hooks/queries/utils/top-insertion-sort-order'
import { workflowKeys } from '@/hooks/queries/workflows'
import { useFolderStore } from '@/stores/folders/store'
import type { WorkflowFolder } from '@/stores/folders/types'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
const logger = createLogger('FolderQueries')
@@ -135,35 +133,40 @@ function createFolderMutationHandlers<TVariables extends { workspaceId: string }
})
}
/**
* Calculates the next sort order for a folder in a given parent
*/
function getNextSortOrder(
folders: Record<string, WorkflowFolder>,
workspaceId: string,
parentId: string | null | undefined
): number {
const siblingFolders = Object.values(folders).filter(
(f) => f.workspaceId === workspaceId && f.parentId === (parentId || null)
)
return siblingFolders.reduce((max, f) => Math.max(max, f.sortOrder), -1) + 1
}
export function useCreateFolder() {
const queryClient = useQueryClient()
const handlers = createFolderMutationHandlers<CreateFolderVariables>(
queryClient,
'CreateFolder',
(variables, tempId, previousFolders) => {
const currentWorkflows = useWorkflowRegistry.getState().workflows
return {
id: tempId,
name: variables.name,
userId: '',
workspaceId: variables.workspaceId,
parentId: variables.parentId || null,
color: variables.color || '#808080',
isExpanded: false,
sortOrder:
variables.sortOrder ??
getTopInsertionSortOrder(
currentWorkflows,
previousFolders,
variables.workspaceId,
variables.parentId
),
createdAt: new Date(),
updatedAt: new Date(),
}
}
(variables, tempId, previousFolders) => ({
id: tempId,
name: variables.name,
userId: '',
workspaceId: variables.workspaceId,
parentId: variables.parentId || null,
color: variables.color || '#808080',
isExpanded: false,
sortOrder:
variables.sortOrder ??
getNextSortOrder(previousFolders, variables.workspaceId, variables.parentId),
createdAt: new Date(),
updatedAt: new Date(),
})
)
return useMutation({
@@ -239,25 +242,17 @@ export function useDuplicateFolderMutation() {
queryClient,
'DuplicateFolder',
(variables, tempId, previousFolders) => {
const currentWorkflows = useWorkflowRegistry.getState().workflows
// Get source folder info if available
const sourceFolder = previousFolders[variables.id]
const targetParentId = variables.parentId ?? sourceFolder?.parentId ?? null
return {
id: tempId,
name: variables.name,
userId: sourceFolder?.userId || '',
workspaceId: variables.workspaceId,
parentId: targetParentId,
parentId: variables.parentId ?? sourceFolder?.parentId ?? null,
color: variables.color || sourceFolder?.color || '#808080',
isExpanded: false,
sortOrder: getTopInsertionSortOrder(
currentWorkflows,
previousFolders,
variables.workspaceId,
targetParentId
),
sortOrder: getNextSortOrder(previousFolders, variables.workspaceId, variables.parentId),
createdAt: new Date(),
updatedAt: new Date(),
}

View File

@@ -1,44 +0,0 @@
interface SortableWorkflow {
workspaceId?: string
folderId?: string | null
sortOrder?: number
}
interface SortableFolder {
workspaceId?: string
parentId?: string | null
sortOrder: number
}
/**
* Calculates the insertion sort order that places a new item at the top of a
* mixed list of folders and workflows within the same parent scope.
*/
export function getTopInsertionSortOrder(
workflows: Record<string, SortableWorkflow>,
folders: Record<string, SortableFolder>,
workspaceId: string,
parentId: string | null | undefined
): number {
const normalizedParentId = parentId ?? null
const siblingWorkflows = Object.values(workflows).filter(
(workflow) =>
workflow.workspaceId === workspaceId && (workflow.folderId ?? null) === normalizedParentId
)
const siblingFolders = Object.values(folders).filter(
(folder) =>
folder.workspaceId === workspaceId && (folder.parentId ?? null) === normalizedParentId
)
const siblingOrders = [
...siblingWorkflows.map((workflow) => workflow.sortOrder ?? 0),
...siblingFolders.map((folder) => folder.sortOrder),
]
if (siblingOrders.length === 0) {
return 0
}
return Math.min(...siblingOrders) - 1
}

View File

@@ -8,8 +8,6 @@ import {
createOptimisticMutationHandlers,
generateTempId,
} from '@/hooks/queries/utils/optimistic-mutation'
import { getTopInsertionSortOrder } from '@/hooks/queries/utils/top-insertion-sort-order'
import { useFolderStore } from '@/stores/folders/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
import { generateCreativeWorkflowName } from '@/stores/workflows/registry/utils'
@@ -225,13 +223,11 @@ export function useCreateWorkflow() {
sortOrder = variables.sortOrder
} else {
const currentWorkflows = useWorkflowRegistry.getState().workflows
const currentFolders = useFolderStore.getState().folders
sortOrder = getTopInsertionSortOrder(
currentWorkflows,
currentFolders,
variables.workspaceId,
variables.folderId
const targetFolderId = variables.folderId || null
const workflowsInFolder = Object.values(currentWorkflows).filter(
(w) => w.folderId === targetFolderId
)
sortOrder = workflowsInFolder.reduce((min, w) => Math.min(min, w.sortOrder ?? 0), 1) - 1
}
return {
@@ -327,8 +323,11 @@ export function useDuplicateWorkflowMutation() {
'DuplicateWorkflow',
(variables, tempId) => {
const currentWorkflows = useWorkflowRegistry.getState().workflows
const currentFolders = useFolderStore.getState().folders
const targetFolderId = variables.folderId ?? null
const targetFolderId = variables.folderId || null
const workflowsInFolder = Object.values(currentWorkflows).filter(
(w) => w.folderId === targetFolderId
)
const minSortOrder = workflowsInFolder.reduce((min, w) => Math.min(min, w.sortOrder ?? 0), 1)
return {
id: tempId,
@@ -339,12 +338,7 @@ export function useDuplicateWorkflowMutation() {
color: variables.color,
workspaceId: variables.workspaceId,
folderId: targetFolderId,
sortOrder: getTopInsertionSortOrder(
currentWorkflows,
currentFolders,
variables.workspaceId,
targetFolderId
),
sortOrder: minSortOrder - 1,
}
}
)

View File

@@ -466,7 +466,7 @@ export const auth = betterAuth({
sendVerificationOnSignUp: isEmailVerificationEnabled, // Auto-send verification OTP on signup when verification is required
throwOnMissingCredentials: true,
throwOnInvalidCredentials: true,
sendResetPassword: async ({ user, url, token }, ctx) => {
sendResetPassword: async ({ user, url, token }, request) => {
const username = user.name || ''
const html = await renderPasswordResetEmail(username, url)
@@ -542,7 +542,7 @@ export const auth = betterAuth({
plugins: [
nextCookies(),
oneTimeToken({
expiresIn: 24 * 60, // 24 hours in minutes - Socket.IO handles connection persistence with heartbeats
expiresIn: 24 * 60 * 60, // 24 hours - Socket.IO handles connection persistence with heartbeats
}),
customSession(async ({ user, session }) => ({
user,
@@ -2876,9 +2876,9 @@ export const auth = betterAuth({
return hasTeamPlan
},
organizationHooks: {
afterCreateOrganization: async ({ organization, member, user }) => {
logger.info('[organizationHooks.afterCreateOrganization] Organization created', {
organizationCreation: {
afterCreate: async ({ organization, user }) => {
logger.info('[organizationCreation.afterCreate] Organization created', {
organizationId: organization.id,
creatorId: user.id,
})

View File

@@ -1,197 +0,0 @@
/**
* @vitest-environment node
*/
import { mockConsoleLogger, setupCommonApiMocks } from '@sim/testing'
import { drizzleOrmMock } from '@sim/testing/mocks'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mockAuthorizeWorkflowByWorkspacePermission = vi.fn()
const mockGetUserEntityPermissions = vi.fn()
const { mockDb } = vi.hoisted(() => ({
mockDb: {
transaction: vi.fn(),
},
}))
vi.mock('drizzle-orm', () => ({
...drizzleOrmMock,
min: vi.fn((field) => ({ type: 'min', field })),
}))
vi.mock('@/lib/workflows/utils', () => ({
authorizeWorkflowByWorkspacePermission: (...args: unknown[]) =>
mockAuthorizeWorkflowByWorkspacePermission(...args),
}))
vi.mock('@/lib/workspaces/permissions/utils', () => ({
getUserEntityPermissions: (...args: unknown[]) => mockGetUserEntityPermissions(...args),
}))
vi.mock('@sim/db/schema', () => ({
workflow: {
id: 'id',
workspaceId: 'workspaceId',
folderId: 'folderId',
sortOrder: 'sortOrder',
variables: 'variables',
},
workflowFolder: {
workspaceId: 'workspaceId',
parentId: 'parentId',
sortOrder: 'sortOrder',
},
workflowBlocks: {
workflowId: 'workflowId',
},
workflowEdges: {
workflowId: 'workflowId',
},
workflowSubflows: {
workflowId: 'workflowId',
},
}))
vi.mock('@sim/db', () => ({
db: mockDb,
}))
import { duplicateWorkflow } from './duplicate'
function createMockTx(
selectResults: unknown[],
onWorkflowInsert?: (values: Record<string, unknown>) => void
) {
let selectCallCount = 0
const select = vi.fn().mockImplementation(() => ({
from: vi.fn().mockReturnValue({
where: vi.fn().mockImplementation(() => {
const result = selectResults[selectCallCount++] ?? []
if (selectCallCount === 1) {
return {
limit: vi.fn().mockResolvedValue(result),
}
}
return Promise.resolve(result)
}),
}),
}))
const insert = vi.fn().mockReturnValue({
values: vi.fn().mockImplementation((values: Record<string, unknown>) => {
onWorkflowInsert?.(values)
return Promise.resolve(undefined)
}),
})
const update = vi.fn().mockReturnValue({
set: vi.fn().mockReturnValue({
where: vi.fn().mockResolvedValue(undefined),
}),
})
return {
select,
insert,
update,
}
}
describe('duplicateWorkflow ordering', () => {
beforeEach(() => {
setupCommonApiMocks()
mockConsoleLogger()
vi.clearAllMocks()
vi.stubGlobal('crypto', {
randomUUID: vi.fn().mockReturnValue('new-workflow-id'),
})
mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({ allowed: true })
mockGetUserEntityPermissions.mockResolvedValue('write')
})
it('uses mixed-sibling top insertion sort order', async () => {
let insertedWorkflowValues: Record<string, unknown> | null = null
const tx = createMockTx(
[
[
{
id: 'source-workflow-id',
workspaceId: 'workspace-123',
folderId: null,
description: 'source',
color: '#000000',
variables: {},
},
],
[{ minOrder: 5 }],
[{ minOrder: 2 }],
[],
[],
[],
],
(values) => {
insertedWorkflowValues = values
}
)
mockDb.transaction.mockImplementation(async (callback: (txArg: unknown) => Promise<unknown>) =>
callback(tx)
)
const result = await duplicateWorkflow({
sourceWorkflowId: 'source-workflow-id',
userId: 'user-123',
name: 'Duplicated',
workspaceId: 'workspace-123',
folderId: null,
requestId: 'req-1',
})
expect(result.sortOrder).toBe(1)
expect(insertedWorkflowValues?.sortOrder).toBe(1)
})
it('defaults to sortOrder 0 when target has no siblings', async () => {
let insertedWorkflowValues: Record<string, unknown> | null = null
const tx = createMockTx(
[
[
{
id: 'source-workflow-id',
workspaceId: 'workspace-123',
folderId: null,
description: 'source',
color: '#000000',
variables: {},
},
],
[],
[],
[],
[],
[],
],
(values) => {
insertedWorkflowValues = values
}
)
mockDb.transaction.mockImplementation(async (callback: (txArg: unknown) => Promise<unknown>) =>
callback(tx)
)
const result = await duplicateWorkflow({
sourceWorkflowId: 'source-workflow-id',
userId: 'user-123',
name: 'Duplicated',
workspaceId: 'workspace-123',
folderId: null,
requestId: 'req-2',
})
expect(result.sortOrder).toBe(0)
expect(insertedWorkflowValues?.sortOrder).toBe(0)
})
})

View File

@@ -1,11 +1,5 @@
import { db } from '@sim/db'
import {
workflow,
workflowBlocks,
workflowEdges,
workflowFolder,
workflowSubflows,
} from '@sim/db/schema'
import { workflow, workflowBlocks, workflowEdges, workflowSubflows } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, isNull, min } from 'drizzle-orm'
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
@@ -138,31 +132,15 @@ export async function duplicateWorkflow(
throw new Error('Write or admin access required for target workspace')
}
const targetFolderId = folderId !== undefined ? folderId : source.folderId
const workflowParentCondition = targetFolderId
const folderCondition = targetFolderId
? eq(workflow.folderId, targetFolderId)
: isNull(workflow.folderId)
const folderParentCondition = targetFolderId
? eq(workflowFolder.parentId, targetFolderId)
: isNull(workflowFolder.parentId)
const [[workflowMinResult], [folderMinResult]] = await Promise.all([
tx
.select({ minOrder: min(workflow.sortOrder) })
.from(workflow)
.where(and(eq(workflow.workspaceId, targetWorkspaceId), workflowParentCondition)),
tx
.select({ minOrder: min(workflowFolder.sortOrder) })
.from(workflowFolder)
.where(and(eq(workflowFolder.workspaceId, targetWorkspaceId), folderParentCondition)),
])
const minSortOrder = [workflowMinResult?.minOrder, folderMinResult?.minOrder].reduce<
number | null
>((currentMin, candidate) => {
if (candidate == null) return currentMin
if (currentMin == null) return candidate
return Math.min(currentMin, candidate)
}, null)
const sortOrder = minSortOrder != null ? minSortOrder - 1 : 0
const [minResult] = await tx
.select({ minOrder: min(workflow.sortOrder) })
.from(workflow)
.where(and(eq(workflow.workspaceId, targetWorkspaceId), folderCondition))
const sortOrder = (minResult?.minOrder ?? 1) - 1
// Mapping from old variable IDs to new variable IDs (populated during variable duplication)
const varIdMapping = new Map<string, string>()

View File

@@ -1,6 +1,6 @@
import type { NextConfig } from 'next'
import { env, getEnv, isTruthy } from './lib/core/config/env'
import { isDev } from './lib/core/config/feature-flags'
import { isDev, isHosted } from './lib/core/config/feature-flags'
import {
getFormEmbedCSPPolicy,
getMainCSPPolicy,
@@ -306,15 +306,34 @@ const nextConfig: NextConfig = {
}
)
return redirects
},
async rewrites() {
return [
{
// Only enable domain redirects for the hosted version
if (isHosted) {
redirects.push(
{
source: '/((?!api|_next|_vercel|favicon|static|ingest|.*\\..*).*)',
destination: 'https://www.sim.ai/$1',
permanent: true,
has: [{ type: 'host' as const, value: 'simstudio.ai' }],
},
{
source: '/((?!api|_next|_vercel|favicon|static|ingest|.*\\..*).*)',
destination: 'https://www.sim.ai/$1',
permanent: true,
has: [{ type: 'host' as const, value: 'www.simstudio.ai' }],
}
)
}
// Beluga campaign short link tracking
if (isHosted) {
redirects.push({
source: '/r/:shortCode',
destination: 'https://go.trybeluga.ai/:shortCode',
},
]
permanent: false,
})
}
return redirects
},
}

View File

@@ -35,8 +35,8 @@
"@aws-sdk/s3-request-presigner": "^3.779.0",
"@azure/communication-email": "1.0.0",
"@azure/storage-blob": "12.27.0",
"@better-auth/sso": "1.4.18",
"@better-auth/stripe": "1.4.18",
"@better-auth/sso": "1.3.12",
"@better-auth/stripe": "1.3.12",
"@browserbasehq/stagehand": "^3.0.5",
"@cerebras/cerebras_cloud_sdk": "^1.23.0",
"@e2b/code-interpreter": "^2.0.0",
@@ -82,7 +82,7 @@
"@trigger.dev/sdk": "4.1.2",
"@types/react-window": "2.0.0",
"@types/three": "0.177.0",
"better-auth": "1.4.18",
"better-auth": "1.3.12",
"binary-extensions": "^2.0.0",
"browser-image-compression": "^2.0.2",
"chalk": "5.6.2",
@@ -125,7 +125,7 @@
"mysql2": "3.14.3",
"nanoid": "^3.3.7",
"neo4j-driver": "6.0.1",
"next": "16.1.6",
"next": "16.1.0-canary.21",
"next-mdx-remote": "^5.0.0",
"next-runtime-env": "3.3.0",
"next-themes": "^0.4.6",
@@ -208,8 +208,8 @@
"sharp"
],
"overrides": {
"next": "16.1.6",
"@next/env": "16.1.6",
"next": "16.1.0-canary.21",
"@next/env": "16.1.0-canary.21",
"drizzle-orm": "^0.44.5",
"postgres": "^3.4.5"
}

View File

@@ -1693,58 +1693,6 @@ import {
typeformUpdateFormTool,
} from '@/tools/typeform'
import type { ToolConfig } from '@/tools/types'
import {
vercelAddDomainTool,
vercelAddProjectDomainTool,
vercelCancelDeploymentTool,
vercelCreateAliasTool,
vercelCreateCheckTool,
vercelCreateDeploymentTool,
vercelCreateDnsRecordTool,
vercelCreateEdgeConfigTool,
vercelCreateEnvVarTool,
vercelCreateProjectTool,
vercelCreateWebhookTool,
vercelDeleteAliasTool,
vercelDeleteDeploymentTool,
vercelDeleteDnsRecordTool,
vercelDeleteDomainTool,
vercelDeleteEnvVarTool,
vercelDeleteProjectTool,
vercelDeleteWebhookTool,
vercelGetAliasTool,
vercelGetCheckTool,
vercelGetDeploymentEventsTool,
vercelGetDeploymentTool,
vercelGetDomainConfigTool,
vercelGetDomainTool,
vercelGetEdgeConfigItemsTool,
vercelGetEdgeConfigTool,
vercelGetEnvVarsTool,
vercelGetProjectTool,
vercelGetTeamTool,
vercelGetUserTool,
vercelListAliasesTool,
vercelListChecksTool,
vercelListDeploymentFilesTool,
vercelListDeploymentsTool,
vercelListDnsRecordsTool,
vercelListDomainsTool,
vercelListEdgeConfigsTool,
vercelListProjectDomainsTool,
vercelListProjectsTool,
vercelListTeamMembersTool,
vercelListTeamsTool,
vercelListWebhooksTool,
vercelPauseProjectTool,
vercelRemoveProjectDomainTool,
vercelRerequestCheckTool,
vercelUnpauseProjectTool,
vercelUpdateCheckTool,
vercelUpdateEdgeConfigItemsTool,
vercelUpdateEnvVarTool,
vercelUpdateProjectTool,
} from '@/tools/vercel'
import {
falaiVideoTool,
lumaVideoTool,
@@ -2752,66 +2700,6 @@ export const tools: Record<string, ToolConfig> = {
trello_update_card: trelloUpdateCardTool,
trello_get_actions: trelloGetActionsTool,
trello_add_comment: trelloAddCommentTool,
// Vercel - Deployments
vercel_list_deployments: vercelListDeploymentsTool,
vercel_get_deployment: vercelGetDeploymentTool,
vercel_create_deployment: vercelCreateDeploymentTool,
vercel_cancel_deployment: vercelCancelDeploymentTool,
vercel_delete_deployment: vercelDeleteDeploymentTool,
vercel_get_deployment_events: vercelGetDeploymentEventsTool,
vercel_list_deployment_files: vercelListDeploymentFilesTool,
// Vercel - Projects
vercel_list_projects: vercelListProjectsTool,
vercel_get_project: vercelGetProjectTool,
vercel_create_project: vercelCreateProjectTool,
vercel_update_project: vercelUpdateProjectTool,
vercel_delete_project: vercelDeleteProjectTool,
vercel_pause_project: vercelPauseProjectTool,
vercel_unpause_project: vercelUnpauseProjectTool,
vercel_list_project_domains: vercelListProjectDomainsTool,
vercel_add_project_domain: vercelAddProjectDomainTool,
vercel_remove_project_domain: vercelRemoveProjectDomainTool,
// Vercel - Environment Variables
vercel_get_env_vars: vercelGetEnvVarsTool,
vercel_create_env_var: vercelCreateEnvVarTool,
vercel_update_env_var: vercelUpdateEnvVarTool,
vercel_delete_env_var: vercelDeleteEnvVarTool,
// Vercel - Domains
vercel_list_domains: vercelListDomainsTool,
vercel_get_domain: vercelGetDomainTool,
vercel_add_domain: vercelAddDomainTool,
vercel_delete_domain: vercelDeleteDomainTool,
vercel_get_domain_config: vercelGetDomainConfigTool,
// Vercel - DNS
vercel_list_dns_records: vercelListDnsRecordsTool,
vercel_create_dns_record: vercelCreateDnsRecordTool,
vercel_delete_dns_record: vercelDeleteDnsRecordTool,
// Vercel - Aliases
vercel_list_aliases: vercelListAliasesTool,
vercel_get_alias: vercelGetAliasTool,
vercel_create_alias: vercelCreateAliasTool,
vercel_delete_alias: vercelDeleteAliasTool,
// Vercel - Edge Config
vercel_list_edge_configs: vercelListEdgeConfigsTool,
vercel_get_edge_config: vercelGetEdgeConfigTool,
vercel_create_edge_config: vercelCreateEdgeConfigTool,
vercel_get_edge_config_items: vercelGetEdgeConfigItemsTool,
vercel_update_edge_config_items: vercelUpdateEdgeConfigItemsTool,
// Vercel - Teams & User
vercel_list_teams: vercelListTeamsTool,
vercel_get_team: vercelGetTeamTool,
vercel_list_team_members: vercelListTeamMembersTool,
vercel_get_user: vercelGetUserTool,
// Webhooks
vercel_list_webhooks: vercelListWebhooksTool,
vercel_create_webhook: vercelCreateWebhookTool,
vercel_delete_webhook: vercelDeleteWebhookTool,
// Checks
vercel_create_check: vercelCreateCheckTool,
vercel_get_check: vercelGetCheckTool,
vercel_list_checks: vercelListChecksTool,
vercel_update_check: vercelUpdateCheckTool,
vercel_rerequest_check: vercelRerequestCheckTool,
twilio_send_sms: sendSMSTool,
twilio_voice_make_call: makeCallTool,
twilio_voice_list_calls: listCallsTool,

View File

@@ -1,84 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelAddDomainParams, VercelAddDomainResponse } from '@/tools/vercel/types'
export const vercelAddDomainTool: ToolConfig<VercelAddDomainParams, VercelAddDomainResponse> = {
id: 'vercel_add_domain',
name: 'Vercel Add Domain',
description: 'Add a new domain to a Vercel account or team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain name to add',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelAddDomainParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v7/domains${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelAddDomainParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelAddDomainParams) => ({
method: 'add',
name: params.name.trim(),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const d = data.domain ?? data
return {
success: true,
output: {
id: d.id ?? null,
name: d.name ?? null,
verified: d.verified ?? false,
createdAt: d.createdAt ?? null,
serviceType: d.serviceType ?? null,
nameservers: d.nameservers ?? [],
intendedNameservers: d.intendedNameservers ?? [],
},
}
},
outputs: {
id: { type: 'string', description: 'Domain ID' },
name: { type: 'string', description: 'Domain name' },
verified: { type: 'boolean', description: 'Whether domain is verified' },
createdAt: { type: 'number', description: 'Creation timestamp' },
serviceType: { type: 'string', description: 'Service type (zeit.world, external, na)' },
nameservers: {
type: 'array',
description: 'Current nameservers',
items: { type: 'string' },
},
intendedNameservers: {
type: 'array',
description: 'Intended nameservers',
items: { type: 'string' },
},
},
}

View File

@@ -1,113 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelAddProjectDomainParams,
VercelAddProjectDomainResponse,
} from '@/tools/vercel/types'
export const vercelAddProjectDomainTool: ToolConfig<
VercelAddProjectDomainParams,
VercelAddProjectDomainResponse
> = {
id: 'vercel_add_project_domain',
name: 'Vercel Add Project Domain',
description: 'Add a domain to a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Domain name to add',
},
redirect: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Target domain for redirect',
},
redirectStatusCode: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'HTTP status code for redirect (301, 302, 307, 308)',
},
gitBranch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Git branch to link the domain to',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelAddProjectDomainParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v10/projects/${params.projectId.trim()}/domains${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelAddProjectDomainParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelAddProjectDomainParams) => {
const body: Record<string, unknown> = { name: params.domain.trim() }
if (params.redirect) body.redirect = params.redirect.trim()
if (params.redirectStatusCode) body.redirectStatusCode = params.redirectStatusCode
if (params.gitBranch) body.gitBranch = params.gitBranch.trim()
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
name: data.name,
apexName: data.apexName,
verified: data.verified,
gitBranch: data.gitBranch ?? null,
redirect: data.redirect ?? null,
redirectStatusCode: data.redirectStatusCode ?? null,
createdAt: data.createdAt,
updatedAt: data.updatedAt,
},
}
},
outputs: {
name: { type: 'string', description: 'Domain name' },
apexName: { type: 'string', description: 'Apex domain name' },
verified: { type: 'boolean', description: 'Whether the domain is verified' },
gitBranch: { type: 'string', description: 'Git branch for the domain', optional: true },
redirect: { type: 'string', description: 'Redirect target domain', optional: true },
redirectStatusCode: {
type: 'number',
description: 'HTTP status code for redirect (301, 302, 307, 308)',
optional: true,
},
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
},
}

View File

@@ -1,83 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelCancelDeploymentParams,
VercelCancelDeploymentResponse,
} from '@/tools/vercel/types'
export const vercelCancelDeploymentTool: ToolConfig<
VercelCancelDeploymentParams,
VercelCancelDeploymentResponse
> = {
id: 'vercel_cancel_deployment',
name: 'Vercel Cancel Deployment',
description: 'Cancel a running Vercel deployment',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The deployment ID to cancel',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCancelDeploymentParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v12/deployments/${params.deploymentId.trim()}/cancel${qs ? `?${qs}` : ''}`
},
method: 'PATCH',
headers: (params: VercelCancelDeploymentParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? data.uid,
name: data.name ?? null,
state: data.readyState ?? data.state ?? 'CANCELED',
url: data.url ?? null,
},
}
},
outputs: {
id: {
type: 'string',
description: 'Deployment ID',
},
name: {
type: 'string',
description: 'Deployment name',
},
state: {
type: 'string',
description: 'Deployment state after cancellation',
},
url: {
type: 'string',
description: 'Deployment URL',
},
},
}

View File

@@ -1,87 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelCreateAliasParams, VercelCreateAliasResponse } from '@/tools/vercel/types'
export const vercelCreateAliasTool: ToolConfig<VercelCreateAliasParams, VercelCreateAliasResponse> =
{
id: 'vercel_create_alias',
name: 'Vercel Create Alias',
description: 'Assign an alias (domain/subdomain) to a deployment',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Deployment ID to assign the alias to',
},
alias: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain or subdomain to assign as an alias',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCreateAliasParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v2/deployments/${params.deploymentId.trim()}/aliases${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateAliasParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateAliasParams) => ({
alias: params.alias.trim(),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
uid: data.uid ?? null,
alias: data.alias ?? null,
created: data.created ?? null,
oldDeploymentId: data.oldDeploymentId ?? null,
},
}
},
outputs: {
uid: {
type: 'string',
description: 'Alias ID',
},
alias: {
type: 'string',
description: 'Alias hostname',
},
created: {
type: 'string',
description: 'Creation timestamp as ISO 8601 date-time string',
},
oldDeploymentId: {
type: 'string',
description: 'ID of the previously aliased deployment, if the alias was reassigned',
},
},
}

View File

@@ -1,141 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelCheckResponse, VercelCreateCheckParams } from '@/tools/vercel/types'
export const vercelCreateCheckTool: ToolConfig<VercelCreateCheckParams, VercelCheckResponse> = {
id: 'vercel_create_check',
name: 'Vercel Create Check',
description: 'Create a new deployment check',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Deployment ID to create the check for',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the check (max 100 characters)',
},
blocking: {
type: 'boolean',
required: true,
visibility: 'user-or-llm',
description: 'Whether the check blocks the deployment',
},
path: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Page path being checked',
},
detailsUrl: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'URL with details about the check',
},
externalId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'External identifier for the check',
},
rerequestable: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the check can be rerequested',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCreateCheckParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/deployments/${params.deploymentId.trim()}/checks${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateCheckParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateCheckParams) => {
const body: Record<string, unknown> = {
name: params.name.trim(),
blocking: params.blocking,
}
if (params.path) body.path = params.path
if (params.detailsUrl) body.detailsUrl = params.detailsUrl
if (params.externalId) body.externalId = params.externalId
if (params.rerequestable !== undefined) body.rerequestable = params.rerequestable
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
status: data.status ?? 'registered',
conclusion: data.conclusion ?? null,
blocking: data.blocking ?? false,
deploymentId: data.deploymentId,
integrationId: data.integrationId ?? null,
externalId: data.externalId ?? null,
detailsUrl: data.detailsUrl ?? null,
path: data.path ?? null,
rerequestable: data.rerequestable ?? false,
createdAt: data.createdAt,
updatedAt: data.updatedAt,
startedAt: data.startedAt ?? null,
completedAt: data.completedAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Check ID' },
name: { type: 'string', description: 'Check name' },
status: { type: 'string', description: 'Check status: registered, running, or completed' },
conclusion: {
type: 'string',
description: 'Check conclusion: canceled, failed, neutral, succeeded, skipped, or stale',
optional: true,
},
blocking: { type: 'boolean', description: 'Whether the check blocks the deployment' },
deploymentId: { type: 'string', description: 'Associated deployment ID' },
integrationId: { type: 'string', description: 'Associated integration ID', optional: true },
externalId: { type: 'string', description: 'External identifier', optional: true },
detailsUrl: { type: 'string', description: 'URL with details about the check', optional: true },
path: { type: 'string', description: 'Page path being checked', optional: true },
rerequestable: { type: 'boolean', description: 'Whether the check can be rerequested' },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
updatedAt: { type: 'number', description: 'Last update timestamp in milliseconds' },
startedAt: { type: 'number', description: 'Start timestamp in milliseconds', optional: true },
completedAt: {
type: 'number',
description: 'Completion timestamp in milliseconds',
optional: true,
},
},
}

View File

@@ -1,136 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelCreateDeploymentParams,
VercelCreateDeploymentResponse,
} from '@/tools/vercel/types'
export const vercelCreateDeploymentTool: ToolConfig<
VercelCreateDeploymentParams,
VercelCreateDeploymentResponse
> = {
id: 'vercel_create_deployment',
name: 'Vercel Create Deployment',
description: 'Create a new deployment or redeploy an existing one',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project name for the deployment',
},
project: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Project ID (overrides name for project lookup)',
},
deploymentId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Existing deployment ID to redeploy',
},
target: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Target environment: production, staging, or a custom environment identifier',
},
gitSource: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON string defining the Git Repository source to deploy (e.g. {"type":"github","repo":"owner/repo","ref":"main"})',
},
forceNew: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Forces a new deployment even if there is a previous similar deployment (0 or 1)',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCreateDeploymentParams) => {
const query = new URLSearchParams()
if (params.forceNew) query.set('forceNew', params.forceNew)
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v13/deployments${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateDeploymentParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateDeploymentParams) => {
const body: Record<string, any> = {
name: params.name.trim(),
}
if (params.project) body.project = params.project.trim()
if (params.deploymentId) body.deploymentId = params.deploymentId.trim()
if (params.target) body.target = params.target
if (params.gitSource) {
try {
body.gitSource = JSON.parse(params.gitSource)
} catch {
body.gitSource = params.gitSource
}
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
url: data.url ?? '',
readyState: data.readyState ?? 'QUEUED',
projectId: data.projectId ?? '',
createdAt: data.createdAt ?? data.created,
alias: data.alias ?? [],
target: data.target ?? null,
inspectorUrl: data.inspectorUrl ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Deployment ID' },
name: { type: 'string', description: 'Deployment name' },
url: { type: 'string', description: 'Unique deployment URL' },
readyState: {
type: 'string',
description: 'Deployment ready state: QUEUED, BUILDING, ERROR, INITIALIZING, READY, CANCELED',
},
projectId: { type: 'string', description: 'Associated project ID' },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
alias: {
type: 'array',
description: 'Assigned aliases',
items: { type: 'string', description: 'Alias domain' },
},
target: { type: 'string', description: 'Target environment', optional: true },
inspectorUrl: { type: 'string', description: 'Vercel inspector URL' },
},
}

View File

@@ -1,107 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelCreateDnsRecordParams,
VercelCreateDnsRecordResponse,
} from '@/tools/vercel/types'
export const vercelCreateDnsRecordTool: ToolConfig<
VercelCreateDnsRecordParams,
VercelCreateDnsRecordResponse
> = {
id: 'vercel_create_dns_record',
name: 'Vercel Create DNS Record',
description: 'Create a DNS record for a domain in a Vercel account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain name to create the record for',
},
recordName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The subdomain or record name',
},
recordType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'DNS record type (A, AAAA, ALIAS, CAA, CNAME, HTTPS, MX, SRV, TXT, NS)',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The value of the DNS record',
},
ttl: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Time to live in seconds',
},
mxPriority: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Priority for MX records',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCreateDnsRecordParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v2/domains/${params.domain.trim()}/records${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateDnsRecordParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateDnsRecordParams) => {
const body: Record<string, unknown> = {
name: params.recordName.trim(),
type: params.recordType.trim(),
value: params.value.trim(),
}
if (params.ttl != null) body.ttl = params.ttl
if (params.mxPriority != null) body.mxPriority = params.mxPriority
return body
},
},
transformResponse: async (response: Response) => {
const d = await response.json()
return {
success: true,
output: {
uid: d.uid ?? null,
updated: d.updated ?? null,
},
}
},
outputs: {
uid: { type: 'string', description: 'The DNS record ID' },
updated: { type: 'number', description: 'Timestamp of the update' },
},
}

View File

@@ -1,106 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelCreateEdgeConfigParams,
VercelCreateEdgeConfigResponse,
} from '@/tools/vercel/types'
export const vercelCreateEdgeConfigTool: ToolConfig<
VercelCreateEdgeConfigParams,
VercelCreateEdgeConfigResponse
> = {
id: 'vercel_create_edge_config',
name: 'Vercel Create Edge Config',
description: 'Create a new Edge Config store',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
slug: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The name/slug for the new Edge Config',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCreateEdgeConfigParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/edge-config${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateEdgeConfigParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateEdgeConfigParams) => ({
slug: params.slug.trim(),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? null,
slug: data.slug ?? null,
ownerId: data.ownerId ?? null,
digest: data.digest ?? null,
createdAt: data.createdAt ?? null,
updatedAt: data.updatedAt ?? null,
itemCount: data.itemCount ?? 0,
sizeInBytes: data.sizeInBytes ?? 0,
},
}
},
outputs: {
id: {
type: 'string',
description: 'Edge Config ID',
},
slug: {
type: 'string',
description: 'Edge Config slug',
},
ownerId: {
type: 'string',
description: 'Owner ID',
},
digest: {
type: 'string',
description: 'Content digest hash',
},
createdAt: {
type: 'number',
description: 'Creation timestamp',
},
updatedAt: {
type: 'number',
description: 'Last update timestamp',
},
itemCount: {
type: 'number',
description: 'Number of items',
},
sizeInBytes: {
type: 'number',
description: 'Size in bytes',
},
},
}

View File

@@ -1,145 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelCreateEnvVarParams, VercelCreateEnvVarResponse } from '@/tools/vercel/types'
export const vercelCreateEnvVarTool: ToolConfig<
VercelCreateEnvVarParams,
VercelCreateEnvVarResponse
> = {
id: 'vercel_create_env_var',
name: 'Vercel Create Environment Variable',
description: 'Create an environment variable for a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
key: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Environment variable name',
},
value: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Environment variable value',
},
target: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated list of target environments (production, preview, development)',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Variable type: system, secret, encrypted, plain, or sensitive (default: plain)',
},
gitBranch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Git branch to associate with the variable (requires target to include preview)',
},
comment: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comment to add context to the variable (max 500 characters)',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCreateEnvVarParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v10/projects/${params.projectId.trim()}/env${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateEnvVarParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateEnvVarParams) => {
const body: Record<string, unknown> = {
key: params.key,
value: params.value,
target: params.target.split(',').map((t) => t.trim()),
type: params.type || 'plain',
}
if (params.gitBranch) body.gitBranch = params.gitBranch
if (params.comment) body.comment = params.comment
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
const env = data.created ?? data
return {
success: true,
output: {
id: env.id,
key: env.key,
value: env.value ?? '',
type: env.type ?? 'plain',
target: env.target ?? [],
gitBranch: env.gitBranch ?? null,
comment: env.comment ?? null,
},
}
},
outputs: {
id: {
type: 'string',
description: 'Environment variable ID',
},
key: {
type: 'string',
description: 'Variable name',
},
value: {
type: 'string',
description: 'Variable value',
},
type: {
type: 'string',
description: 'Variable type (secret, system, encrypted, plain, sensitive)',
},
target: {
type: 'array',
description: 'Target environments',
items: { type: 'string', description: 'Environment name' },
},
gitBranch: {
type: 'string',
description: 'Git branch filter',
optional: true,
},
comment: {
type: 'string',
description: 'Comment providing context for the variable',
optional: true,
},
},
}

View File

@@ -1,108 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelCreateProjectParams, VercelCreateProjectResponse } from '@/tools/vercel/types'
export const vercelCreateProjectTool: ToolConfig<
VercelCreateProjectParams,
VercelCreateProjectResponse
> = {
id: 'vercel_create_project',
name: 'Vercel Create Project',
description: 'Create a new Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project name',
},
framework: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Project framework (e.g. nextjs, remix, vite)',
},
gitRepository: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Git repository connection object with type and repo',
},
buildCommand: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom build command',
},
outputDirectory: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom output directory',
},
installCommand: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom install command',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelCreateProjectParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v11/projects${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateProjectParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateProjectParams) => {
const body: Record<string, unknown> = { name: params.name.trim() }
if (params.framework) body.framework = params.framework.trim()
if (params.gitRepository) body.gitRepository = params.gitRepository
if (params.buildCommand) body.buildCommand = params.buildCommand.trim()
if (params.outputDirectory) body.outputDirectory = params.outputDirectory.trim()
if (params.installCommand) body.installCommand = params.installCommand.trim()
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
framework: data.framework ?? null,
createdAt: data.createdAt,
updatedAt: data.updatedAt,
},
}
},
outputs: {
id: { type: 'string', description: 'Project ID' },
name: { type: 'string', description: 'Project name' },
framework: { type: 'string', description: 'Project framework', optional: true },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
},
}

View File

@@ -1,105 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelCreateWebhookParams, VercelCreateWebhookResponse } from '@/tools/vercel/types'
export const vercelCreateWebhookTool: ToolConfig<
VercelCreateWebhookParams,
VercelCreateWebhookResponse
> = {
id: 'vercel_create_webhook',
name: 'Vercel Create Webhook',
description: 'Create a new webhook for a Vercel team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Webhook URL (must be https)',
},
events: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated event names to subscribe to',
},
projectIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated project IDs to scope the webhook to',
},
teamId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Team ID to create the webhook for',
},
},
request: {
url: (params: VercelCreateWebhookParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/webhooks${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelCreateWebhookParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelCreateWebhookParams) => {
const body: Record<string, any> = {
url: params.url.trim(),
events: params.events.split(',').map((e) => e.trim()),
}
if (params.projectIds) {
body.projectIds = params.projectIds.split(',').map((p) => p.trim())
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? null,
url: data.url ?? null,
secret: data.secret ?? null,
events: data.events ?? [],
ownerId: data.ownerId ?? null,
projectIds: data.projectIds ?? [],
createdAt: data.createdAt ?? null,
updatedAt: data.updatedAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Webhook ID' },
url: { type: 'string', description: 'Webhook URL' },
secret: { type: 'string', description: 'Webhook signing secret' },
events: {
type: 'array',
description: 'Events the webhook listens to',
items: { type: 'string', description: 'Event name' },
},
ownerId: { type: 'string', description: 'Owner ID' },
projectIds: {
type: 'array',
description: 'Associated project IDs',
items: { type: 'string', description: 'Project ID' },
},
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
},
}

View File

@@ -1,62 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelDeleteAliasParams, VercelDeleteAliasResponse } from '@/tools/vercel/types'
export const vercelDeleteAliasTool: ToolConfig<VercelDeleteAliasParams, VercelDeleteAliasResponse> =
{
id: 'vercel_delete_alias',
name: 'Vercel Delete Alias',
description: 'Delete an alias by its ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
aliasId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Alias ID to delete',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelDeleteAliasParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v2/aliases/${params.aliasId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelDeleteAliasParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
status: data.status ?? 'SUCCESS',
},
}
},
outputs: {
status: {
type: 'string',
description: 'Deletion status (SUCCESS)',
},
},
}

View File

@@ -1,77 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelDeleteDeploymentParams,
VercelDeleteDeploymentResponse,
} from '@/tools/vercel/types'
export const vercelDeleteDeploymentTool: ToolConfig<
VercelDeleteDeploymentParams,
VercelDeleteDeploymentResponse
> = {
id: 'vercel_delete_deployment',
name: 'Vercel Delete Deployment',
description: 'Delete a Vercel deployment',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The deployment ID or URL to delete',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelDeleteDeploymentParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const id = params.deploymentId.trim()
if (id.includes('.')) {
query.set('url', id)
}
const qs = query.toString()
return `https://api.vercel.com/v13/deployments/${id}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelDeleteDeploymentParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
uid: data.uid ?? data.id ?? null,
state: data.state ?? 'DELETED',
},
}
},
outputs: {
uid: {
type: 'string',
description: 'The removed deployment ID',
},
state: {
type: 'string',
description: 'Deployment state after deletion (DELETED)',
},
},
}

View File

@@ -1,69 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelDeleteDnsRecordParams,
VercelDeleteDnsRecordResponse,
} from '@/tools/vercel/types'
export const vercelDeleteDnsRecordTool: ToolConfig<
VercelDeleteDnsRecordParams,
VercelDeleteDnsRecordResponse
> = {
id: 'vercel_delete_dns_record',
name: 'Vercel Delete DNS Record',
description: 'Delete a DNS record for a domain in a Vercel account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain name the record belongs to',
},
recordId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the DNS record to delete',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelDeleteDnsRecordParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v2/domains/${params.domain.trim()}/records/${params.recordId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelDeleteDnsRecordParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async () => {
return {
success: true,
output: {
deleted: true,
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the record was deleted' },
},
}

View File

@@ -1,64 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelDeleteDomainParams, VercelDeleteDomainResponse } from '@/tools/vercel/types'
export const vercelDeleteDomainTool: ToolConfig<
VercelDeleteDomainParams,
VercelDeleteDomainResponse
> = {
id: 'vercel_delete_domain',
name: 'Vercel Delete Domain',
description: 'Delete a domain from a Vercel account or team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain name to delete',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelDeleteDomainParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v6/domains/${params.domain.trim()}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelDeleteDomainParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const d = await response.json()
return {
success: true,
output: {
uid: d.uid ?? null,
deleted: true,
},
}
},
outputs: {
uid: { type: 'string', description: 'The ID of the deleted domain' },
deleted: { type: 'boolean', description: 'Whether the domain was deleted' },
},
}

View File

@@ -1,69 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelDeleteEnvVarParams, VercelDeleteEnvVarResponse } from '@/tools/vercel/types'
export const vercelDeleteEnvVarTool: ToolConfig<
VercelDeleteEnvVarParams,
VercelDeleteEnvVarResponse
> = {
id: 'vercel_delete_env_var',
name: 'Vercel Delete Environment Variable',
description: 'Delete an environment variable from a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
envId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Environment variable ID to delete',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelDeleteEnvVarParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v9/projects/${params.projectId.trim()}/env/${params.envId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelDeleteEnvVarParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async () => {
return {
success: true,
output: {
deleted: true,
},
}
},
outputs: {
deleted: {
type: 'boolean',
description: 'Whether the environment variable was successfully deleted',
},
},
}

View File

@@ -1,60 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelDeleteProjectParams, VercelDeleteProjectResponse } from '@/tools/vercel/types'
export const vercelDeleteProjectTool: ToolConfig<
VercelDeleteProjectParams,
VercelDeleteProjectResponse
> = {
id: 'vercel_delete_project',
name: 'Vercel Delete Project',
description: 'Delete a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelDeleteProjectParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v9/projects/${params.projectId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelDeleteProjectParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async () => {
return {
success: true,
output: {
deleted: true,
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the project was successfully deleted' },
},
}

View File

@@ -1,63 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelDeleteWebhookParams, VercelDeleteWebhookResponse } from '@/tools/vercel/types'
export const vercelDeleteWebhookTool: ToolConfig<
VercelDeleteWebhookParams,
VercelDeleteWebhookResponse
> = {
id: 'vercel_delete_webhook',
name: 'Vercel Delete Webhook',
description: 'Delete a webhook from a Vercel team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
webhookId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The webhook ID to delete',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelDeleteWebhookParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/webhooks/${params.webhookId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelDeleteWebhookParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async () => {
return {
success: true,
output: {
deleted: true,
},
}
},
outputs: {
deleted: {
type: 'boolean',
description: 'Whether the webhook was successfully deleted',
},
},
}

View File

@@ -1,97 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetAliasParams, VercelGetAliasResponse } from '@/tools/vercel/types'
export const vercelGetAliasTool: ToolConfig<VercelGetAliasParams, VercelGetAliasResponse> = {
id: 'vercel_get_alias',
name: 'Vercel Get Alias',
description: 'Get details about a specific alias by ID or hostname',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
aliasId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Alias ID or hostname to look up',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetAliasParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v4/aliases/${params.aliasId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetAliasParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
uid: data.uid ?? null,
alias: data.alias ?? null,
deploymentId: data.deploymentId ?? null,
projectId: data.projectId ?? null,
createdAt: data.createdAt ?? null,
updatedAt: data.updatedAt ?? null,
redirect: data.redirect ?? null,
redirectStatusCode: data.redirectStatusCode ?? null,
},
}
},
outputs: {
uid: {
type: 'string',
description: 'Alias ID',
},
alias: {
type: 'string',
description: 'Alias hostname',
},
deploymentId: {
type: 'string',
description: 'Associated deployment ID',
},
projectId: {
type: 'string',
description: 'Associated project ID',
},
createdAt: {
type: 'number',
description: 'Creation timestamp in milliseconds',
},
updatedAt: {
type: 'number',
description: 'Last update timestamp in milliseconds',
},
redirect: {
type: 'string',
description: 'Target domain for redirect aliases',
},
redirectStatusCode: {
type: 'number',
description: 'HTTP status code for redirect (301, 302, 307, or 308)',
},
},
}

View File

@@ -1,99 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelCheckResponse, VercelGetCheckParams } from '@/tools/vercel/types'
export const vercelGetCheckTool: ToolConfig<VercelGetCheckParams, VercelCheckResponse> = {
id: 'vercel_get_check',
name: 'Vercel Get Check',
description: 'Get details of a specific deployment check',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Deployment ID the check belongs to',
},
checkId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Check ID to retrieve',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetCheckParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/deployments/${params.deploymentId.trim()}/checks/${params.checkId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetCheckParams) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
status: data.status ?? 'registered',
conclusion: data.conclusion ?? null,
blocking: data.blocking ?? false,
deploymentId: data.deploymentId,
integrationId: data.integrationId ?? null,
externalId: data.externalId ?? null,
detailsUrl: data.detailsUrl ?? null,
path: data.path ?? null,
rerequestable: data.rerequestable ?? false,
createdAt: data.createdAt,
updatedAt: data.updatedAt,
startedAt: data.startedAt ?? null,
completedAt: data.completedAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Check ID' },
name: { type: 'string', description: 'Check name' },
status: { type: 'string', description: 'Check status: registered, running, or completed' },
conclusion: {
type: 'string',
description: 'Check conclusion: canceled, failed, neutral, succeeded, skipped, or stale',
optional: true,
},
blocking: { type: 'boolean', description: 'Whether the check blocks the deployment' },
deploymentId: { type: 'string', description: 'Associated deployment ID' },
integrationId: { type: 'string', description: 'Associated integration ID', optional: true },
externalId: { type: 'string', description: 'External identifier', optional: true },
detailsUrl: { type: 'string', description: 'URL with details about the check', optional: true },
path: { type: 'string', description: 'Page path being checked', optional: true },
rerequestable: { type: 'boolean', description: 'Whether the check can be rerequested' },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
updatedAt: { type: 'number', description: 'Last update timestamp in milliseconds' },
startedAt: { type: 'number', description: 'Start timestamp in milliseconds', optional: true },
completedAt: {
type: 'number',
description: 'Completion timestamp in milliseconds',
optional: true,
},
},
}

View File

@@ -1,176 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetDeploymentParams, VercelGetDeploymentResponse } from '@/tools/vercel/types'
export const vercelGetDeploymentTool: ToolConfig<
VercelGetDeploymentParams,
VercelGetDeploymentResponse
> = {
id: 'vercel_get_deployment',
name: 'Vercel Get Deployment',
description: 'Get details of a specific Vercel deployment',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The unique deployment identifier or hostname',
},
withGitRepoInfo: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Whether to add in gitRepo information (true/false)',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetDeploymentParams) => {
const query = new URLSearchParams()
if (params.withGitRepoInfo) query.set('withGitRepoInfo', params.withGitRepoInfo)
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v13/deployments/${params.deploymentId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetDeploymentParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
url: data.url ?? '',
readyState: data.readyState ?? 'UNKNOWN',
status: data.status ?? data.readyState ?? 'UNKNOWN',
target: data.target ?? null,
createdAt: data.createdAt ?? data.created,
buildingAt: data.buildingAt ?? null,
ready: data.ready ?? null,
source: data.source ?? '',
alias: data.alias ?? [],
regions: data.regions ?? [],
inspectorUrl: data.inspectorUrl ?? '',
projectId: data.projectId ?? '',
creator: {
uid: data.creator?.uid ?? '',
username: data.creator?.username ?? '',
},
project: data.project
? {
id: data.project.id,
name: data.project.name,
framework: data.project.framework ?? null,
}
: null,
meta: data.meta ?? {},
gitSource: data.gitSource ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Deployment ID' },
name: { type: 'string', description: 'Deployment name' },
url: { type: 'string', description: 'Unique deployment URL' },
readyState: {
type: 'string',
description: 'Deployment ready state: QUEUED, BUILDING, ERROR, INITIALIZING, READY, CANCELED',
},
status: {
type: 'string',
description: 'Deployment status',
},
target: { type: 'string', description: 'Target environment', optional: true },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
buildingAt: { type: 'number', description: 'Build start timestamp', optional: true },
ready: { type: 'number', description: 'Ready timestamp', optional: true },
source: {
type: 'string',
description: 'Deployment source: cli, git, redeploy, import, v0-web, etc.',
},
alias: {
type: 'array',
description: 'Assigned aliases',
items: { type: 'string', description: 'Alias domain' },
},
regions: {
type: 'array',
description: 'Deployment regions',
items: { type: 'string', description: 'Region code' },
},
inspectorUrl: { type: 'string', description: 'Vercel inspector URL' },
projectId: { type: 'string', description: 'Associated project ID' },
creator: {
type: 'object',
description: 'Creator information',
properties: {
uid: { type: 'string', description: 'Creator user ID' },
username: { type: 'string', description: 'Creator username' },
},
},
project: {
type: 'object',
description: 'Associated project',
optional: true,
properties: {
id: { type: 'string', description: 'Project ID' },
name: { type: 'string', description: 'Project name' },
framework: { type: 'string', description: 'Project framework', optional: true },
},
},
meta: {
type: 'object',
description: 'Deployment metadata (key-value strings)',
properties: {
githubCommitSha: { type: 'string', description: 'GitHub commit SHA', optional: true },
githubCommitMessage: {
type: 'string',
description: 'GitHub commit message',
optional: true,
},
githubCommitRef: { type: 'string', description: 'GitHub branch/ref', optional: true },
githubRepo: { type: 'string', description: 'GitHub repository', optional: true },
githubOrg: { type: 'string', description: 'GitHub organization', optional: true },
githubCommitAuthorName: {
type: 'string',
description: 'Commit author name',
optional: true,
},
},
},
gitSource: {
type: 'object',
description: 'Git source information',
optional: true,
properties: {
type: {
type: 'string',
description: 'Git provider type (e.g., github, gitlab, bitbucket)',
},
ref: { type: 'string', description: 'Git ref (branch or tag)' },
sha: { type: 'string', description: 'Git commit SHA' },
repoId: { type: 'string', description: 'Repository ID', optional: true },
},
},
},
}

View File

@@ -1,135 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelGetDeploymentEventsParams,
VercelGetDeploymentEventsResponse,
} from '@/tools/vercel/types'
export const vercelGetDeploymentEventsTool: ToolConfig<
VercelGetDeploymentEventsParams,
VercelGetDeploymentEventsResponse
> = {
id: 'vercel_get_deployment_events',
name: 'Vercel Get Deployment Events',
description: 'Get build and runtime events for a Vercel deployment',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The unique deployment identifier or hostname',
},
direction: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Order of events by timestamp: backward or forward (default: forward)',
},
follow: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'When set to 1, returns live events as they happen',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of events to return (-1 for all)',
},
since: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timestamp to start pulling build logs from',
},
until: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timestamp to stop pulling build logs at',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetDeploymentEventsParams) => {
const query = new URLSearchParams()
if (params.direction) query.set('direction', params.direction)
if (params.follow !== undefined) query.set('follow', String(params.follow))
if (params.limit !== undefined) query.set('limit', String(params.limit))
if (params.since !== undefined) query.set('since', String(params.since))
if (params.until !== undefined) query.set('until', String(params.until))
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v3/deployments/${params.deploymentId.trim()}/events${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetDeploymentEventsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const events = (Array.isArray(data) ? data : (data.events ?? [])).map((e: any) => ({
type: e.type ?? null,
created: e.created ?? null,
date: e.date ?? null,
text: e.text ?? e.payload?.text ?? null,
serial: e.serial ?? null,
deploymentId: e.deploymentId ?? e.payload?.deploymentId ?? null,
id: e.id ?? null,
level: e.level ?? null,
}))
return {
success: true,
output: {
events,
count: events.length,
},
}
},
outputs: {
events: {
type: 'array',
description: 'List of deployment events',
items: {
type: 'object',
properties: {
type: {
type: 'string',
description:
'Event type: delimiter, command, stdout, stderr, exit, deployment-state, middleware, middleware-invocation, edge-function-invocation, metric, report, fatal',
},
created: { type: 'number', description: 'Event creation timestamp' },
date: { type: 'number', description: 'Event date timestamp' },
text: { type: 'string', description: 'Event text content' },
serial: { type: 'string', description: 'Event serial identifier' },
deploymentId: { type: 'string', description: 'Associated deployment ID' },
id: { type: 'string', description: 'Event unique identifier' },
level: { type: 'string', description: 'Event level: error or warning' },
},
},
},
count: {
type: 'number',
description: 'Number of events returned',
},
},
}

View File

@@ -1,94 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetDomainParams, VercelGetDomainResponse } from '@/tools/vercel/types'
export const vercelGetDomainTool: ToolConfig<VercelGetDomainParams, VercelGetDomainResponse> = {
id: 'vercel_get_domain',
name: 'Vercel Get Domain',
description: 'Get information about a specific domain in a Vercel account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain name to retrieve',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetDomainParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v5/domains/${params.domain.trim()}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetDomainParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const d = data.domain ?? data
return {
success: true,
output: {
id: d.id ?? null,
name: d.name ?? null,
verified: d.verified ?? false,
createdAt: d.createdAt ?? null,
expiresAt: d.expiresAt ?? null,
serviceType: d.serviceType ?? null,
nameservers: d.nameservers ?? [],
intendedNameservers: d.intendedNameservers ?? [],
customNameservers: d.customNameservers ?? [],
renew: d.renew ?? false,
boughtAt: d.boughtAt ?? null,
transferredAt: d.transferredAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Domain ID' },
name: { type: 'string', description: 'Domain name' },
verified: { type: 'boolean', description: 'Whether domain is verified' },
createdAt: { type: 'number', description: 'Creation timestamp' },
expiresAt: { type: 'number', description: 'Expiration timestamp' },
serviceType: { type: 'string', description: 'Service type (zeit.world, external, na)' },
nameservers: {
type: 'array',
description: 'Current nameservers',
items: { type: 'string' },
},
intendedNameservers: {
type: 'array',
description: 'Intended nameservers',
items: { type: 'string' },
},
customNameservers: {
type: 'array',
description: 'Custom nameservers',
items: { type: 'string' },
},
renew: { type: 'boolean', description: 'Whether auto-renewal is enabled' },
boughtAt: { type: 'number', description: 'Purchase timestamp' },
transferredAt: { type: 'number', description: 'Transfer completion timestamp' },
},
}

View File

@@ -1,107 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelGetDomainConfigParams,
VercelGetDomainConfigResponse,
} from '@/tools/vercel/types'
export const vercelGetDomainConfigTool: ToolConfig<
VercelGetDomainConfigParams,
VercelGetDomainConfigResponse
> = {
id: 'vercel_get_domain_config',
name: 'Vercel Get Domain Config',
description: 'Get the configuration for a domain in a Vercel account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain name to get configuration for',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetDomainConfigParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v6/domains/${params.domain.trim()}/config${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetDomainConfigParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const d = await response.json()
return {
success: true,
output: {
configuredBy: d.configuredBy ?? null,
acceptedChallenges: d.acceptedChallenges ?? [],
misconfigured: d.misconfigured ?? false,
recommendedIPv4: d.recommendedIPv4 ?? [],
recommendedCNAME: d.recommendedCNAME ?? [],
},
}
},
outputs: {
configuredBy: {
type: 'string',
description: 'How the domain is configured (CNAME, A, http, dns-01, or null)',
},
acceptedChallenges: {
type: 'array',
description: 'Accepted challenge types for certificate issuance (dns-01, http-01)',
items: { type: 'string' },
},
misconfigured: {
type: 'boolean',
description: 'Whether the domain is misconfigured for TLS certificate generation',
},
recommendedIPv4: {
type: 'array',
description: 'Recommended IPv4 addresses with rank values',
items: {
type: 'object',
properties: {
rank: { type: 'number', description: 'Priority rank (1 is preferred)' },
value: {
type: 'array',
description: 'IPv4 addresses',
items: { type: 'string' },
},
},
},
},
recommendedCNAME: {
type: 'array',
description: 'Recommended CNAME records with rank values',
items: {
type: 'object',
properties: {
rank: { type: 'number', description: 'Priority rank (1 is preferred)' },
value: { type: 'string', description: 'CNAME value' },
},
},
},
},
}

View File

@@ -1,100 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetEdgeConfigParams, VercelGetEdgeConfigResponse } from '@/tools/vercel/types'
export const vercelGetEdgeConfigTool: ToolConfig<
VercelGetEdgeConfigParams,
VercelGetEdgeConfigResponse
> = {
id: 'vercel_get_edge_config',
name: 'Vercel Get Edge Config',
description: 'Get details about a specific Edge Config store',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
edgeConfigId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Edge Config ID to look up',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetEdgeConfigParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/edge-config/${params.edgeConfigId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetEdgeConfigParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? null,
slug: data.slug ?? null,
ownerId: data.ownerId ?? null,
digest: data.digest ?? null,
createdAt: data.createdAt ?? null,
updatedAt: data.updatedAt ?? null,
itemCount: data.itemCount ?? 0,
sizeInBytes: data.sizeInBytes ?? 0,
},
}
},
outputs: {
id: {
type: 'string',
description: 'Edge Config ID',
},
slug: {
type: 'string',
description: 'Edge Config slug',
},
ownerId: {
type: 'string',
description: 'Owner ID',
},
digest: {
type: 'string',
description: 'Content digest hash',
},
createdAt: {
type: 'number',
description: 'Creation timestamp',
},
updatedAt: {
type: 'number',
description: 'Last update timestamp',
},
itemCount: {
type: 'number',
description: 'Number of items',
},
sizeInBytes: {
type: 'number',
description: 'Size in bytes',
},
},
}

View File

@@ -1,93 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelGetEdgeConfigItemsParams,
VercelGetEdgeConfigItemsResponse,
} from '@/tools/vercel/types'
export const vercelGetEdgeConfigItemsTool: ToolConfig<
VercelGetEdgeConfigItemsParams,
VercelGetEdgeConfigItemsResponse
> = {
id: 'vercel_get_edge_config_items',
name: 'Vercel Get Edge Config Items',
description: 'Get all items in an Edge Config store',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
edgeConfigId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Edge Config ID to get items from',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetEdgeConfigItemsParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/edge-config/${params.edgeConfigId.trim()}/items${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetEdgeConfigItemsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const rawItems = Array.isArray(data) ? data : (data.items ?? [])
const items = rawItems.map((item: any) => ({
key: item.key ?? null,
value: item.value ?? null,
description: item.description ?? null,
edgeConfigId: item.edgeConfigId ?? null,
createdAt: item.createdAt ?? null,
updatedAt: item.updatedAt ?? null,
}))
return {
success: true,
output: {
items,
count: items.length,
},
}
},
outputs: {
items: {
type: 'array',
description: 'List of Edge Config items',
items: {
type: 'object',
properties: {
key: { type: 'string', description: 'Item key' },
value: { type: 'json', description: 'Item value' },
description: { type: 'string', description: 'Item description' },
edgeConfigId: { type: 'string', description: 'Parent Edge Config ID' },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last update timestamp' },
},
},
},
count: {
type: 'number',
description: 'Number of items returned',
},
},
}

View File

@@ -1,99 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetEnvVarsParams, VercelGetEnvVarsResponse } from '@/tools/vercel/types'
export const vercelGetEnvVarsTool: ToolConfig<VercelGetEnvVarsParams, VercelGetEnvVarsResponse> = {
id: 'vercel_get_env_vars',
name: 'Vercel Get Environment Variables',
description: 'Retrieve environment variables for a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetEnvVarsParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v10/projects/${params.projectId.trim()}/env${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetEnvVarsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const envs = (data.envs ?? []).map((e: any) => ({
id: e.id,
key: e.key,
value: e.value ?? '',
type: e.type ?? 'plain',
target: e.target ?? [],
gitBranch: e.gitBranch ?? null,
comment: e.comment ?? null,
}))
return {
success: true,
output: {
envs,
count: envs.length,
},
}
},
outputs: {
envs: {
type: 'array',
description: 'List of environment variables',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Environment variable ID' },
key: { type: 'string', description: 'Variable name' },
value: { type: 'string', description: 'Variable value' },
type: {
type: 'string',
description: 'Variable type (secret, system, encrypted, plain, sensitive)',
},
target: {
type: 'array',
description: 'Target environments',
items: { type: 'string', description: 'Environment name' },
},
gitBranch: { type: 'string', description: 'Git branch filter', optional: true },
comment: {
type: 'string',
description: 'Comment providing context for the variable',
optional: true,
},
},
},
},
count: {
type: 'number',
description: 'Number of environment variables returned',
},
},
}

View File

@@ -1,89 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetProjectParams, VercelGetProjectResponse } from '@/tools/vercel/types'
export const vercelGetProjectTool: ToolConfig<VercelGetProjectParams, VercelGetProjectResponse> = {
id: 'vercel_get_project',
name: 'Vercel Get Project',
description: 'Get details of a specific Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelGetProjectParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v9/projects/${params.projectId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelGetProjectParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
framework: data.framework ?? null,
createdAt: data.createdAt,
updatedAt: data.updatedAt,
domains: data.domains ?? [],
link: data.link
? {
type: data.link.type,
repo: data.link.repo,
org: data.link.org,
}
: null,
},
}
},
outputs: {
id: { type: 'string', description: 'Project ID' },
name: { type: 'string', description: 'Project name' },
framework: { type: 'string', description: 'Project framework', optional: true },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
domains: {
type: 'array',
description: 'Project domains',
items: { type: 'string', description: 'Domain' },
},
link: {
type: 'object',
description: 'Git repository connection',
optional: true,
properties: {
type: { type: 'string', description: 'Repository type (github, gitlab, bitbucket)' },
repo: { type: 'string', description: 'Repository name' },
org: { type: 'string', description: 'Organization or owner' },
},
},
},
}

View File

@@ -1,98 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetTeamParams, VercelGetTeamResponse } from '@/tools/vercel/types'
export const vercelGetTeamTool: ToolConfig<VercelGetTeamParams, VercelGetTeamResponse> = {
id: 'vercel_get_team',
name: 'Vercel Get Team',
description: 'Get information about a specific Vercel team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
teamId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The team ID to retrieve',
},
},
request: {
url: (params: VercelGetTeamParams) => `https://api.vercel.com/v2/teams/${params.teamId.trim()}`,
method: 'GET',
headers: (params: VercelGetTeamParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const d = await response.json()
return {
success: true,
output: {
id: d.id ?? null,
slug: d.slug ?? null,
name: d.name ?? null,
avatar: d.avatar ?? null,
description: d.description ?? null,
createdAt: d.createdAt ?? null,
updatedAt: d.updatedAt ?? null,
creatorId: d.creatorId ?? null,
membership: d.membership
? {
uid: d.membership.uid ?? null,
teamId: d.membership.teamId ?? null,
role: d.membership.role ?? null,
confirmed: d.membership.confirmed ?? false,
created: d.membership.created ?? null,
createdAt: d.membership.createdAt ?? null,
accessRequestedAt: d.membership.accessRequestedAt ?? null,
teamRoles: d.membership.teamRoles ?? [],
teamPermissions: d.membership.teamPermissions ?? [],
}
: null,
},
}
},
outputs: {
id: { type: 'string', description: 'Team ID' },
slug: { type: 'string', description: 'Team slug' },
name: { type: 'string', description: 'Team name' },
avatar: { type: 'string', description: 'Avatar file ID' },
description: { type: 'string', description: 'Short team description' },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
updatedAt: { type: 'number', description: 'Last update timestamp in milliseconds' },
creatorId: { type: 'string', description: 'User ID of team creator' },
membership: {
type: 'object',
description: 'Current user membership details',
properties: {
uid: { type: 'string', description: 'User ID of the member' },
teamId: { type: 'string', description: 'Team ID' },
role: { type: 'string', description: 'Membership role' },
confirmed: { type: 'boolean', description: 'Whether membership is confirmed' },
created: { type: 'number', description: 'Membership creation timestamp' },
createdAt: { type: 'number', description: 'Membership creation timestamp (milliseconds)' },
accessRequestedAt: { type: 'number', description: 'When access was requested' },
teamRoles: {
type: 'array',
description: 'Team role assignments',
items: { type: 'string', description: 'Role name' },
},
teamPermissions: {
type: 'array',
description: 'Team permission assignments',
items: { type: 'string', description: 'Permission name' },
},
},
},
},
}

View File

@@ -1,73 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelGetUserParams, VercelGetUserResponse } from '@/tools/vercel/types'
export const vercelGetUserTool: ToolConfig<VercelGetUserParams, VercelGetUserResponse> = {
id: 'vercel_get_user',
name: 'Vercel Get User',
description: 'Get information about the authenticated Vercel user',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
},
request: {
url: () => 'https://api.vercel.com/v2/user',
method: 'GET',
headers: (params: VercelGetUserParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const d = data.user ?? data
return {
success: true,
output: {
id: d.id ?? null,
email: d.email ?? null,
username: d.username ?? null,
name: d.name ?? null,
avatar: d.avatar ?? null,
defaultTeamId: d.defaultTeamId ?? null,
createdAt: d.createdAt ?? null,
stagingPrefix: d.stagingPrefix ?? null,
softBlock: d.softBlock
? {
blockedAt: d.softBlock.blockedAt ?? null,
reason: d.softBlock.reason ?? null,
}
: null,
hasTrialAvailable: d.hasTrialAvailable ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'User ID' },
email: { type: 'string', description: 'User email' },
username: { type: 'string', description: 'Username' },
name: { type: 'string', description: 'Display name' },
avatar: { type: 'string', description: 'SHA1 hash of the avatar' },
defaultTeamId: { type: 'string', description: 'Default team ID' },
createdAt: { type: 'number', description: 'Account creation timestamp in milliseconds' },
stagingPrefix: { type: 'string', description: 'Prefix for preview deployment URLs' },
softBlock: {
type: 'object',
description: 'Account restriction details if blocked',
properties: {
blockedAt: { type: 'number', description: 'When the account was blocked' },
reason: { type: 'string', description: 'Reason for the block' },
},
},
hasTrialAvailable: { type: 'boolean', description: 'Whether a trial is available' },
},
}

View File

@@ -1,126 +0,0 @@
// Deployment tools
// Domain tools
import { vercelAddDomainTool } from '@/tools/vercel/add_domain'
// Project tools
import { vercelAddProjectDomainTool } from '@/tools/vercel/add_project_domain'
import { vercelCancelDeploymentTool } from '@/tools/vercel/cancel_deployment'
// Alias tools
import { vercelCreateAliasTool } from '@/tools/vercel/create_alias'
// Check tools
import { vercelCreateCheckTool } from '@/tools/vercel/create_check'
import { vercelCreateDeploymentTool } from '@/tools/vercel/create_deployment'
// DNS tools
import { vercelCreateDnsRecordTool } from '@/tools/vercel/create_dns_record'
// Edge Config tools
import { vercelCreateEdgeConfigTool } from '@/tools/vercel/create_edge_config'
// Environment variable tools
import { vercelCreateEnvVarTool } from '@/tools/vercel/create_env_var'
import { vercelCreateProjectTool } from '@/tools/vercel/create_project'
// Webhook tools
import { vercelCreateWebhookTool } from '@/tools/vercel/create_webhook'
import { vercelDeleteAliasTool } from '@/tools/vercel/delete_alias'
import { vercelDeleteDeploymentTool } from '@/tools/vercel/delete_deployment'
import { vercelDeleteDnsRecordTool } from '@/tools/vercel/delete_dns_record'
import { vercelDeleteDomainTool } from '@/tools/vercel/delete_domain'
import { vercelDeleteEnvVarTool } from '@/tools/vercel/delete_env_var'
import { vercelDeleteProjectTool } from '@/tools/vercel/delete_project'
import { vercelDeleteWebhookTool } from '@/tools/vercel/delete_webhook'
import { vercelGetAliasTool } from '@/tools/vercel/get_alias'
import { vercelGetCheckTool } from '@/tools/vercel/get_check'
import { vercelGetDeploymentTool } from '@/tools/vercel/get_deployment'
import { vercelGetDeploymentEventsTool } from '@/tools/vercel/get_deployment_events'
import { vercelGetDomainTool } from '@/tools/vercel/get_domain'
import { vercelGetDomainConfigTool } from '@/tools/vercel/get_domain_config'
import { vercelGetEdgeConfigTool } from '@/tools/vercel/get_edge_config'
import { vercelGetEdgeConfigItemsTool } from '@/tools/vercel/get_edge_config_items'
import { vercelGetEnvVarsTool } from '@/tools/vercel/get_env_vars'
import { vercelGetProjectTool } from '@/tools/vercel/get_project'
// Team & User tools
import { vercelGetTeamTool } from '@/tools/vercel/get_team'
import { vercelGetUserTool } from '@/tools/vercel/get_user'
import { vercelListAliasesTool } from '@/tools/vercel/list_aliases'
import { vercelListChecksTool } from '@/tools/vercel/list_checks'
import { vercelListDeploymentFilesTool } from '@/tools/vercel/list_deployment_files'
import { vercelListDeploymentsTool } from '@/tools/vercel/list_deployments'
import { vercelListDnsRecordsTool } from '@/tools/vercel/list_dns_records'
import { vercelListDomainsTool } from '@/tools/vercel/list_domains'
import { vercelListEdgeConfigsTool } from '@/tools/vercel/list_edge_configs'
import { vercelListProjectDomainsTool } from '@/tools/vercel/list_project_domains'
import { vercelListProjectsTool } from '@/tools/vercel/list_projects'
import { vercelListTeamMembersTool } from '@/tools/vercel/list_team_members'
import { vercelListTeamsTool } from '@/tools/vercel/list_teams'
import { vercelListWebhooksTool } from '@/tools/vercel/list_webhooks'
import { vercelPauseProjectTool } from '@/tools/vercel/pause_project'
import { vercelRemoveProjectDomainTool } from '@/tools/vercel/remove_project_domain'
import { vercelRerequestCheckTool } from '@/tools/vercel/rerequest_check'
import { vercelUnpauseProjectTool } from '@/tools/vercel/unpause_project'
import { vercelUpdateCheckTool } from '@/tools/vercel/update_check'
import { vercelUpdateEdgeConfigItemsTool } from '@/tools/vercel/update_edge_config_items'
import { vercelUpdateEnvVarTool } from '@/tools/vercel/update_env_var'
import { vercelUpdateProjectTool } from '@/tools/vercel/update_project'
export {
// Deployments
vercelListDeploymentsTool,
vercelGetDeploymentTool,
vercelCreateDeploymentTool,
vercelCancelDeploymentTool,
vercelDeleteDeploymentTool,
vercelGetDeploymentEventsTool,
vercelListDeploymentFilesTool,
// Projects
vercelListProjectsTool,
vercelGetProjectTool,
vercelCreateProjectTool,
vercelUpdateProjectTool,
vercelDeleteProjectTool,
vercelPauseProjectTool,
vercelUnpauseProjectTool,
vercelListProjectDomainsTool,
vercelAddProjectDomainTool,
vercelRemoveProjectDomainTool,
// Environment Variables
vercelGetEnvVarsTool,
vercelCreateEnvVarTool,
vercelUpdateEnvVarTool,
vercelDeleteEnvVarTool,
// Domains
vercelListDomainsTool,
vercelGetDomainTool,
vercelAddDomainTool,
vercelDeleteDomainTool,
vercelGetDomainConfigTool,
// DNS
vercelListDnsRecordsTool,
vercelCreateDnsRecordTool,
vercelDeleteDnsRecordTool,
// Aliases
vercelListAliasesTool,
vercelGetAliasTool,
vercelCreateAliasTool,
vercelDeleteAliasTool,
// Edge Config
vercelListEdgeConfigsTool,
vercelGetEdgeConfigTool,
vercelCreateEdgeConfigTool,
vercelGetEdgeConfigItemsTool,
vercelUpdateEdgeConfigItemsTool,
// Teams & User
vercelListTeamsTool,
vercelGetTeamTool,
vercelListTeamMembersTool,
vercelGetUserTool,
// Webhooks
vercelListWebhooksTool,
vercelCreateWebhookTool,
vercelDeleteWebhookTool,
// Checks
vercelCreateCheckTool,
vercelGetCheckTool,
vercelListChecksTool,
vercelUpdateCheckTool,
vercelRerequestCheckTool,
}
export * from './types'

View File

@@ -1,107 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelListAliasesParams, VercelListAliasesResponse } from '@/tools/vercel/types'
export const vercelListAliasesTool: ToolConfig<VercelListAliasesParams, VercelListAliasesResponse> =
{
id: 'vercel_list_aliases',
name: 'Vercel List Aliases',
description: 'List aliases for a Vercel project or team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter aliases by project ID',
},
domain: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter aliases by domain',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of aliases to return',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListAliasesParams) => {
const query = new URLSearchParams()
if (params.projectId) query.set('projectId', params.projectId.trim())
if (params.domain) query.set('domain', params.domain.trim())
if (params.limit) query.set('limit', String(params.limit))
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v4/aliases${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListAliasesParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const aliases = (data.aliases ?? []).map((a: any) => ({
uid: a.uid ?? null,
alias: a.alias ?? null,
deploymentId: a.deploymentId ?? null,
projectId: a.projectId ?? null,
createdAt: a.createdAt ?? null,
updatedAt: a.updatedAt ?? null,
}))
return {
success: true,
output: {
aliases,
count: aliases.length,
hasMore: data.pagination?.next != null,
},
}
},
outputs: {
aliases: {
type: 'array',
description: 'List of aliases',
items: {
type: 'object',
properties: {
uid: { type: 'string', description: 'Alias ID' },
alias: { type: 'string', description: 'Alias hostname' },
deploymentId: { type: 'string', description: 'Associated deployment ID' },
projectId: { type: 'string', description: 'Associated project ID' },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
updatedAt: { type: 'number', description: 'Last update timestamp in milliseconds' },
},
},
},
count: {
type: 'number',
description: 'Number of aliases returned',
},
hasMore: {
type: 'boolean',
description: 'Whether more aliases are available',
},
},
}

View File

@@ -1,99 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelListChecksParams, VercelListChecksResponse } from '@/tools/vercel/types'
export const vercelListChecksTool: ToolConfig<VercelListChecksParams, VercelListChecksResponse> = {
id: 'vercel_list_checks',
name: 'Vercel List Checks',
description: 'List all checks for a deployment',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Deployment ID to list checks for',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListChecksParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/deployments/${params.deploymentId.trim()}/checks${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListChecksParams) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const checks = (data.checks ?? []).map((check: Record<string, unknown>) => ({
id: check.id,
name: check.name,
status: check.status ?? 'registered',
conclusion: check.conclusion ?? null,
blocking: check.blocking ?? false,
deploymentId: check.deploymentId,
integrationId: check.integrationId ?? null,
externalId: check.externalId ?? null,
detailsUrl: check.detailsUrl ?? null,
path: check.path ?? null,
rerequestable: check.rerequestable ?? false,
createdAt: check.createdAt,
updatedAt: check.updatedAt,
startedAt: check.startedAt ?? null,
completedAt: check.completedAt ?? null,
}))
return {
success: true,
output: {
checks,
count: checks.length,
},
}
},
outputs: {
checks: {
type: 'array',
description: 'List of deployment checks',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Check ID' },
name: { type: 'string', description: 'Check name' },
status: { type: 'string', description: 'Check status' },
conclusion: { type: 'string', description: 'Check conclusion' },
blocking: { type: 'boolean', description: 'Whether the check blocks the deployment' },
deploymentId: { type: 'string', description: 'Associated deployment ID' },
integrationId: { type: 'string', description: 'Associated integration ID' },
externalId: { type: 'string', description: 'External identifier' },
detailsUrl: { type: 'string', description: 'URL with details about the check' },
path: { type: 'string', description: 'Page path being checked' },
rerequestable: { type: 'boolean', description: 'Whether the check can be rerequested' },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last update timestamp' },
startedAt: { type: 'number', description: 'Start timestamp' },
completedAt: { type: 'number', description: 'Completion timestamp' },
},
},
},
count: { type: 'number', description: 'Total number of checks' },
},
}

View File

@@ -1,114 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelListDeploymentFilesParams,
VercelListDeploymentFilesResponse,
} from '@/tools/vercel/types'
export const vercelListDeploymentFilesTool: ToolConfig<
VercelListDeploymentFilesParams,
VercelListDeploymentFilesResponse
> = {
id: 'vercel_list_deployment_files',
name: 'Vercel List Deployment Files',
description: 'List files in a Vercel deployment',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The deployment ID to list files for',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListDeploymentFilesParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v6/deployments/${params.deploymentId.trim()}/files${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListDeploymentFilesParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const files = (Array.isArray(data) ? data : (data.files ?? [])).map((f: any) => ({
name: f.name ?? null,
type: f.type ?? null,
uid: f.uid ?? null,
mode: f.mode ?? null,
contentType: f.contentType ?? null,
children: f.children ?? [],
}))
return {
success: true,
output: {
files,
count: files.length,
},
}
},
outputs: {
files: {
type: 'array',
description: 'List of deployment files',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'The name of the file tree entry' },
type: {
type: 'string',
description: 'File type: directory, file, symlink, lambda, middleware, or invalid',
},
uid: {
type: 'string',
description: 'Unique file identifier (only valid for file type)',
optional: true,
},
mode: { type: 'number', description: 'File mode indicating file type and permissions' },
contentType: {
type: 'string',
description: 'Content-type of the file (only valid for file type)',
optional: true,
},
children: {
type: 'array',
description: 'Child files of the directory (only valid for directory type)',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'File name' },
type: { type: 'string', description: 'Entry type' },
uid: { type: 'string', description: 'File identifier', optional: true },
},
},
},
},
},
},
count: {
type: 'number',
description: 'Number of files returned',
},
},
}

View File

@@ -1,170 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelListDeploymentsParams,
VercelListDeploymentsResponse,
} from '@/tools/vercel/types'
export const vercelListDeploymentsTool: ToolConfig<
VercelListDeploymentsParams,
VercelListDeploymentsResponse
> = {
id: 'vercel_list_deployments',
name: 'Vercel List Deployments',
description: 'List deployments for a Vercel project or team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter deployments by project ID or name',
},
target: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by environment: production or staging',
},
state: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by state: BUILDING, ERROR, INITIALIZING, QUEUED, READY, CANCELED',
},
app: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by deployment name',
},
since: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Get deployments created after this JavaScript timestamp',
},
until: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Get deployments created before this JavaScript timestamp',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of deployments to return per request',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListDeploymentsParams) => {
const query = new URLSearchParams()
if (params.projectId) query.set('projectId', params.projectId.trim())
if (params.target) query.set('target', params.target)
if (params.state) query.set('state', params.state)
if (params.app) query.set('app', params.app.trim())
if (params.since) query.set('since', String(params.since))
if (params.until) query.set('until', String(params.until))
if (params.limit) query.set('limit', String(params.limit))
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v6/deployments${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListDeploymentsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const deployments = (data.deployments ?? []).map((d: any) => ({
uid: d.uid,
name: d.name,
url: d.url ?? null,
state: d.state ?? d.readyState ?? 'UNKNOWN',
target: d.target ?? null,
created: d.created ?? d.createdAt,
projectId: d.projectId ?? '',
source: d.source ?? '',
inspectorUrl: d.inspectorUrl ?? '',
creator: {
uid: d.creator?.uid ?? '',
email: d.creator?.email ?? '',
username: d.creator?.username ?? '',
},
meta: d.meta ?? {},
}))
return {
success: true,
output: {
deployments,
count: deployments.length,
hasMore: data.pagination?.next != null,
},
}
},
outputs: {
deployments: {
type: 'array',
description: 'List of deployments',
items: {
type: 'object',
properties: {
uid: { type: 'string', description: 'Unique deployment identifier' },
name: { type: 'string', description: 'Deployment name' },
url: { type: 'string', description: 'Deployment URL', optional: true },
state: {
type: 'string',
description:
'Deployment state: BUILDING, ERROR, INITIALIZING, QUEUED, READY, CANCELED, DELETED',
},
target: { type: 'string', description: 'Target environment', optional: true },
created: { type: 'number', description: 'Creation timestamp' },
projectId: { type: 'string', description: 'Associated project ID' },
source: {
type: 'string',
description:
'Deployment source: api-trigger-git-deploy, cli, clone/repo, git, import, import/repo, redeploy, v0-web',
},
inspectorUrl: { type: 'string', description: 'Vercel inspector URL' },
creator: {
type: 'object',
description: 'Creator information',
properties: {
uid: { type: 'string', description: 'Creator user ID' },
email: { type: 'string', description: 'Creator email' },
username: { type: 'string', description: 'Creator username' },
},
},
meta: { type: 'object', description: 'Git provider metadata (key-value strings)' },
},
},
},
count: {
type: 'number',
description: 'Number of deployments returned',
},
hasMore: {
type: 'boolean',
description: 'Whether more deployments are available',
},
},
}

View File

@@ -1,116 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelListDnsRecordsParams, VercelListDnsRecordsResponse } from '@/tools/vercel/types'
export const vercelListDnsRecordsTool: ToolConfig<
VercelListDnsRecordsParams,
VercelListDnsRecordsResponse
> = {
id: 'vercel_list_dns_records',
name: 'Vercel List DNS Records',
description: 'List all DNS records for a domain in a Vercel account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The domain name to list records for',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of records to return',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListDnsRecordsParams) => {
const query = new URLSearchParams()
if (params.limit) query.set('limit', String(params.limit))
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v4/domains/${params.domain.trim()}/records${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListDnsRecordsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const records = (data.records ?? []).map((r: any) => ({
id: r.id ?? null,
slug: r.slug ?? null,
name: r.name ?? null,
type: r.type ?? null,
value: r.value ?? null,
ttl: r.ttl ?? null,
mxPriority: r.mxPriority ?? null,
priority: r.priority ?? null,
creator: r.creator ?? null,
createdAt: r.createdAt ?? null,
updatedAt: r.updatedAt ?? null,
comment: r.comment ?? null,
}))
return {
success: true,
output: {
records,
count: records.length,
hasMore: data.pagination?.next != null,
},
}
},
outputs: {
records: {
type: 'array',
description: 'List of DNS records',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Record ID' },
slug: { type: 'string', description: 'Record slug' },
name: { type: 'string', description: 'Record name' },
type: {
type: 'string',
description: 'Record type (A, AAAA, ALIAS, CAA, CNAME, HTTPS, MX, SRV, TXT, NS)',
},
value: { type: 'string', description: 'Record value' },
ttl: { type: 'number', description: 'Time to live in seconds' },
mxPriority: { type: 'number', description: 'MX record priority' },
priority: { type: 'number', description: 'Record priority' },
creator: { type: 'string', description: 'Creator identifier' },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last update timestamp' },
comment: { type: 'string', description: 'Record comment' },
},
},
},
count: {
type: 'number',
description: 'Number of records returned',
},
hasMore: {
type: 'boolean',
description: 'Whether more records are available',
},
},
}

View File

@@ -1,109 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelListDomainsParams, VercelListDomainsResponse } from '@/tools/vercel/types'
export const vercelListDomainsTool: ToolConfig<VercelListDomainsParams, VercelListDomainsResponse> =
{
id: 'vercel_list_domains',
name: 'Vercel List Domains',
description: 'List all domains in a Vercel account or team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of domains to return (default 20)',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListDomainsParams) => {
const query = new URLSearchParams()
if (params.limit) query.set('limit', String(params.limit))
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v5/domains${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListDomainsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const domains = (data.domains ?? []).map((d: any) => ({
id: d.id,
name: d.name,
verified: d.verified ?? false,
createdAt: d.createdAt,
expiresAt: d.expiresAt ?? null,
serviceType: d.serviceType ?? 'external',
nameservers: d.nameservers ?? [],
intendedNameservers: d.intendedNameservers ?? [],
renew: d.renew ?? false,
boughtAt: d.boughtAt ?? null,
}))
return {
success: true,
output: {
domains,
count: domains.length,
hasMore: data.pagination?.next != null,
},
}
},
outputs: {
domains: {
type: 'array',
description: 'List of domains',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Domain ID' },
name: { type: 'string', description: 'Domain name' },
verified: { type: 'boolean', description: 'Whether domain is verified' },
createdAt: { type: 'number', description: 'Creation timestamp' },
expiresAt: { type: 'number', description: 'Expiration timestamp' },
serviceType: { type: 'string', description: 'Service type (zeit.world, external, na)' },
nameservers: {
type: 'array',
description: 'Current nameservers',
items: { type: 'string' },
},
intendedNameservers: {
type: 'array',
description: 'Intended nameservers',
items: { type: 'string' },
},
renew: { type: 'boolean', description: 'Whether auto-renewal is enabled' },
boughtAt: { type: 'number', description: 'Purchase timestamp' },
},
},
},
count: {
type: 'number',
description: 'Number of domains returned',
},
hasMore: {
type: 'boolean',
description: 'Whether more domains are available',
},
},
}

View File

@@ -1,91 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelListEdgeConfigsParams,
VercelListEdgeConfigsResponse,
} from '@/tools/vercel/types'
export const vercelListEdgeConfigsTool: ToolConfig<
VercelListEdgeConfigsParams,
VercelListEdgeConfigsResponse
> = {
id: 'vercel_list_edge_configs',
name: 'Vercel List Edge Configs',
description: 'List all Edge Config stores for a team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListEdgeConfigsParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/edge-config${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListEdgeConfigsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const items = Array.isArray(data) ? data : (data.edgeConfigs ?? [])
const edgeConfigs = items.map((ec: any) => ({
id: ec.id ?? null,
slug: ec.slug ?? null,
ownerId: ec.ownerId ?? null,
digest: ec.digest ?? null,
createdAt: ec.createdAt ?? null,
updatedAt: ec.updatedAt ?? null,
itemCount: ec.itemCount ?? 0,
sizeInBytes: ec.sizeInBytes ?? 0,
}))
return {
success: true,
output: {
edgeConfigs,
count: edgeConfigs.length,
},
}
},
outputs: {
edgeConfigs: {
type: 'array',
description: 'List of Edge Config stores',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Edge Config ID' },
slug: { type: 'string', description: 'Edge Config slug' },
ownerId: { type: 'string', description: 'Owner ID' },
digest: { type: 'string', description: 'Content digest hash' },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last update timestamp' },
itemCount: { type: 'number', description: 'Number of items' },
sizeInBytes: { type: 'number', description: 'Size in bytes' },
},
},
},
count: {
type: 'number',
description: 'Number of Edge Configs returned',
},
},
}

View File

@@ -1,116 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelListProjectDomainsParams,
VercelListProjectDomainsResponse,
} from '@/tools/vercel/types'
export const vercelListProjectDomainsTool: ToolConfig<
VercelListProjectDomainsParams,
VercelListProjectDomainsResponse
> = {
id: 'vercel_list_project_domains',
name: 'Vercel List Project Domains',
description: 'List all domains for a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of domains to return',
},
},
request: {
url: (params: VercelListProjectDomainsParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
if (params.limit) query.set('limit', String(params.limit))
const qs = query.toString()
return `https://api.vercel.com/v9/projects/${params.projectId.trim()}/domains${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListProjectDomainsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const domains = (data.domains ?? []).map(
(d: {
name: string
apexName: string
redirect: string | null
redirectStatusCode: number | null
verified: boolean
gitBranch: string | null
createdAt: number
updatedAt: number
}) => ({
name: d.name,
apexName: d.apexName,
redirect: d.redirect ?? null,
redirectStatusCode: d.redirectStatusCode ?? null,
verified: d.verified,
gitBranch: d.gitBranch ?? null,
createdAt: d.createdAt,
updatedAt: d.updatedAt,
})
)
return {
success: true,
output: {
domains,
count: domains.length,
hasMore: data.pagination?.next != null,
},
}
},
outputs: {
domains: {
type: 'array',
description: 'List of project domains',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Domain name' },
apexName: { type: 'string', description: 'Apex domain name' },
redirect: { type: 'string', description: 'Redirect target', optional: true },
redirectStatusCode: {
type: 'number',
description: 'Redirect status code',
optional: true,
},
verified: { type: 'boolean', description: 'Whether the domain is verified' },
gitBranch: { type: 'string', description: 'Git branch for the domain', optional: true },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
},
},
},
count: { type: 'number', description: 'Number of domains returned' },
hasMore: { type: 'boolean', description: 'Whether more domains are available' },
},
}

View File

@@ -1,106 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelListProjectsParams, VercelListProjectsResponse } from '@/tools/vercel/types'
export const vercelListProjectsTool: ToolConfig<
VercelListProjectsParams,
VercelListProjectsResponse
> = {
id: 'vercel_list_projects',
name: 'Vercel List Projects',
description: 'List all projects in a Vercel team or account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search projects by name',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of projects to return',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListProjectsParams) => {
const query = new URLSearchParams()
if (params.search) query.set('search', params.search)
if (params.limit) query.set('limit', String(params.limit))
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v10/projects${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListProjectsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const projects = (data.projects ?? []).map((p: any) => ({
id: p.id,
name: p.name,
framework: p.framework ?? null,
createdAt: p.createdAt,
updatedAt: p.updatedAt,
domains: p.domains ?? [],
}))
return {
success: true,
output: {
projects,
count: projects.length,
hasMore: data.pagination?.next != null,
},
}
},
outputs: {
projects: {
type: 'array',
description: 'List of projects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Project ID' },
name: { type: 'string', description: 'Project name' },
framework: { type: 'string', description: 'Framework', optional: true },
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
domains: {
type: 'array',
description: 'Project domains',
items: { type: 'string', description: 'Domain' },
},
},
},
},
count: {
type: 'number',
description: 'Number of projects returned',
},
hasMore: {
type: 'boolean',
description: 'Whether more projects are available',
},
},
}

View File

@@ -1,151 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelListTeamMembersParams,
VercelListTeamMembersResponse,
} from '@/tools/vercel/types'
export const vercelListTeamMembersTool: ToolConfig<
VercelListTeamMembersParams,
VercelListTeamMembersResponse
> = {
id: 'vercel_list_team_members',
name: 'Vercel List Team Members',
description: 'List all members of a Vercel team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
teamId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The team ID to list members for',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of members to return',
},
role: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by role (OWNER, MEMBER, DEVELOPER, SECURITY, BILLING, VIEWER, VIEWER_FOR_PLUS, CONTRIBUTOR)',
},
since: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timestamp in milliseconds to only include members added since then',
},
until: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timestamp in milliseconds to only include members added until then',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search team members by their name, username, and email',
},
},
request: {
url: (params: VercelListTeamMembersParams) => {
const query = new URLSearchParams()
if (params.limit) query.set('limit', String(params.limit))
if (params.role) query.set('role', params.role.trim())
if (params.since) query.set('since', String(params.since))
if (params.until) query.set('until', String(params.until))
if (params.search) query.set('search', params.search.trim())
const qs = query.toString()
return `https://api.vercel.com/v3/teams/${params.teamId.trim()}/members${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListTeamMembersParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const members = (data.members ?? []).map((m: any) => ({
uid: m.uid ?? null,
email: m.email ?? null,
username: m.username ?? null,
name: m.name ?? null,
avatar: m.avatar ?? null,
role: m.role ?? null,
confirmed: m.confirmed ?? false,
createdAt: m.createdAt ?? null,
joinedFrom: m.joinedFrom
? {
origin: m.joinedFrom.origin ?? null,
}
: null,
}))
return {
success: true,
output: {
members,
count: members.length,
pagination: data.pagination
? {
hasNext: data.pagination.hasNext ?? false,
count: data.pagination.count ?? 0,
}
: null,
},
}
},
outputs: {
members: {
type: 'array',
description: 'List of team members',
items: {
type: 'object',
properties: {
uid: { type: 'string', description: 'Member user ID' },
email: { type: 'string', description: 'Member email' },
username: { type: 'string', description: 'Member username' },
name: { type: 'string', description: 'Member full name' },
avatar: { type: 'string', description: 'Avatar file ID' },
role: { type: 'string', description: 'Member role' },
confirmed: { type: 'boolean', description: 'Whether membership is confirmed' },
createdAt: { type: 'number', description: 'Join timestamp in milliseconds' },
joinedFrom: {
type: 'object',
description: 'Origin of how the member joined',
properties: {
origin: { type: 'string', description: 'Join origin identifier' },
},
},
},
},
},
count: {
type: 'number',
description: 'Number of members returned',
},
pagination: {
type: 'object',
description: 'Pagination information',
properties: {
hasNext: { type: 'boolean', description: 'Whether there are more pages' },
count: { type: 'number', description: 'Items in current page' },
},
},
},
}

View File

@@ -1,132 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelListTeamsParams, VercelListTeamsResponse } from '@/tools/vercel/types'
export const vercelListTeamsTool: ToolConfig<VercelListTeamsParams, VercelListTeamsResponse> = {
id: 'vercel_list_teams',
name: 'Vercel List Teams',
description: 'List all teams in a Vercel account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of teams to return',
},
since: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timestamp in milliseconds to only include teams created since then',
},
until: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Timestamp in milliseconds to only include teams created until then',
},
},
request: {
url: (params: VercelListTeamsParams) => {
const query = new URLSearchParams()
if (params.limit) query.set('limit', String(params.limit))
if (params.since) query.set('since', String(params.since))
if (params.until) query.set('until', String(params.until))
const qs = query.toString()
return `https://api.vercel.com/v2/teams${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListTeamsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const teams = (data.teams ?? []).map((t: any) => ({
id: t.id ?? null,
slug: t.slug ?? null,
name: t.name ?? null,
avatar: t.avatar ?? null,
createdAt: t.createdAt ?? null,
updatedAt: t.updatedAt ?? null,
creatorId: t.creatorId ?? null,
membership: t.membership
? {
role: t.membership.role ?? null,
confirmed: t.membership.confirmed ?? false,
created: t.membership.created ?? null,
uid: t.membership.uid ?? null,
teamId: t.membership.teamId ?? null,
}
: null,
}))
return {
success: true,
output: {
teams,
count: teams.length,
pagination: data.pagination
? {
count: data.pagination.count ?? 0,
next: data.pagination.next ?? null,
prev: data.pagination.prev ?? null,
}
: null,
},
}
},
outputs: {
teams: {
type: 'array',
description: 'List of teams',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Team ID' },
slug: { type: 'string', description: 'Team slug' },
name: { type: 'string', description: 'Team name' },
avatar: { type: 'string', description: 'Avatar file ID' },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
updatedAt: { type: 'number', description: 'Last update timestamp in milliseconds' },
creatorId: { type: 'string', description: 'User ID of team creator' },
membership: {
type: 'object',
description: 'Current user membership details',
properties: {
role: { type: 'string', description: 'Membership role' },
confirmed: { type: 'boolean', description: 'Whether membership is confirmed' },
created: { type: 'number', description: 'Membership creation timestamp' },
uid: { type: 'string', description: 'User ID of the member' },
teamId: { type: 'string', description: 'Team ID' },
},
},
},
},
},
count: {
type: 'number',
description: 'Number of teams returned',
},
pagination: {
type: 'object',
description: 'Pagination information',
properties: {
count: { type: 'number', description: 'Items in current page' },
next: { type: 'number', description: 'Timestamp for next page request' },
prev: { type: 'number', description: 'Timestamp for previous page request' },
},
},
},
}

View File

@@ -1,100 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelListWebhooksParams, VercelListWebhooksResponse } from '@/tools/vercel/types'
export const vercelListWebhooksTool: ToolConfig<
VercelListWebhooksParams,
VercelListWebhooksResponse
> = {
id: 'vercel_list_webhooks',
name: 'Vercel List Webhooks',
description: 'List webhooks for a Vercel project or team',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter webhooks by project ID',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelListWebhooksParams) => {
const query = new URLSearchParams()
if (params.projectId) query.set('projectId', params.projectId.trim())
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/webhooks${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params: VercelListWebhooksParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const webhooks = (Array.isArray(data) ? data : []).map((w: any) => ({
id: w.id ?? null,
url: w.url ?? null,
events: w.events ?? [],
ownerId: w.ownerId ?? null,
projectIds: w.projectIds ?? [],
createdAt: w.createdAt ?? null,
updatedAt: w.updatedAt ?? null,
}))
return {
success: true,
output: {
webhooks,
count: webhooks.length,
},
}
},
outputs: {
webhooks: {
type: 'array',
description: 'List of webhooks',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Webhook ID' },
url: { type: 'string', description: 'Webhook URL' },
events: {
type: 'array',
description: 'Events the webhook listens to',
items: { type: 'string', description: 'Event name' },
},
ownerId: { type: 'string', description: 'Owner ID' },
projectIds: {
type: 'array',
description: 'Associated project IDs',
items: { type: 'string', description: 'Project ID' },
},
createdAt: { type: 'number', description: 'Creation timestamp' },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
},
},
},
count: {
type: 'number',
description: 'Number of webhooks returned',
},
},
}

View File

@@ -1,65 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelPauseProjectParams, VercelPauseProjectResponse } from '@/tools/vercel/types'
export const vercelPauseProjectTool: ToolConfig<
VercelPauseProjectParams,
VercelPauseProjectResponse
> = {
id: 'vercel_pause_project',
name: 'Vercel Pause Project',
description: 'Pause a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelPauseProjectParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/projects/${params.projectId.trim()}/pause${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelPauseProjectParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
paused: data.paused ?? true,
},
}
},
outputs: {
id: { type: 'string', description: 'Project ID' },
name: { type: 'string', description: 'Project name' },
paused: { type: 'boolean', description: 'Whether the project is paused' },
},
}

View File

@@ -1,69 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelRemoveProjectDomainParams,
VercelRemoveProjectDomainResponse,
} from '@/tools/vercel/types'
export const vercelRemoveProjectDomainTool: ToolConfig<
VercelRemoveProjectDomainParams,
VercelRemoveProjectDomainResponse
> = {
id: 'vercel_remove_project_domain',
name: 'Vercel Remove Project Domain',
description: 'Remove a domain from a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Domain name to remove',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelRemoveProjectDomainParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v9/projects/${params.projectId.trim()}/domains/${params.domain.trim()}${qs ? `?${qs}` : ''}`
},
method: 'DELETE',
headers: (params: VercelRemoveProjectDomainParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async () => {
return {
success: true,
output: {
deleted: true,
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the domain was successfully removed' },
},
}

View File

@@ -1,65 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelRerequestCheckParams, VercelRerequestCheckResponse } from '@/tools/vercel/types'
export const vercelRerequestCheckTool: ToolConfig<
VercelRerequestCheckParams,
VercelRerequestCheckResponse
> = {
id: 'vercel_rerequest_check',
name: 'Vercel Rerequest Check',
description: 'Rerequest a deployment check',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Deployment ID the check belongs to',
},
checkId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Check ID to rerequest',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelRerequestCheckParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/deployments/${params.deploymentId.trim()}/checks/${params.checkId.trim()}/rerequest${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelRerequestCheckParams) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async () => {
return {
success: true,
output: {
rerequested: true,
},
}
},
outputs: {
rerequested: { type: 'boolean', description: 'Whether the check was successfully rerequested' },
},
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,65 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelUnpauseProjectParams, VercelUnpauseProjectResponse } from '@/tools/vercel/types'
export const vercelUnpauseProjectTool: ToolConfig<
VercelUnpauseProjectParams,
VercelUnpauseProjectResponse
> = {
id: 'vercel_unpause_project',
name: 'Vercel Unpause Project',
description: 'Unpause a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelUnpauseProjectParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/projects/${params.projectId.trim()}/unpause${qs ? `?${qs}` : ''}`
},
method: 'POST',
headers: (params: VercelUnpauseProjectParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
paused: data.paused ?? false,
},
}
},
outputs: {
id: { type: 'string', description: 'Project ID' },
name: { type: 'string', description: 'Project name' },
paused: { type: 'boolean', description: 'Whether the project is paused' },
},
}

View File

@@ -1,159 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelCheckResponse, VercelUpdateCheckParams } from '@/tools/vercel/types'
export const vercelUpdateCheckTool: ToolConfig<VercelUpdateCheckParams, VercelCheckResponse> = {
id: 'vercel_update_check',
name: 'Vercel Update Check',
description: 'Update an existing deployment check',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
deploymentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Deployment ID the check belongs to',
},
checkId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Check ID to update',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated name of the check',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated status: running or completed',
},
conclusion: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Check conclusion: canceled, failed, neutral, succeeded, or skipped',
},
detailsUrl: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'URL with details about the check',
},
externalId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'External identifier for the check',
},
path: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Page path being checked',
},
output: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON string with check output metrics',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelUpdateCheckParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/deployments/${params.deploymentId.trim()}/checks/${params.checkId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'PATCH',
headers: (params: VercelUpdateCheckParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelUpdateCheckParams) => {
const body: Record<string, unknown> = {}
if (params.name) body.name = params.name.trim()
if (params.status) body.status = params.status
if (params.conclusion) body.conclusion = params.conclusion
if (params.detailsUrl) body.detailsUrl = params.detailsUrl
if (params.externalId) body.externalId = params.externalId
if (params.path) body.path = params.path
if (params.output) {
try {
body.output = JSON.parse(params.output)
} catch {
body.output = params.output
}
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
status: data.status ?? 'registered',
conclusion: data.conclusion ?? null,
blocking: data.blocking ?? false,
deploymentId: data.deploymentId,
integrationId: data.integrationId ?? null,
externalId: data.externalId ?? null,
detailsUrl: data.detailsUrl ?? null,
path: data.path ?? null,
rerequestable: data.rerequestable ?? false,
createdAt: data.createdAt,
updatedAt: data.updatedAt,
startedAt: data.startedAt ?? null,
completedAt: data.completedAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Check ID' },
name: { type: 'string', description: 'Check name' },
status: { type: 'string', description: 'Check status: registered, running, or completed' },
conclusion: {
type: 'string',
description: 'Check conclusion: canceled, failed, neutral, succeeded, skipped, or stale',
optional: true,
},
blocking: { type: 'boolean', description: 'Whether the check blocks the deployment' },
deploymentId: { type: 'string', description: 'Associated deployment ID' },
integrationId: { type: 'string', description: 'Associated integration ID', optional: true },
externalId: { type: 'string', description: 'External identifier', optional: true },
detailsUrl: { type: 'string', description: 'URL with details about the check', optional: true },
path: { type: 'string', description: 'Page path being checked', optional: true },
rerequestable: { type: 'boolean', description: 'Whether the check can be rerequested' },
createdAt: { type: 'number', description: 'Creation timestamp in milliseconds' },
updatedAt: { type: 'number', description: 'Last update timestamp in milliseconds' },
startedAt: { type: 'number', description: 'Start timestamp in milliseconds', optional: true },
completedAt: {
type: 'number',
description: 'Completion timestamp in milliseconds',
optional: true,
},
},
}

View File

@@ -1,77 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type {
VercelUpdateEdgeConfigItemsParams,
VercelUpdateEdgeConfigItemsResponse,
} from '@/tools/vercel/types'
export const vercelUpdateEdgeConfigItemsTool: ToolConfig<
VercelUpdateEdgeConfigItemsParams,
VercelUpdateEdgeConfigItemsResponse
> = {
id: 'vercel_update_edge_config_items',
name: 'Vercel Update Edge Config Items',
description: 'Create, update, upsert, or delete items in an Edge Config store',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
edgeConfigId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Edge Config ID to update items in',
},
items: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'Array of operations: [{operation: "create"|"update"|"upsert"|"delete", key: string, value?: any}]',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelUpdateEdgeConfigItemsParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v1/edge-config/${params.edgeConfigId.trim()}/items${qs ? `?${qs}` : ''}`
},
method: 'PATCH',
headers: (params: VercelUpdateEdgeConfigItemsParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelUpdateEdgeConfigItemsParams) => {
const parsedItems = typeof params.items === 'string' ? JSON.parse(params.items) : params.items
return { items: parsedItems }
},
},
transformResponse: async () => {
return {
success: true,
output: {
status: 'ok',
},
}
},
outputs: {
status: {
type: 'string',
description: 'Operation status',
},
},
}

View File

@@ -1,149 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelUpdateEnvVarParams, VercelUpdateEnvVarResponse } from '@/tools/vercel/types'
export const vercelUpdateEnvVarTool: ToolConfig<
VercelUpdateEnvVarParams,
VercelUpdateEnvVarResponse
> = {
id: 'vercel_update_env_var',
name: 'Vercel Update Environment Variable',
description: 'Update an environment variable for a Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
envId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Environment variable ID to update',
},
key: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New variable name',
},
value: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New variable value',
},
target: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of target environments (production, preview, development)',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Variable type: system, secret, encrypted, plain, or sensitive',
},
gitBranch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Git branch to associate with the variable (requires target to include preview)',
},
comment: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comment to add context to the variable (max 500 characters)',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelUpdateEnvVarParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v9/projects/${params.projectId.trim()}/env/${params.envId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'PATCH',
headers: (params: VercelUpdateEnvVarParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelUpdateEnvVarParams) => {
const body: Record<string, unknown> = {}
if (params.key) body.key = params.key
if (params.value) body.value = params.value
if (params.target) body.target = params.target.split(',').map((t) => t.trim())
if (params.type) body.type = params.type
if (params.gitBranch) body.gitBranch = params.gitBranch
if (params.comment) body.comment = params.comment
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
key: data.key,
value: data.value ?? '',
type: data.type ?? 'plain',
target: data.target ?? [],
gitBranch: data.gitBranch ?? null,
comment: data.comment ?? null,
},
}
},
outputs: {
id: {
type: 'string',
description: 'Environment variable ID',
},
key: {
type: 'string',
description: 'Variable name',
},
value: {
type: 'string',
description: 'Variable value',
},
type: {
type: 'string',
description: 'Variable type (secret, system, encrypted, plain, sensitive)',
},
target: {
type: 'array',
description: 'Target environments',
items: { type: 'string', description: 'Environment name' },
},
gitBranch: {
type: 'string',
description: 'Git branch filter',
optional: true,
},
comment: {
type: 'string',
description: 'Comment providing context for the variable',
optional: true,
},
},
}

View File

@@ -1,106 +0,0 @@
import type { ToolConfig } from '@/tools/types'
import type { VercelUpdateProjectParams, VercelUpdateProjectResponse } from '@/tools/vercel/types'
export const vercelUpdateProjectTool: ToolConfig<
VercelUpdateProjectParams,
VercelUpdateProjectResponse
> = {
id: 'vercel_update_project',
name: 'Vercel Update Project',
description: 'Update an existing Vercel project',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Vercel Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or name',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New project name',
},
framework: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Project framework (e.g. nextjs, remix, vite)',
},
buildCommand: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom build command',
},
outputDirectory: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom output directory',
},
installCommand: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom install command',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Team ID to scope the request',
},
},
request: {
url: (params: VercelUpdateProjectParams) => {
const query = new URLSearchParams()
if (params.teamId) query.set('teamId', params.teamId.trim())
const qs = query.toString()
return `https://api.vercel.com/v9/projects/${params.projectId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'PATCH',
headers: (params: VercelUpdateProjectParams) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params: VercelUpdateProjectParams) => {
const body: Record<string, unknown> = {}
if (params.name) body.name = params.name.trim()
if (params.framework) body.framework = params.framework.trim()
if (params.buildCommand) body.buildCommand = params.buildCommand.trim()
if (params.outputDirectory) body.outputDirectory = params.outputDirectory.trim()
if (params.installCommand) body.installCommand = params.installCommand.trim()
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
framework: data.framework ?? null,
updatedAt: data.updatedAt,
},
}
},
outputs: {
id: { type: 'string', description: 'Project ID' },
name: { type: 'string', description: 'Project name' },
framework: { type: 'string', description: 'Project framework', optional: true },
updatedAt: { type: 'number', description: 'Last updated timestamp' },
},
}

175
bun.lock
View File

@@ -4,21 +4,16 @@
"workspaces": {
"": {
"name": "simstudio",
"dependencies": {
"@better-auth/sso": "1.4.18",
"@better-auth/stripe": "1.4.18",
"better-auth": "1.4.18",
},
"devDependencies": {
"@biomejs/biome": "2.0.0-beta.5",
"@next/env": "16.1.6",
"@next/env": "16.1.0-canary.21",
"@octokit/rest": "^21.0.0",
"@tailwindcss/typography": "0.5.19",
"drizzle-kit": "^0.31.4",
"glob": "13.0.0",
"husky": "9.1.7",
"lint-staged": "16.0.0",
"turbo": "2.8.10",
"turbo": "2.8.9",
},
},
"apps/docs": {
@@ -35,7 +30,7 @@
"fumadocs-mdx": "14.1.0",
"fumadocs-ui": "16.2.3",
"lucide-react": "^0.511.0",
"next": "16.1.6",
"next": "16.1.0-canary.21",
"next-themes": "^0.4.6",
"postgres": "^3.4.5",
"react": "19.2.1",
@@ -71,8 +66,8 @@
"@aws-sdk/s3-request-presigner": "^3.779.0",
"@azure/communication-email": "1.0.0",
"@azure/storage-blob": "12.27.0",
"@better-auth/sso": "1.4.18",
"@better-auth/stripe": "1.4.18",
"@better-auth/sso": "1.3.12",
"@better-auth/stripe": "1.3.12",
"@browserbasehq/stagehand": "^3.0.5",
"@cerebras/cerebras_cloud_sdk": "^1.23.0",
"@e2b/code-interpreter": "^2.0.0",
@@ -118,7 +113,7 @@
"@trigger.dev/sdk": "4.1.2",
"@types/react-window": "2.0.0",
"@types/three": "0.177.0",
"better-auth": "1.4.18",
"better-auth": "1.3.12",
"binary-extensions": "^2.0.0",
"browser-image-compression": "^2.0.2",
"chalk": "5.6.2",
@@ -161,7 +156,7 @@
"mysql2": "3.14.3",
"nanoid": "^3.3.7",
"neo4j-driver": "6.0.1",
"next": "16.1.6",
"next": "16.1.0-canary.21",
"next-mdx-remote": "^5.0.0",
"next-runtime-env": "3.3.0",
"next-themes": "^0.4.6",
@@ -324,9 +319,9 @@
"sharp",
],
"overrides": {
"@next/env": "16.1.6",
"@next/env": "16.1.0-canary.21",
"drizzle-orm": "^0.44.5",
"next": "16.1.6",
"next": "16.1.0-canary.21",
"postgres": "^3.4.5",
"react": "19.2.1",
"react-dom": "19.2.1",
@@ -570,13 +565,9 @@
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
"@better-auth/core": ["@better-auth/core@1.4.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.3.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "better-call": "1.1.8", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-q+awYgC7nkLEBdx2sW0iJjkzgSHlIxGnOpsN1r/O1+a4m7osJNHtfK2mKJSL1I+GfNyIlxJF8WvD/NLuYMpmcg=="],
"@better-auth/sso": ["@better-auth/sso@1.3.12", "", { "dependencies": { "@better-fetch/fetch": "^1.1.18", "fast-xml-parser": "^5.2.5", "jose": "^6.1.0", "oauth2-mock-server": "^7.2.1", "samlify": "^2.10.1", "zod": "^4.1.5" }, "peerDependencies": { "better-auth": "1.3.12" } }, "sha512-7uxBKZ07jbGXcRi4dCgH9AA475UUod1X7XiDG86T7iyRU4hnk/TaQ+XlWXzMO4dQDGqTKL3hawc8T94ggti3fQ=="],
"@better-auth/sso": ["@better-auth/sso@1.4.18", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "fast-xml-parser": "^5.2.5", "jose": "^6.1.0", "samlify": "^2.10.1", "zod": "^4.3.5" }, "peerDependencies": { "better-auth": "1.4.18" } }, "sha512-jwTxZUBp71W6YVOavy50DBZ4OFi0a9MGvTTAi+mxMuINFRcz2RyGZv0gb3jy8AJT97TwKU60qRYIWYYyJj1uhA=="],
"@better-auth/stripe": ["@better-auth/stripe@1.4.18", "", { "dependencies": { "defu": "^6.1.4", "zod": "^4.3.5" }, "peerDependencies": { "@better-auth/core": "1.4.18", "better-auth": "1.4.18", "stripe": "^18 || ^19 || ^20" } }, "sha512-T1vUPHEnzd3/gQH2e9BigfqcaOZ3W+jBVlfHpXZVGoat1oKFPtKnJ3OHmfU1MwSCjNSdIFllrN6U6DhsQlZoEQ=="],
"@better-auth/telemetry": ["@better-auth/telemetry@1.4.18", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.4.18" } }, "sha512-e5rDF8S4j3Um/0LIVATL2in9dL4lfO2fr2v1Wio4qTMRbfxqnUDTa+6SZtwdeJrbc4O+a3c+IyIpjG9Q/6GpfQ=="],
"@better-auth/stripe": ["@better-auth/stripe@1.3.12", "", { "dependencies": { "zod": "^4.1.5" }, "peerDependencies": { "better-auth": "1.3.12", "stripe": "^18" } }, "sha512-dw08MIECPo2+blWD3MZznXj/cAVSAKiZNRPfKXcsA1Y58Huq/C9j7We4cBdPGYNv0VffoF7lPobisGf0iLKIbA=="],
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
@@ -714,6 +705,8 @@
"@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
"@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="],
"@hookform/resolvers": ["@hookform/resolvers@4.1.3", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ=="],
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
@@ -798,6 +791,8 @@
"@langchain/openai": ["@langchain/openai@0.4.9", "", { "dependencies": { "js-tiktoken": "^1.0.12", "openai": "^4.87.3", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" }, "peerDependencies": { "@langchain/core": ">=0.3.39 <0.4.0" } }, "sha512-NAsaionRHNdqaMjVLPkFCyjUDze+OqRHghA1Cn4fPoAafz+FXcl9c7LlEl9Xo0FH6/8yiCl7Rw2t780C/SBVxQ=="],
"@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="],
"@linear/sdk": ["@linear/sdk@40.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.0", "graphql": "^15.4.0", "isomorphic-unfetch": "^3.1.0" } }, "sha512-R4lyDIivdi00fO+DYPs7gWNX221dkPJhgDowFrsfos/rNG6o5HixsCPgwXWtKN0GA0nlqLvFTmzvzLXpud1xKw=="],
"@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="],
@@ -834,23 +829,23 @@
"@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.91", "", { "os": "win32", "cpu": "x64" }, "sha512-++gtW9EV/neKI8TshD8WFxzBYALSPag2kFRahIJV+LYsyt5kBn21b1dBhEUDHf7O+wiZmuFCeUa7QKGHnYRZBA=="],
"@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="],
"@next/env": ["@next/env@16.1.0-canary.21", "", {}, "sha512-J5inWwxC8EpAr/a2GApmQK1KkftG7K2nM6SuzNvciNaPt9Z0AHFeazvFuQxbvXn024p+akBHRlo8P7ZJRoU7kA=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.0-canary.21", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4kXAH8QQ01Mx0kCZQIcWdur048egbaK1KUj6HwGPfZg/H992jLTA0kWNxTgnnCNTw2ktea6N5QVhCsHeg538LQ=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.0-canary.21", "", { "os": "darwin", "cpu": "x64" }, "sha512-4FO08KUjRohb+rgLmiCJGBy2jxJp4tG7JCC1AuABBtPOZNwhbenVgJCK1PI8wT2yswbjC8BQn+JePnRBEkJWKg=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.0-canary.21", "", { "os": "linux", "cpu": "arm64" }, "sha512-C7xkG5HFufr+DN7KNpQUrZmjDXfuYA4ejFqx9VQAJ2w1ruvcy1z4V0ysCIZjLUcXnGJeQa07qt/SnMoKyBkujw=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.0-canary.21", "", { "os": "linux", "cpu": "arm64" }, "sha512-k6ZRbyISC20J72uKom8qLnoBKhFb7aVsZPmD6bxVGtx4CzGGY5K5ytiT79Gok+qzH/9pZscjYuN212BVhCLaPQ=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.0-canary.21", "", { "os": "linux", "cpu": "x64" }, "sha512-OEM12KOZ025SObzrtpXSbOw5aS+D2V+lVE/Al4wxChvyO4/SvaXx5gs/ckAddDB6tQj3bpIKS7ehvfTSto2HFg=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.0-canary.21", "", { "os": "linux", "cpu": "x64" }, "sha512-Cu5dzIKzopbUxiTuaaKlWcyarpdBTlgVSqoGDMgmQjJPw2k1z1Elo/UjPeOJlGlzZgvZjdYCjY3Pvvvepa98Rg=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.0-canary.21", "", { "os": "win32", "cpu": "arm64" }, "sha512-l+p3OOlGqY4hR0h4mHcMzRPnThvYDWQUJ4s9pGM5qF/ft6JLr7lWeI32sUnmS8AWHQGmLjeAylb9ismQlMHSvg=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.0-canary.21", "", { "os": "win32", "cpu": "x64" }, "sha512-JfRb34d8Q6i9Jy38ak2VXe1nl3O43kFyF+J2zsRu9P3HVtBVbUZF/4/PIaBChMeMZb+vYBDSqDTyJoVRK4/wzA=="],
"@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="],
@@ -950,6 +945,30 @@
"@pdf-lib/upng": ["@pdf-lib/upng@1.0.1", "", { "dependencies": { "pako": "^1.0.10" } }, "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ=="],
"@peculiar/asn1-android": ["@peculiar/asn1-android@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-cBRCKtYPF7vJGN76/yG8VbxRcHLPF3HnkoHhKOZeHpoVtbMYfY9ROKtH3DtYUY9m8uI1Mh47PRhHf2hSK3xcSQ=="],
"@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "@peculiar/asn1-x509-attr": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-2uZqP+ggSncESeUF/9Su8rWqGclEfEiz1SyU02WX5fUONFfkjzS2Z/F1Li0ofSmf4JqYXIOdCAZqIXAIBAT1OA=="],
"@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-BeWIu5VpTIhfRysfEp73SGbwjjoLL/JWXhJ/9mo4vXnz3tRGm+NGm3KNcRzQ9VMVqwYS2RHlolz21svzRXIHPQ=="],
"@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-FF3LMGq6SfAOwUG2sKpPXblibn6XnEIKa+SryvUl5Pik+WR9rmRA3OCiwz8R3lVXnYnyRkSZsSLdml8H3UiOcw=="],
"@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.6.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-pkcs8": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-rtUvtf+tyKGgokHHmZzeUojRZJYPxoD/jaN1+VAB4kKR7tXrnDCA/RAWXAIhMJJC+7W27IIRGe9djvxKgsldCQ=="],
"@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-KyQ4D8G/NrS7Fw3XCJrngxmjwO/3htnA0lL9gDICvEQ+GJ+EPFqldcJQTwPIdvx98Tua+WjkdKHSC0/Km7T+lA=="],
"@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.6.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-pfx": "^2.6.0", "@peculiar/asn1-pkcs8": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "@peculiar/asn1-x509-attr": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-b78OQ6OciW0aqZxdzliXGYHASeCvvw5caqidbpQRYW2mBtXIX2WhofNXTEe7NyxTb0P6J62kAAWLwn0HuMF1Fw=="],
"@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-Nu4C19tsrTsCp9fDrH+sdcOKoVfdfoQQ7S3VqjJU6vedR7tY3RLkQ5oguOIB3zFW33USDUuYZnPEQYySlgha4w=="],
"@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.6.0", "", { "dependencies": { "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg=="],
"@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-uzYbPEpoQiBoTq0/+jZtpM6Gq6zADBx+JNFP3yqRgziWBxQ/Dt/HcuvRfm9zJTPdRcBqPNdaRHTVwpyiq6iNMA=="],
"@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.6.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-MuIAXFX3/dc8gmoZBkwJWxUWOSvG4MMDntXhrOZpJVMkYX+MYc/rUAU2uJOved9iJEoiUx7//3D8oG83a78UJA=="],
"@peculiar/x509": ["@peculiar/x509@1.14.3", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-csr": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.0", "@peculiar/asn1-pkcs9": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA=="],
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
@@ -1232,6 +1251,10 @@
"@sim/tsconfig": ["@sim/tsconfig@workspace:packages/tsconfig"],
"@simplewebauthn/browser": ["@simplewebauthn/browser@13.2.2", "", {}, "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA=="],
"@simplewebauthn/server": ["@simplewebauthn/server@13.2.2", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.3.10", "@peculiar/asn1-ecc": "^2.3.8", "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/asn1-x509": "^2.3.8", "@peculiar/x509": "^1.13.0" } }, "sha512-HcWLW28yTMGXpwE9VLx9J+N2KEUaELadLrkPEEI9tpI5la70xNEVEsu/C+m3u7uoq4FulLqZQhgBCzR9IZhFpA=="],
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.8", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw=="],
"@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA=="],
@@ -1634,8 +1657,12 @@
"arktype": ["arktype@2.1.29", "", { "dependencies": { "@ark/schema": "0.56.0", "@ark/util": "0.56.0", "arkregex": "0.0.5" } }, "sha512-jyfKk4xIOzvYNayqnD8ZJQqOwcrTOUbIU4293yrzAjA3O1dWh61j71ArMQ6tS/u4pD7vabSPe7nG3RCyoXW6RQ=="],
"array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="],
"asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="],
"asn1js": ["asn1js@3.0.7", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ=="],
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
"ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="],
@@ -1680,15 +1707,17 @@
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="],
"basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
"basic-ftp": ["basic-ftp@5.1.0", "", {}, "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw=="],
"bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "^0.14.3" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="],
"before-after-hook": ["before-after-hook@3.0.2", "", {}, "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="],
"better-auth": ["better-auth@1.4.18", "", { "dependencies": { "@better-auth/core": "1.4.18", "@better-auth/telemetry": "1.4.18", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.8", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.3.5" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-bnyifLWBPcYVltH3RhS7CM62MoelEqC6Q+GnZwfiDWNfepXoQZBjEvn4urcERC7NTKgKq5zNBM8rvPvRBa6xcg=="],
"better-auth": ["better-auth@1.3.12", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.19", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.5" }, "peerDependencies": { "@lynx-js/react": "*", "@sveltejs/kit": "^2.0.0", "next": "^14.0.0 || ^15.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@sveltejs/kit", "next", "react", "react-dom", "solid-js", "svelte", "vue"] }, "sha512-FckxiAexCkkk2F0EOPmhXjWhYYE8eYg2x68lOIirSgyQ0TWc4JDvA5y8Vax5Jc7iyXk5MjJBY3DfwTPDZ87Lbg=="],
"better-call": ["better-call@1.1.8", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw=="],
"better-call": ["better-call@1.0.19", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw=="],
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
@@ -1962,6 +1991,8 @@
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
"destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
@@ -2378,6 +2409,8 @@
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
"is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
"is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="],
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
@@ -2610,6 +2643,8 @@
"meshoptimizer": ["meshoptimizer@0.18.1", "", {}, "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw=="],
"methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
@@ -2682,6 +2717,8 @@
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
@@ -2754,7 +2791,7 @@
"netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="],
"next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="],
"next": ["next@16.1.0-canary.21", "", { "dependencies": { "@next/env": "16.1.0-canary.21", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.0-canary.21", "@next/swc-darwin-x64": "16.1.0-canary.21", "@next/swc-linux-arm64-gnu": "16.1.0-canary.21", "@next/swc-linux-arm64-musl": "16.1.0-canary.21", "@next/swc-linux-x64-gnu": "16.1.0-canary.21", "@next/swc-linux-x64-musl": "16.1.0-canary.21", "@next/swc-win32-arm64-msvc": "16.1.0-canary.21", "@next/swc-win32-x64-msvc": "16.1.0-canary.21", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-5DV7GwHGNgnTuC1SFIkDZ1WV2jEwMHE6t5LqIUmIl5OguQ1SMlanLuYqdgU8hVGk9IR6hcB8L4MJY7veUE6nww=="],
"next-mdx-remote": ["next-mdx-remote@5.0.0", "", { "dependencies": { "@babel/code-frame": "^7.23.5", "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", "unist-util-remove": "^3.1.0", "vfile": "^6.0.1", "vfile-matter": "^5.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ=="],
@@ -2802,6 +2839,8 @@
"nypm": ["nypm@0.6.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "pathe": "^2.0.3", "pkg-types": "^2.0.0", "tinyexec": "^0.3.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg=="],
"oauth2-mock-server": ["oauth2-mock-server@7.2.1", "", { "dependencies": { "basic-auth": "^2.0.1", "cors": "^2.8.5", "express": "^4.21.2", "is-plain-object": "^5.0.0", "jose": "^5.10.0" }, "bin": { "oauth2-mock-server": "dist\\oauth2-mock-server.js" } }, "sha512-ZXL+VuJU2pvzehseq+7b47ZSN7p2Z7J5GoI793X0oECgdLYdol7tnBbTY/aUxuMkk+xpnE186ZzhnigwCAEBOQ=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
@@ -2992,6 +3031,10 @@
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
"pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="],
"qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
"query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="],
@@ -3062,6 +3105,8 @@
"redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
"reflect-metadata": ["reflect-metadata@0.2.2", "", {}, "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q=="],
"regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
"regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
@@ -3110,7 +3155,7 @@
"rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="],
"rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="],
"rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="],
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
@@ -3388,21 +3433,23 @@
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tsyringe": ["tsyringe@4.10.0", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw=="],
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
"turbo": ["turbo@2.8.10", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.10", "turbo-darwin-arm64": "2.8.10", "turbo-linux-64": "2.8.10", "turbo-linux-arm64": "2.8.10", "turbo-windows-64": "2.8.10", "turbo-windows-arm64": "2.8.10" }, "bin": { "turbo": "bin/turbo" } }, "sha512-OxbzDES66+x7nnKGg2MwBA1ypVsZoDTLHpeaP4giyiHSixbsiTaMyeJqbEyvBdp5Cm28fc+8GG6RdQtic0ijwQ=="],
"turbo": ["turbo@2.8.9", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.9", "turbo-darwin-arm64": "2.8.9", "turbo-linux-64": "2.8.9", "turbo-linux-arm64": "2.8.9", "turbo-windows-64": "2.8.9", "turbo-windows-arm64": "2.8.9" }, "bin": { "turbo": "bin/turbo" } }, "sha512-G+Mq8VVQAlpz/0HTsxiNNk/xywaHGl+dk1oiBREgOEVCCDjXInDlONWUn5srRnC9s5tdHTFD1bx1N19eR4hI+g=="],
"turbo-darwin-64": ["turbo-darwin-64@2.8.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-A03fXh+B7S8mL3PbdhTd+0UsaGrhfyPkODvzBDpKRY7bbeac4MDFpJ7I+Slf2oSkCEeSvHKR7Z4U71uKRUfX7g=="],
"turbo-darwin-64": ["turbo-darwin-64@2.8.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-KnCw1ZI9KTnEAhdI9avZrnZ/z4wsM++flMA1w8s8PKOqi5daGpFV36qoPafg4S8TmYMe52JPWEoFr0L+lQ5JIw=="],
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sidzowgWL3s5xCHLeqwC9M3s9M0i16W1nuQF3Mc7fPHpZ+YPohvcbVFBB2uoRRHYZg6yBnwD4gyUHKTeXfwtXA=="],
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CbD5Y2NKJKBXTOZ7z7Cc7vGlFPZkYjApA7ri9lH4iFwKV1X7MoZswh9gyRLetXYWImVX1BqIvP8KftulJg/wIA=="],
"turbo-linux-64": ["turbo-linux-64@2.8.10", "", { "os": "linux", "cpu": "x64" }, "sha512-YK9vcpL3TVtqonB021XwgaQhY9hJJbKKUhLv16osxV0HkcQASQWUqR56yMge7puh6nxU67rQlTq1b7ksR1T3KA=="],
"turbo-linux-64": ["turbo-linux-64@2.8.9", "", { "os": "linux", "cpu": "x64" }, "sha512-OXC9HdCtsHvyH+5KUoH8ds+p5WU13vdif0OPbsFzZca4cUXMwKA3HWwUuCgQetk0iAE4cscXpi/t8A263n3VTg=="],
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-3+j2tL0sG95iBJTm+6J8/45JsETQABPqtFyYjVjBbi6eVGdtNTiBmHNKrbvXRlQ3ZbUG75bKLaSSDHSEEN+btQ=="],
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-yI5n8jNXiFA6+CxnXG0gO7h5ZF1+19K8uO3/kXPQmyl37AdiA7ehKJQOvf9OPAnmkGDHcF2HSCPltabERNRmug=="],
"turbo-windows-64": ["turbo-windows-64@2.8.10", "", { "os": "win32", "cpu": "x64" }, "sha512-hdeF5qmVY/NFgiucf8FW0CWJWtyT2QPm5mIsX0W1DXAVzqKVXGq+Zf+dg4EUngAFKjDzoBeN6ec2Fhajwfztkw=="],
"turbo-windows-64": ["turbo-windows-64@2.8.9", "", { "os": "win32", "cpu": "x64" }, "sha512-/OztzeGftJAg258M/9vK2ZCkUKUzqrWXJIikiD2pm8TlqHcIYUmepDbyZSDfOiUjMy6NzrLFahpNLnY7b5vNgg=="],
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-QGdr/Q8LWmj+ITMkSvfiz2glf0d7JG0oXVzGL3jxkGqiBI1zXFj20oqVY0qWi+112LO9SVrYdpHS0E/oGFrMbQ=="],
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-xZ2VTwVTjIqpFZKN4UBxDHCPM3oJ2J5cpRzCBSmRpJ/Pn33wpiYjs+9FB2E03svKaD04/lSSLlEUej0UYsugfg=="],
"tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="],
@@ -3474,6 +3521,8 @@
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
"uzip": ["uzip@0.20201231.0", "", {}, "sha512-OZeJfZP+R0z9D6TmBgLq2LHzSSptGMGDGigGiEe0pr8UBe/7fdflgHlHBNDASTXB5jnFuxHpNaJywSg8YFeGng=="],
@@ -3686,10 +3735,6 @@
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"@better-auth/core/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
"@better-auth/core/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"@better-auth/sso/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
"@better-auth/sso/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
@@ -3906,6 +3951,8 @@
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"better-auth/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
"better-auth/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
@@ -4050,6 +4097,10 @@
"nypm/pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
"oauth2-mock-server/express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="],
"oauth2-mock-server/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
"ollama-ai-provider-v2/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"openai/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
@@ -4172,6 +4223,8 @@
"thriftrw/long": ["long@2.4.0", "", {}, "sha512-ijUtjmO/n2A5PaosNG9ZGDsQ3vxJg7ZW8vsY8Kp0f2yIZWhSJvjmegV7t+9RPQKxKrvj8yKGehhS+po14hPLGQ=="],
"tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"twilio/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
"twilio/xmlbuilder": ["xmlbuilder@13.0.2", "", {}, "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ=="],
@@ -4536,6 +4589,28 @@
"nypm/pkg-types/confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="],
"oauth2-mock-server/express/body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="],
"oauth2-mock-server/express/content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
"oauth2-mock-server/express/cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
"oauth2-mock-server/express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"oauth2-mock-server/express/finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="],
"oauth2-mock-server/express/fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
"oauth2-mock-server/express/merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
"oauth2-mock-server/express/path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
"oauth2-mock-server/express/send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="],
"oauth2-mock-server/express/serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
"oauth2-mock-server/express/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
"openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"openai/node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
@@ -4748,6 +4823,16 @@
"log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"oauth2-mock-server/express/body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"oauth2-mock-server/express/body-parser/raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="],
"oauth2-mock-server/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"oauth2-mock-server/express/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
"oauth2-mock-server/express/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"openai/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"openai/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
@@ -4812,6 +4897,8 @@
"log-update/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"oauth2-mock-server/express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"rimraf/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"rimraf/glob/jackspeak/@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],

View File

@@ -28,21 +28,21 @@
"overrides": {
"react": "19.2.1",
"react-dom": "19.2.1",
"next": "16.1.6",
"@next/env": "16.1.6",
"next": "16.1.0-canary.21",
"@next/env": "16.1.0-canary.21",
"drizzle-orm": "^0.44.5",
"postgres": "^3.4.5"
},
"devDependencies": {
"@biomejs/biome": "2.0.0-beta.5",
"@next/env": "16.1.6",
"@next/env": "16.1.0-canary.21",
"@octokit/rest": "^21.0.0",
"@tailwindcss/typography": "0.5.19",
"drizzle-kit": "^0.31.4",
"glob": "13.0.0",
"husky": "9.1.7",
"lint-staged": "16.0.0",
"turbo": "2.8.10"
"turbo": "2.8.9"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css,scss}": [
@@ -51,10 +51,5 @@
},
"trustedDependencies": [
"sharp"
],
"dependencies": {
"@better-auth/sso": "1.4.18",
"@better-auth/stripe": "1.4.18",
"better-auth": "1.4.18"
}
]
}