From 0acd86023cbe4f966ae0780f9ef0005b6b4b0b7d Mon Sep 17 00:00:00 2001 From: Waleed Date: Sun, 14 Dec 2025 23:16:09 -0800 Subject: [PATCH 01/17] fix(blog): use unoptimized tag for image assets (#2374) --- apps/sim/app/(landing)/studio/[slug]/page.tsx | 2 ++ apps/sim/app/(landing)/studio/authors/[id]/page.tsx | 2 ++ apps/sim/app/(landing)/studio/page.tsx | 1 + apps/sim/lib/blog/mdx.tsx | 1 + 4 files changed, 6 insertions(+) diff --git a/apps/sim/app/(landing)/studio/[slug]/page.tsx b/apps/sim/app/(landing)/studio/[slug]/page.tsx index db5092b29..531cebd52 100644 --- a/apps/sim/app/(landing)/studio/[slug]/page.tsx +++ b/apps/sim/app/(landing)/studio/[slug]/page.tsx @@ -64,6 +64,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string sizes='(max-width: 768px) 100vw, 450px' priority itemProp='image' + unoptimized /> @@ -144,6 +145,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string className='h-[160px] w-full object-cover' sizes='(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw' loading='lazy' + unoptimized />
diff --git a/apps/sim/app/(landing)/studio/authors/[id]/page.tsx b/apps/sim/app/(landing)/studio/authors/[id]/page.tsx index 6a4f0c9b3..720020bbd 100644 --- a/apps/sim/app/(landing)/studio/authors/[id]/page.tsx +++ b/apps/sim/app/(landing)/studio/authors/[id]/page.tsx @@ -38,6 +38,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str width={40} height={40} className='rounded-full' + unoptimized /> ) : null}

{author.name}

@@ -52,6 +53,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str width={600} height={315} className='h-[160px] w-full object-cover transition-transform group-hover:scale-[1.02]' + unoptimized />
diff --git a/apps/sim/app/(landing)/studio/page.tsx b/apps/sim/app/(landing)/studio/page.tsx index 76c51fc83..0a32b267d 100644 --- a/apps/sim/app/(landing)/studio/page.tsx +++ b/apps/sim/app/(landing)/studio/page.tsx @@ -76,6 +76,7 @@ export default async function StudioIndex({ className='h-48 w-full object-cover' sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw' loading='lazy' + unoptimized />
diff --git a/apps/sim/lib/blog/mdx.tsx b/apps/sim/lib/blog/mdx.tsx index 833ed355b..2547f5717 100644 --- a/apps/sim/lib/blog/mdx.tsx +++ b/apps/sim/lib/blog/mdx.tsx @@ -13,6 +13,7 @@ export const mdxComponents: MDXRemoteProps['components'] = { className={clsx('h-auto w-full rounded-lg', props.className)} sizes='(max-width: 768px) 100vw, 800px' loading='lazy' + unoptimized /> ), h2: (props: any) => ( From 6009a7359f9bd7d51383c11735979f702e2b9cea Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 10:48:48 -0800 Subject: [PATCH 02/17] fix(vllm): remove requirement for api key for vllm (#2380) --- .../app/api/providers/ollama/models/route.ts | 1 - .../app/api/providers/vllm/models/route.ts | 13 +- apps/sim/providers/utils.test.ts | 116 +++++++++++------- apps/sim/providers/utils.ts | 8 +- apps/sim/providers/vllm/index.ts | 10 +- 5 files changed, 95 insertions(+), 53 deletions(-) diff --git a/apps/sim/app/api/providers/ollama/models/route.ts b/apps/sim/app/api/providers/ollama/models/route.ts index 16a448028..d135afc9e 100644 --- a/apps/sim/app/api/providers/ollama/models/route.ts +++ b/apps/sim/app/api/providers/ollama/models/route.ts @@ -45,7 +45,6 @@ export async function GET(request: NextRequest) { host: OLLAMA_HOST, }) - // Return empty array instead of error to avoid breaking the UI return NextResponse.json({ models: [] }) } } diff --git a/apps/sim/app/api/providers/vllm/models/route.ts b/apps/sim/app/api/providers/vllm/models/route.ts index 843ea9fa6..f9f76332e 100644 --- a/apps/sim/app/api/providers/vllm/models/route.ts +++ b/apps/sim/app/api/providers/vllm/models/route.ts @@ -20,10 +20,16 @@ export async function GET(request: NextRequest) { baseUrl, }) + const headers: Record = { + 'Content-Type': 'application/json', + } + + if (env.VLLM_API_KEY) { + headers.Authorization = `Bearer ${env.VLLM_API_KEY}` + } + const response = await fetch(`${baseUrl}/v1/models`, { - headers: { - 'Content-Type': 'application/json', - }, + headers, next: { revalidate: 60 }, }) @@ -50,7 +56,6 @@ export async function GET(request: NextRequest) { baseUrl, }) - // Return empty array instead of error to avoid breaking the UI return NextResponse.json({ models: [] }) } } diff --git a/apps/sim/providers/utils.test.ts b/apps/sim/providers/utils.test.ts index 003119c86..4fa913214 100644 --- a/apps/sim/providers/utils.test.ts +++ b/apps/sim/providers/utils.test.ts @@ -53,19 +53,20 @@ describe('getApiKey', () => { module.require = originalRequire }) - it('should return user-provided key when not in hosted environment', () => { + it.concurrent('should return user-provided key when not in hosted environment', () => { isHostedSpy.mockReturnValue(false) - // For OpenAI const key1 = getApiKey('openai', 'gpt-4', 'user-key-openai') expect(key1).toBe('user-key-openai') - // For Anthropic const key2 = getApiKey('anthropic', 'claude-3', 'user-key-anthropic') expect(key2).toBe('user-key-anthropic') + + const key3 = getApiKey('google', 'gemini-2.5-flash', 'user-key-google') + expect(key3).toBe('user-key-google') }) - it('should throw error if no key provided in non-hosted environment', () => { + it.concurrent('should throw error if no key provided in non-hosted environment', () => { isHostedSpy.mockReturnValue(false) expect(() => getApiKey('openai', 'gpt-4')).toThrow('API key is required for openai gpt-4') @@ -74,63 +75,87 @@ describe('getApiKey', () => { ) }) - it('should fall back to user key in hosted environment if rotation fails', () => { + it.concurrent('should fall back to user key in hosted environment if rotation fails', () => { isHostedSpy.mockReturnValue(true) module.require = vi.fn(() => { throw new Error('Rotation failed') }) - // Use gpt-4o which IS in the hosted models list const key = getApiKey('openai', 'gpt-4o', 'user-fallback-key') expect(key).toBe('user-fallback-key') }) - it('should throw error in hosted environment if rotation fails and no user key', () => { - isHostedSpy.mockReturnValue(true) + it.concurrent( + 'should throw error in hosted environment if rotation fails and no user key', + () => { + isHostedSpy.mockReturnValue(true) - module.require = vi.fn(() => { - throw new Error('Rotation failed') - }) + module.require = vi.fn(() => { + throw new Error('Rotation failed') + }) - // Use gpt-4o which IS in the hosted models list - expect(() => getApiKey('openai', 'gpt-4o')).toThrow('No API key available for openai gpt-4o') + expect(() => getApiKey('openai', 'gpt-4o')).toThrow('No API key available for openai gpt-4o') + } + ) + + it.concurrent( + 'should require user key for non-OpenAI/Anthropic providers even in hosted environment', + () => { + isHostedSpy.mockReturnValue(true) + + const key = getApiKey('other-provider', 'some-model', 'user-key') + expect(key).toBe('user-key') + + expect(() => getApiKey('other-provider', 'some-model')).toThrow( + 'API key is required for other-provider some-model' + ) + } + ) + + it.concurrent( + 'should require user key for models NOT in hosted list even if provider matches', + () => { + isHostedSpy.mockReturnValue(true) + + const key1 = getApiKey('anthropic', 'claude-sonnet-4-20250514', 'user-key-anthropic') + expect(key1).toBe('user-key-anthropic') + + expect(() => getApiKey('anthropic', 'claude-sonnet-4-20250514')).toThrow( + 'API key is required for anthropic claude-sonnet-4-20250514' + ) + + const key2 = getApiKey('openai', 'gpt-4o-2024-08-06', 'user-key-openai') + expect(key2).toBe('user-key-openai') + + expect(() => getApiKey('openai', 'gpt-4o-2024-08-06')).toThrow( + 'API key is required for openai gpt-4o-2024-08-06' + ) + } + ) + + it.concurrent('should return empty for ollama provider without requiring API key', () => { + isHostedSpy.mockReturnValue(false) + + const key = getApiKey('ollama', 'llama2') + expect(key).toBe('empty') + + const key2 = getApiKey('ollama', 'codellama', 'user-key') + expect(key2).toBe('empty') }) - it('should require user key for non-OpenAI/Anthropic providers even in hosted environment', () => { - isHostedSpy.mockReturnValue(true) + it.concurrent( + 'should return empty or user-provided key for vllm provider without requiring API key', + () => { + isHostedSpy.mockReturnValue(false) - const key = getApiKey('other-provider', 'some-model', 'user-key') - expect(key).toBe('user-key') + const key = getApiKey('vllm', 'vllm/qwen-3') + expect(key).toBe('empty') - expect(() => getApiKey('other-provider', 'some-model')).toThrow( - 'API key is required for other-provider some-model' - ) - }) - - it('should require user key for models NOT in hosted list even if provider matches', () => { - isHostedSpy.mockReturnValue(true) - - // Models with version suffixes that are NOT in the hosted list should require user API key - // even though they're from anthropic/openai providers - - // User provides their own key - should work - const key1 = getApiKey('anthropic', 'claude-sonnet-4-20250514', 'user-key-anthropic') - expect(key1).toBe('user-key-anthropic') - - // No user key - should throw, NOT use server key - expect(() => getApiKey('anthropic', 'claude-sonnet-4-20250514')).toThrow( - 'API key is required for anthropic claude-sonnet-4-20250514' - ) - - // Same for OpenAI versioned models not in list - const key2 = getApiKey('openai', 'gpt-4o-2024-08-06', 'user-key-openai') - expect(key2).toBe('user-key-openai') - - expect(() => getApiKey('openai', 'gpt-4o-2024-08-06')).toThrow( - 'API key is required for openai gpt-4o-2024-08-06' - ) - }) + const key2 = getApiKey('vllm', 'vllm/llama', 'user-key') + expect(key2).toBe('user-key') + } + ) }) describe('Model Capabilities', () => { @@ -202,7 +227,6 @@ describe('Model Capabilities', () => { it.concurrent( 'should inherit temperature support from provider for dynamically fetched models', () => { - // OpenRouter models should inherit temperature support from provider capabilities expect(supportsTemperature('openrouter/anthropic/claude-3.5-sonnet')).toBe(true) expect(supportsTemperature('openrouter/openai/gpt-4')).toBe(true) } diff --git a/apps/sim/providers/utils.ts b/apps/sim/providers/utils.ts index 972cdf815..695818fae 100644 --- a/apps/sim/providers/utils.ts +++ b/apps/sim/providers/utils.ts @@ -630,13 +630,19 @@ export function getApiKey(provider: string, model: string, userProvidedKey?: str // If user provided a key, use it as a fallback const hasUserKey = !!userProvidedKey - // Ollama models don't require API keys - they run locally + // Ollama and vLLM models don't require API keys const isOllamaModel = provider === 'ollama' || useProvidersStore.getState().providers.ollama.models.includes(model) if (isOllamaModel) { return 'empty' // Ollama uses 'empty' as a placeholder API key } + const isVllmModel = + provider === 'vllm' || useProvidersStore.getState().providers.vllm.models.includes(model) + if (isVllmModel) { + return userProvidedKey || 'empty' // vLLM uses 'empty' as a placeholder if no key provided + } + // Use server key rotation for all OpenAI models, Anthropic's Claude models, and Google's Gemini models on the hosted platform const isOpenAIModel = provider === 'openai' const isClaudeModel = provider === 'anthropic' diff --git a/apps/sim/providers/vllm/index.ts b/apps/sim/providers/vllm/index.ts index be59f5480..bd6805be7 100644 --- a/apps/sim/providers/vllm/index.ts +++ b/apps/sim/providers/vllm/index.ts @@ -79,7 +79,15 @@ export const vllmProvider: ProviderConfig = { } try { - const response = await fetch(`${baseUrl}/v1/models`) + const headers: Record = { + 'Content-Type': 'application/json', + } + + if (env.VLLM_API_KEY) { + headers.Authorization = `Bearer ${env.VLLM_API_KEY}` + } + + const response = await fetch(`${baseUrl}/v1/models`, { headers }) if (!response.ok) { useProvidersStore.getState().setProviderModels('vllm', []) logger.warn('vLLM service is not available. The provider will be disabled.') From e43afc8b6ca77cd454a331b0ab6e740187537927 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 15 Dec 2025 11:36:08 -0800 Subject: [PATCH 03/17] fix(subscription): incomplete team subscription race condition (#2381) --- apps/sim/app/api/organizations/route.ts | 8 +- apps/sim/lib/auth/auth.ts | 38 ++++---- apps/sim/lib/billing/client/upgrade.ts | 111 ++++++------------------ apps/sim/lib/billing/organization.ts | 83 ++++++++++++++++-- 4 files changed, 136 insertions(+), 104 deletions(-) diff --git a/apps/sim/app/api/organizations/route.ts b/apps/sim/app/api/organizations/route.ts index b9fdb374b..81ae107c3 100644 --- a/apps/sim/app/api/organizations/route.ts +++ b/apps/sim/app/api/organizations/route.ts @@ -16,7 +16,6 @@ export async function GET() { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } - // Get organizations where user is owner or admin const userOrganizations = await db .select({ id: organization.id, @@ -32,8 +31,15 @@ export async function GET() { ) ) + const anyMembership = await db + .select({ id: member.id }) + .from(member) + .where(eq(member.userId, session.user.id)) + .limit(1) + return NextResponse.json({ organizations: userOrganizations, + isMemberOfAnyOrg: anyMembership.length > 0, }) } catch (error) { logger.error('Failed to fetch organizations', { diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index fb9287e90..0efdee78f 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -24,7 +24,10 @@ import { import { sendPlanWelcomeEmail } from '@/lib/billing' import { authorizeSubscriptionReference } from '@/lib/billing/authorization' import { handleNewUser } from '@/lib/billing/core/usage' -import { syncSubscriptionUsageLimits } from '@/lib/billing/organization' +import { + ensureOrganizationForTeamSubscription, + syncSubscriptionUsageLimits, +} from '@/lib/billing/organization' import { getPlans } from '@/lib/billing/plans' import { syncSeatsFromStripeQuantity } from '@/lib/billing/validation/seat-management' import { handleChargeDispute, handleDisputeClosed } from '@/lib/billing/webhooks/disputes' @@ -2021,11 +2024,14 @@ export const auth = betterAuth({ status: subscription.status, }) - await handleSubscriptionCreated(subscription) + const resolvedSubscription = + await ensureOrganizationForTeamSubscription(subscription) - await syncSubscriptionUsageLimits(subscription) + await handleSubscriptionCreated(resolvedSubscription) - await sendPlanWelcomeEmail(subscription) + await syncSubscriptionUsageLimits(resolvedSubscription) + + await sendPlanWelcomeEmail(resolvedSubscription) }, onSubscriptionUpdate: async ({ event, @@ -2040,40 +2046,42 @@ export const auth = betterAuth({ plan: subscription.plan, }) + const resolvedSubscription = + await ensureOrganizationForTeamSubscription(subscription) + try { - await syncSubscriptionUsageLimits(subscription) + await syncSubscriptionUsageLimits(resolvedSubscription) } catch (error) { logger.error('[onSubscriptionUpdate] Failed to sync usage limits', { - subscriptionId: subscription.id, - referenceId: subscription.referenceId, + subscriptionId: resolvedSubscription.id, + referenceId: resolvedSubscription.referenceId, error, }) } - // Sync seat count from Stripe subscription quantity for team plans - if (subscription.plan === 'team') { + if (resolvedSubscription.plan === 'team') { try { const stripeSubscription = event.data.object as Stripe.Subscription const quantity = stripeSubscription.items?.data?.[0]?.quantity || 1 const result = await syncSeatsFromStripeQuantity( - subscription.id, - subscription.seats, + resolvedSubscription.id, + resolvedSubscription.seats ?? null, quantity ) if (result.synced) { logger.info('[onSubscriptionUpdate] Synced seat count from Stripe', { - subscriptionId: subscription.id, - referenceId: subscription.referenceId, + subscriptionId: resolvedSubscription.id, + referenceId: resolvedSubscription.referenceId, previousSeats: result.previousSeats, newSeats: result.newSeats, }) } } catch (error) { logger.error('[onSubscriptionUpdate] Failed to sync seat count', { - subscriptionId: subscription.id, - referenceId: subscription.referenceId, + subscriptionId: resolvedSubscription.id, + referenceId: resolvedSubscription.referenceId, error, }) } diff --git a/apps/sim/lib/billing/client/upgrade.ts b/apps/sim/lib/billing/client/upgrade.ts index 297efe3bb..869b30444 100644 --- a/apps/sim/lib/billing/client/upgrade.ts +++ b/apps/sim/lib/billing/client/upgrade.ts @@ -12,9 +12,6 @@ const CONSTANTS = { INITIAL_TEAM_SEATS: 1, } as const -/** - * Handles organization creation for team plans and proper referenceId management - */ export function useSubscriptionUpgrade() { const { data: session } = useSession() const betterAuthSubscription = useSubscription() @@ -40,83 +37,43 @@ export function useSubscriptionUpgrade() { let referenceId = userId - // For team plans, create organization first and use its ID as referenceId if (targetPlan === 'team') { try { - // Check if user already has an organization where they are owner/admin const orgsResponse = await fetch('/api/organizations') - if (orgsResponse.ok) { - const orgsData = await orgsResponse.json() - const existingOrg = orgsData.organizations?.find( - (org: any) => org.role === 'owner' || org.role === 'admin' - ) - - if (existingOrg) { - logger.info('Using existing organization for team plan upgrade', { - userId, - organizationId: existingOrg.id, - }) - referenceId = existingOrg.id - } + if (!orgsResponse.ok) { + throw new Error('Failed to check organization status') } - // Only create new organization if no suitable one exists - if (referenceId === userId) { - logger.info('Creating organization for team plan upgrade', { + const orgsData = await orgsResponse.json() + const existingOrg = orgsData.organizations?.find( + (org: any) => org.role === 'owner' || org.role === 'admin' + ) + + if (existingOrg) { + logger.info('Using existing organization for team plan upgrade', { userId, + organizationId: existingOrg.id, }) + referenceId = existingOrg.id - const response = await fetch('/api/organizations', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }) - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - if (response.status === 409) { - throw new Error( - 'You are already a member of an organization. Please leave it or ask an admin to upgrade.' - ) - } - throw new Error( - errorData.message || `Failed to create organization: ${response.statusText}` - ) + try { + await client.organization.setActive({ organizationId: referenceId }) + logger.info('Set organization as active', { organizationId: referenceId }) + } catch (error) { + logger.warn('Failed to set organization as active, proceeding with upgrade', { + organizationId: referenceId, + error: error instanceof Error ? error.message : 'Unknown error', + }) } - const result = await response.json() - - logger.info('Organization API response', { - result, - success: result.success, - organizationId: result.organizationId, - }) - - if (!result.success || !result.organizationId) { - throw new Error('Failed to create organization for team plan') - } - - referenceId = result.organizationId - } - - // Set the organization as active so Better Auth recognizes it - try { - await client.organization.setActive({ organizationId: referenceId }) - - logger.info('Set organization as active', { - organizationId: referenceId, - oldReferenceId: userId, - newReferenceId: referenceId, - }) - } catch (error) { - logger.warn('Failed to set organization as active, but proceeding with upgrade', { - organizationId: referenceId, - error: error instanceof Error ? error.message : 'Unknown error', - }) - // Continue with upgrade even if setting active fails + } else if (orgsData.isMemberOfAnyOrg) { + throw new Error( + 'You are already a member of an organization. Please leave it or ask an admin to upgrade.' + ) + } else { + logger.info('Will create organization after payment succeeds', { userId }) } } catch (error) { - logger.error('Failed to prepare organization for team plan', error) + logger.error('Failed to prepare for team plan upgrade', error) throw error instanceof Error ? error : new Error('Failed to prepare team workspace. Please try again or contact support.') @@ -134,23 +91,17 @@ export function useSubscriptionUpgrade() { ...(targetPlan === 'team' && { seats: CONSTANTS.INITIAL_TEAM_SEATS }), } as const - // Add subscriptionId for existing subscriptions to ensure proper plan switching const finalParams = currentSubscriptionId ? { ...upgradeParams, subscriptionId: currentSubscriptionId } : upgradeParams logger.info( currentSubscriptionId ? 'Upgrading existing subscription' : 'Creating new subscription', - { - targetPlan, - currentSubscriptionId, - referenceId, - } + { targetPlan, currentSubscriptionId, referenceId } ) await betterAuthSubscription.upgrade(finalParams) - // If upgrading to team plan, ensure the subscription is transferred to the organization if (targetPlan === 'team' && currentSubscriptionId && referenceId !== userId) { try { logger.info('Transferring subscription to organization after upgrade', { @@ -174,7 +125,6 @@ export function useSubscriptionUpgrade() { organizationId: referenceId, error: text, }) - // We don't throw here because the upgrade itself succeeded } else { logger.info('Successfully transferred subscription to organization', { subscriptionId: currentSubscriptionId, @@ -186,21 +136,16 @@ export function useSubscriptionUpgrade() { } } - // For team plans, refresh organization data to ensure UI updates if (targetPlan === 'team') { try { await queryClient.invalidateQueries({ queryKey: organizationKeys.lists() }) logger.info('Refreshed organization data after team upgrade') } catch (error) { logger.warn('Failed to refresh organization data after upgrade', error) - // Don't fail the entire upgrade if data refresh fails } } - logger.info('Subscription upgrade completed successfully', { - targetPlan, - referenceId, - }) + logger.info('Subscription upgrade completed successfully', { targetPlan, referenceId }) } catch (error) { logger.error('Failed to initiate subscription upgrade:', error) diff --git a/apps/sim/lib/billing/organization.ts b/apps/sim/lib/billing/organization.ts index 17511fc4a..61033832d 100644 --- a/apps/sim/lib/billing/organization.ts +++ b/apps/sim/lib/billing/organization.ts @@ -76,9 +76,6 @@ async function createOrganizationWithOwner( return newOrg.id } -/** - * Create organization for team/enterprise plan upgrade - */ export async function createOrganizationForTeamPlan( userId: string, userName?: string, @@ -86,13 +83,11 @@ export async function createOrganizationForTeamPlan( organizationSlug?: string ): Promise { try { - // Check if user already owns an organization const existingOrgId = await getUserOwnedOrganization(userId) if (existingOrgId) { return existingOrgId } - // Create new organization (same naming for both team and enterprise) const organizationName = userName || `${userEmail || 'User'}'s Team` const slug = organizationSlug || `${userId}-team-${Date.now()}` @@ -117,6 +112,84 @@ export async function createOrganizationForTeamPlan( } } +export async function ensureOrganizationForTeamSubscription( + subscription: SubscriptionData +): Promise { + if (subscription.plan !== 'team') { + return subscription + } + + if (subscription.referenceId.startsWith('org_')) { + return subscription + } + + const userId = subscription.referenceId + + logger.info('Creating organization for team subscription', { + subscriptionId: subscription.id, + userId, + }) + + const existingMembership = await db + .select({ + id: schema.member.id, + organizationId: schema.member.organizationId, + role: schema.member.role, + }) + .from(schema.member) + .where(eq(schema.member.userId, userId)) + .limit(1) + + if (existingMembership.length > 0) { + const membership = existingMembership[0] + if (membership.role === 'owner' || membership.role === 'admin') { + logger.info('User already owns/admins an org, using it', { + userId, + organizationId: membership.organizationId, + }) + + await db + .update(schema.subscription) + .set({ referenceId: membership.organizationId }) + .where(eq(schema.subscription.id, subscription.id)) + + return { ...subscription, referenceId: membership.organizationId } + } + + logger.error('User is member of org but not owner/admin - cannot create team subscription', { + userId, + existingOrgId: membership.organizationId, + subscriptionId: subscription.id, + }) + throw new Error('User is already member of another organization') + } + + const [userData] = await db + .select({ name: schema.user.name, email: schema.user.email }) + .from(schema.user) + .where(eq(schema.user.id, userId)) + .limit(1) + + const orgId = await createOrganizationForTeamPlan( + userId, + userData?.name || undefined, + userData?.email || undefined + ) + + await db + .update(schema.subscription) + .set({ referenceId: orgId }) + .where(eq(schema.subscription.id, subscription.id)) + + logger.info('Created organization and updated subscription referenceId', { + subscriptionId: subscription.id, + userId, + organizationId: orgId, + }) + + return { ...subscription, referenceId: orgId } +} + /** * Sync usage limits for subscription members * Updates usage limits for all users associated with the subscription From 9762bbc4514da62de9ec5ff2b432f2971142fdcf Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 12:20:45 -0800 Subject: [PATCH 04/17] fix(wand): validate session before allowing access to wand generation (#2383) * fix(wand): validate session before allowing access to wand generation * ack PR comment * ack PR comments --- apps/sim/app/api/wand/route.ts | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/apps/sim/app/api/wand/route.ts b/apps/sim/app/api/wand/route.ts index b3925663d..a1c1b573a 100644 --- a/apps/sim/app/api/wand/route.ts +++ b/apps/sim/app/api/wand/route.ts @@ -3,11 +3,13 @@ import { userStats, workflow } from '@sim/db/schema' import { eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import OpenAI, { AzureOpenAI } from 'openai' +import { getSession } from '@/lib/auth' import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing' import { env } from '@/lib/core/config/env' import { getCostMultiplier, isBillingEnabled } from '@/lib/core/config/feature-flags' import { generateRequestId } from '@/lib/core/utils/request' import { createLogger } from '@/lib/logs/console/logger' +import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' import { getModelPricing } from '@/providers/utils' export const dynamic = 'force-dynamic' @@ -135,7 +137,6 @@ async function updateUserStatsForWand( costAdded: costToStore, }) - // Check if user has hit overage threshold and bill incrementally await checkAndBillOverageThreshold(userId) } catch (error) { logger.error(`[${requestId}] Failed to update user stats for wand usage`, error) @@ -146,6 +147,12 @@ export async function POST(req: NextRequest) { const requestId = generateRequestId() logger.info(`[${requestId}] Received wand generation request`) + const session = await getSession() + if (!session?.user?.id) { + logger.warn(`[${requestId}] Unauthorized wand generation attempt`) + return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 }) + } + if (!client) { logger.error(`[${requestId}] AI client not initialized. Missing API key.`) return NextResponse.json( @@ -167,6 +174,35 @@ export async function POST(req: NextRequest) { ) } + if (workflowId) { + const [workflowRecord] = await db + .select({ workspaceId: workflow.workspaceId, userId: workflow.userId }) + .from(workflow) + .where(eq(workflow.id, workflowId)) + .limit(1) + + if (!workflowRecord) { + logger.warn(`[${requestId}] Workflow not found: ${workflowId}`) + return NextResponse.json({ success: false, error: 'Workflow not found' }, { status: 404 }) + } + + if (workflowRecord.workspaceId) { + const permission = await verifyWorkspaceMembership( + session.user.id, + workflowRecord.workspaceId + ) + if (!permission || (permission !== 'admin' && permission !== 'write')) { + logger.warn( + `[${requestId}] User ${session.user.id} does not have write access to workspace for workflow ${workflowId}` + ) + return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 403 }) + } + } else if (workflowRecord.userId !== session.user.id) { + logger.warn(`[${requestId}] User ${session.user.id} does not own workflow ${workflowId}`) + return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 403 }) + } + } + const finalSystemPrompt = systemPrompt || 'You are a helpful AI assistant. Generate content exactly as requested by the user.' From 8d38c2f15e49455079a9798e05bd621053f8fa29 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 15 Dec 2025 13:17:05 -0800 Subject: [PATCH 05/17] fix(wand): should not be able to use wand ui without write/admin perms (#2384) --- .../components/editor/components/sub-block/sub-block.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index 22d1e16a4..395c4c61d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -157,6 +157,7 @@ const renderLabel = ( isWandEnabled: boolean isPreview: boolean isStreaming: boolean + disabled: boolean onSearchClick: () => void onSearchBlur: () => void onSearchChange: (value: string) => void @@ -175,6 +176,7 @@ const renderLabel = ( isWandEnabled, isPreview, isStreaming, + disabled, onSearchClick, onSearchBlur, onSearchChange, @@ -208,7 +210,7 @@ const renderLabel = (
{/* Wand inline prompt */} - {isWandEnabled && !isPreview && ( + {isWandEnabled && !isPreview && !disabled && (
{!isSearchActive ? (
- {page.data.title} - {page.data.description} + {data.title} + {data.description}
, - h2: (props) => , - h3: (props) => , - h4: (props) => , - h5: (props) => , - h6: (props) => , + h1: (props: React.HTMLAttributes) => ( + + ), + h2: (props: React.HTMLAttributes) => ( + + ), + h3: (props: React.HTMLAttributes) => ( + + ), + h4: (props: React.HTMLAttributes) => ( + + ), + h5: (props: React.HTMLAttributes) => ( + + ), + h6: (props: React.HTMLAttributes) => ( + + ), }} /> @@ -240,16 +254,17 @@ export async function generateMetadata(props: { const page = source.getPage(params.slug, params.lang) if (!page) notFound() + const data = page.data as PageData const baseUrl = 'https://docs.sim.ai' const fullUrl = `${baseUrl}${page.url}` - const description = page.data.description || '' - const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(page.data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}` + const description = data.description || '' + const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}` return { - title: page.data.title, + title: data.title, description: - page.data.description || 'Sim visual workflow builder for AI applications documentation', + data.description || 'Sim visual workflow builder for AI applications documentation', keywords: [ 'AI workflow builder', 'visual workflow editor', @@ -258,16 +273,16 @@ export async function generateMetadata(props: { 'AI agents', 'no-code AI', 'drag and drop workflows', - page.data.title?.toLowerCase().split(' '), + data.title?.toLowerCase().split(' '), ] .flat() .filter(Boolean), authors: [{ name: 'Sim Team' }], category: 'Developer Tools', openGraph: { - title: page.data.title, + title: data.title, description: - page.data.description || 'Sim visual workflow builder for AI applications documentation', + data.description || 'Sim visual workflow builder for AI applications documentation', url: fullUrl, siteName: 'Sim Documentation', type: 'article', @@ -280,15 +295,15 @@ export async function generateMetadata(props: { url: ogImageUrl, width: 1200, height: 630, - alt: page.data.title, + alt: data.title, }, ], }, twitter: { card: 'summary_large_image', - title: page.data.title, + title: data.title, description: - page.data.description || 'Sim visual workflow builder for AI applications documentation', + data.description || 'Sim visual workflow builder for AI applications documentation', images: [ogImageUrl], creator: '@simdotai', site: '@simdotai', diff --git a/apps/docs/app/api/og/route.tsx b/apps/docs/app/api/og/route.tsx index c4d8e550a..5458cb1be 100644 --- a/apps/docs/app/api/og/route.tsx +++ b/apps/docs/app/api/og/route.tsx @@ -43,7 +43,6 @@ export async function GET(request: NextRequest) { const description = searchParams.get('description') || '' const baseUrl = new URL(request.url).origin - const backgroundImageUrl = `${baseUrl}/static/og-background.png` const allText = `${title}${category}${description}docs.sim.ai` const fontData = await loadGoogleFont('Geist', '400;500;600', allText) @@ -55,36 +54,49 @@ export async function GET(request: NextRequest) { width: '100%', display: 'flex', flexDirection: 'column', - background: 'linear-gradient(315deg, #1e1e3f 0%, #1a1a2e 40%, #0f0f0f 100%)', + background: '#0c0c0c', position: 'relative', fontFamily: 'Geist', }} > - {/* Background texture */} - - {/* Subtle purple glow from bottom right */} + {/* Secondary glow - adds depth without harsh edges */}
+ + {/* Top darkening - creates natural vignette */} +
diff --git a/apps/docs/lib/llms.ts b/apps/docs/lib/llms.ts index dc206b817..ec6bd0381 100644 --- a/apps/docs/lib/llms.ts +++ b/apps/docs/lib/llms.ts @@ -1,9 +1,10 @@ import type { InferPageType } from 'fumadocs-core/source' -import type { source } from '@/lib/source' +import type { PageData, source } from '@/lib/source' export async function getLLMText(page: InferPageType) { - const processed = await page.data.getText('processed') - return `# ${page.data.title} (${page.url}) + const data = page.data as PageData + const processed = await data.getText('processed') + return `# ${data.title} (${page.url}) ${processed}` } diff --git a/apps/docs/lib/source.ts b/apps/docs/lib/source.ts index 3376bd897..5eddfe166 100644 --- a/apps/docs/lib/source.ts +++ b/apps/docs/lib/source.ts @@ -1,4 +1,5 @@ -import { loader } from 'fumadocs-core/source' +import { type InferPageType, loader } from 'fumadocs-core/source' +import type { DocData, DocMethods } from 'fumadocs-mdx/runtime/types' import { docs } from '@/.source/server' import { i18n } from './i18n' @@ -7,3 +8,13 @@ export const source = loader({ source: docs.toFumadocsSource(), i18n, }) + +/** Full page data type including MDX content and metadata */ +export type PageData = DocData & + DocMethods & { + title: string + description?: string + full?: boolean + } + +export type Page = InferPageType diff --git a/apps/docs/package.json b/apps/docs/package.json index 8987f561f..a589e671e 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -19,7 +19,7 @@ "fumadocs-mdx": "14.1.0", "fumadocs-ui": "16.2.3", "lucide-react": "^0.511.0", - "next": "16.0.9", + "next": "16.1.0-canary.21", "next-themes": "^0.4.6", "react": "19.2.1", "react-dom": "19.2.1", diff --git a/apps/sim/app/api/function/execute/route.ts b/apps/sim/app/api/function/execute/route.ts index 10d73f14b..9cc78d6ca 100644 --- a/apps/sim/app/api/function/execute/route.ts +++ b/apps/sim/app/api/function/execute/route.ts @@ -5,6 +5,10 @@ import { executeInE2B } from '@/lib/execution/e2b' import { executeInIsolatedVM } from '@/lib/execution/isolated-vm' import { CodeLanguage, DEFAULT_CODE_LANGUAGE, isValidCodeLanguage } from '@/lib/execution/languages' import { createLogger } from '@/lib/logs/console/logger' +import { + createEnvVarPattern, + createWorkflowVariablePattern, +} from '@/executor/utils/reference-validation' export const dynamic = 'force-dynamic' export const runtime = 'nodejs' @@ -321,9 +325,9 @@ function createUserFriendlyErrorMessage( ): string { let errorMessage = enhanced.message - // Add line and column information if available + // Add line information if available if (enhanced.line !== undefined) { - let lineInfo = `Line ${enhanced.line}${enhanced.column !== undefined ? `:${enhanced.column}` : ''}` + let lineInfo = `Line ${enhanced.line}` // Add the actual line content if available if (enhanced.lineContent) { @@ -337,8 +341,7 @@ function createUserFriendlyErrorMessage( const stackMatch = enhanced.stack.match(/user-function\.js:(\d+)(?::(\d+))?/) if (stackMatch) { const line = Number.parseInt(stackMatch[1], 10) - const column = stackMatch[2] ? Number.parseInt(stackMatch[2], 10) : undefined - let lineInfo = `Line ${line}${column ? `:${column}` : ''}` + let lineInfo = `Line ${line}` // Try to get line content if we have userCode if (userCode) { @@ -375,27 +378,6 @@ function createUserFriendlyErrorMessage( } } - // For syntax errors, provide additional context - if (enhanced.name === 'SyntaxError') { - if (errorMessage.includes('Invalid or unexpected token')) { - errorMessage += ' (Check for missing quotes, brackets, or semicolons)' - } else if (errorMessage.includes('Unexpected end of input')) { - errorMessage += ' (Check for missing closing brackets or braces)' - } else if (errorMessage.includes('Unexpected token')) { - // Check if this might be due to incomplete code - if ( - enhanced.lineContent && - ((enhanced.lineContent.includes('(') && !enhanced.lineContent.includes(')')) || - (enhanced.lineContent.includes('[') && !enhanced.lineContent.includes(']')) || - (enhanced.lineContent.includes('{') && !enhanced.lineContent.includes('}'))) - ) { - errorMessage += ' (Check for missing closing parentheses, brackets, or braces)' - } else { - errorMessage += ' (Check your syntax)' - } - } - } - return errorMessage } @@ -409,19 +391,27 @@ function resolveWorkflowVariables( ): string { let resolvedCode = code - const variableMatches = resolvedCode.match(/]+)>/g) || [] - for (const match of variableMatches) { - const variableName = match.slice(' = [] + + while ((match = regex.exec(code)) !== null) { + const variableName = match[1].trim() // Find the variable by name (workflowVariables is indexed by ID, values are variable objects) const foundVariable = Object.entries(workflowVariables).find( ([_, variable]) => (variable.name || '').replace(/\s+/g, '') === variableName ) + let variableValue: unknown = '' if (foundVariable) { const variable = foundVariable[1] - // Get the typed value - handle different variable types - let variableValue = variable.value + variableValue = variable.value if (variable.value !== undefined && variable.value !== null) { try { @@ -443,22 +433,30 @@ function resolveWorkflowVariables( // Keep original value if JSON parsing fails } } - } catch (error) { + } catch { // Fallback to original value on error variableValue = variable.value } } - - // Create a safe variable reference - const safeVarName = `__variable_${variableName.replace(/[^a-zA-Z0-9_]/g, '_')}` - contextVariables[safeVarName] = variableValue - - // Replace the variable reference with the safe variable name - resolvedCode = resolvedCode.replace(new RegExp(escapeRegExp(match), 'g'), safeVarName) - } else { - // Variable not found - replace with empty string to avoid syntax errors - resolvedCode = resolvedCode.replace(new RegExp(escapeRegExp(match), 'g'), '') } + + replacements.push({ + match: match[0], + index: match.index, + variableName, + variableValue, + }) + } + + // Process replacements in reverse order to maintain correct indices + for (let i = replacements.length - 1; i >= 0; i--) { + const { match: matchStr, index, variableName, variableValue } = replacements[i] + + // Use variable reference approach + const safeVarName = `__variable_${variableName.replace(/[^a-zA-Z0-9_]/g, '_')}` + contextVariables[safeVarName] = variableValue + resolvedCode = + resolvedCode.slice(0, index) + safeVarName + resolvedCode.slice(index + matchStr.length) } return resolvedCode @@ -475,18 +473,29 @@ function resolveEnvironmentVariables( ): string { let resolvedCode = code - const envVarMatches = resolvedCode.match(/\{\{([^}]+)\}\}/g) || [] - for (const match of envVarMatches) { - const varName = match.slice(2, -2).trim() - // Priority: 1. Environment variables from workflow, 2. Params - const varValue = envVars[varName] || params[varName] || '' + const regex = createEnvVarPattern() + let match: RegExpExecArray | null + const replacements: Array<{ match: string; index: number; varName: string; varValue: string }> = + [] + + while ((match = regex.exec(code)) !== null) { + const varName = match[1].trim() + const varValue = envVars[varName] || params[varName] || '' + replacements.push({ + match: match[0], + index: match.index, + varName, + varValue: String(varValue), + }) + } + + for (let i = replacements.length - 1; i >= 0; i--) { + const { match: matchStr, index, varName, varValue } = replacements[i] - // Instead of injecting large JSON directly, create a variable reference const safeVarName = `__var_${varName.replace(/[^a-zA-Z0-9_]/g, '_')}` contextVariables[safeVarName] = varValue - - // Replace the template with a variable reference - resolvedCode = resolvedCode.replace(new RegExp(escapeRegExp(match), 'g'), safeVarName) + resolvedCode = + resolvedCode.slice(0, index) + safeVarName + resolvedCode.slice(index + matchStr.length) } return resolvedCode @@ -905,18 +914,22 @@ export async function POST(req: NextRequest) { const executionTime = Date.now() - startTime if (isolatedResult.error) { - const errorObj = { - message: isolatedResult.error.message, - name: isolatedResult.error.name, - stack: isolatedResult.error.stack, - } - logger.error(`[${requestId}] Function execution failed in isolated-vm`, { error: isolatedResult.error, executionTime, }) - const enhancedError = extractEnhancedError(errorObj, userCodeStartLine, resolvedCode) + const ivmError = isolatedResult.error + const enhancedError: EnhancedError = { + message: ivmError.message, + name: ivmError.name, + stack: ivmError.stack, + originalError: ivmError, + line: ivmError.line, + column: ivmError.column, + lineContent: ivmError.lineContent, + } + const userFriendlyErrorMessage = createUserFriendlyErrorMessage( enhancedError, requestId, @@ -924,13 +937,12 @@ export async function POST(req: NextRequest) { ) logger.error(`[${requestId}] Enhanced error details`, { - originalMessage: errorObj.message, + originalMessage: ivmError.message, enhancedMessage: userFriendlyErrorMessage, line: enhancedError.line, column: enhancedError.column, lineContent: enhancedError.lineContent, errorType: enhancedError.name, - userCodeStartLine, }) return NextResponse.json( @@ -978,7 +990,6 @@ export async function POST(req: NextRequest) { resolvedCode ) - // Log enhanced error details for debugging logger.error(`[${requestId}] Enhanced error details`, { originalMessage: error.message, enhancedMessage: userFriendlyErrorMessage, @@ -997,7 +1008,6 @@ export async function POST(req: NextRequest) { stdout: cleanStdout(stdout), executionTime, }, - // Include debug information in development or for debugging debug: { line: enhancedError.line, column: enhancedError.column, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/code-editor/code-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/code-editor/code-editor.tsx index 714d40a39..17d855ec6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/code-editor/code-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/code-editor/code-editor.tsx @@ -13,6 +13,10 @@ import { } from '@/components/emcn' import { Button } from '@/components/ui/button' import { cn } from '@/lib/core/utils/cn' +import { + createEnvVarPattern, + createWorkflowVariablePattern, +} from '@/executor/utils/reference-validation' interface CodeEditorProps { value: string @@ -132,15 +136,28 @@ export function CodeEditor({ return highlight(code, languages[language], language) } - const placeholders: Array<{ placeholder: string; original: string; type: 'env' | 'param' }> = [] + const escapeHtml = (text: string) => + text.replace(/&/g, '&').replace(//g, '>') + + const placeholders: Array<{ + placeholder: string + original: string + type: 'env' | 'param' | 'variable' + }> = [] let processedCode = code - processedCode = processedCode.replace(/\{\{([^}]+)\}\}/g, (match) => { + processedCode = processedCode.replace(createEnvVarPattern(), (match) => { const placeholder = `__ENV_VAR_${placeholders.length}__` placeholders.push({ placeholder, original: match, type: 'env' }) return placeholder }) + processedCode = processedCode.replace(createWorkflowVariablePattern(), (match) => { + const placeholder = `__VARIABLE_${placeholders.length}__` + placeholders.push({ placeholder, original: match, type: 'variable' }) + return placeholder + }) + if (schemaParameters.length > 0) { schemaParameters.forEach((param) => { const escapedName = param.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') @@ -156,9 +173,10 @@ export function CodeEditor({ let highlighted = highlight(processedCode, languages[language], language) placeholders.forEach(({ placeholder, original, type }) => { + const escapedOriginal = type === 'variable' ? escapeHtml(original) : original const replacement = - type === 'env' - ? `${original}` + type === 'env' || type === 'variable' + ? `${escapedOriginal}` : `${original}` highlighted = highlighted.replace(placeholder, replacement) diff --git a/apps/sim/executor/utils/reference-validation.ts b/apps/sim/executor/utils/reference-validation.ts index ce569561c..6f93c57fa 100644 --- a/apps/sim/executor/utils/reference-validation.ts +++ b/apps/sim/executor/utils/reference-validation.ts @@ -19,6 +19,17 @@ export function createEnvVarPattern(): RegExp { return new RegExp(`\\${REFERENCE.ENV_VAR_START}([^}]+)\\${REFERENCE.ENV_VAR_END}`, 'g') } +/** + * Creates a regex pattern for matching workflow variables + * Captures the variable name (after "variable.") in group 1 + */ +export function createWorkflowVariablePattern(): RegExp { + return new RegExp( + `${REFERENCE.START}${REFERENCE.PREFIX.VARIABLE}\\${REFERENCE.PATH_DELIMITER}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`, + 'g' + ) +} + /** * Combined pattern matching both and {{env_var}} */ diff --git a/apps/sim/lib/core/security/csp.ts b/apps/sim/lib/core/security/csp.ts index 5e379b590..68ea7ec1e 100644 --- a/apps/sim/lib/core/security/csp.ts +++ b/apps/sim/lib/core/security/csp.ts @@ -55,6 +55,7 @@ export const buildTimeCSPDirectives: CSPDirectives = { 'https://*.s3.amazonaws.com', 'https://s3.amazonaws.com', 'https://github.com/*', + 'https://collector.onedollarstats.com', ...(env.S3_BUCKET_NAME && env.AWS_REGION ? [`https://${env.S3_BUCKET_NAME}.s3.${env.AWS_REGION}.amazonaws.com`] : []), @@ -153,7 +154,7 @@ export function generateRuntimeCSP(): string { default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.google.com https://apis.google.com https://assets.onedollarstats.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; - img-src 'self' data: blob: https://*.googleusercontent.com https://*.google.com https://*.atlassian.com https://cdn.discordapp.com https://*.githubusercontent.com https://*.s3.amazonaws.com https://s3.amazonaws.com https://*.amazonaws.com https://*.blob.core.windows.net https://github.com/* ${brandLogoDomain} ${brandFaviconDomain}; + img-src 'self' data: blob: https://*.googleusercontent.com https://*.google.com https://*.atlassian.com https://cdn.discordapp.com https://*.githubusercontent.com https://*.s3.amazonaws.com https://s3.amazonaws.com https://*.amazonaws.com https://*.blob.core.windows.net https://github.com/* https://collector.onedollarstats.com ${brandLogoDomain} ${brandFaviconDomain}; media-src 'self' blob:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ${appUrl} ${ollamaUrl} ${socketUrl} ${socketWsUrl} https://api.browser-use.com https://api.exa.ai https://api.firecrawl.dev https://*.googleapis.com https://*.amazonaws.com https://*.s3.amazonaws.com https://*.blob.core.windows.net https://api.github.com https://github.com/* https://*.atlassian.com https://*.supabase.co https://collector.onedollarstats.com ${dynamicDomainsStr}; diff --git a/apps/sim/lib/execution/isolated-vm.ts b/apps/sim/lib/execution/isolated-vm.ts index 678bbb646..5411fc125 100644 --- a/apps/sim/lib/execution/isolated-vm.ts +++ b/apps/sim/lib/execution/isolated-vm.ts @@ -28,8 +28,22 @@ export interface IsolatedVMError { message: string name: string stack?: string + line?: number + column?: number + lineContent?: string } +/** + * Number of lines added before user code in the wrapper. + * The wrapper structure is: + * Line 1: (empty - newline after template literal backtick) + * Line 2: (async () => { + * Line 3: try { + * Line 4: const __userResult = await (async () => { + * Line 5+: user code starts here + */ +const USER_CODE_START_LINE = 4 + /** * Secure fetch wrapper that validates URLs to prevent SSRF attacks */ @@ -69,28 +83,96 @@ async function secureFetch( } } +/** + * Extract line and column from error stack or message + */ +function extractLineInfo(errorMessage: string, stack?: string): { line?: number; column?: number } { + if (stack) { + const stackMatch = stack.match(/(?:|user-function\.js):(\d+):(\d+)/) + if (stackMatch) { + return { + line: Number.parseInt(stackMatch[1], 10), + column: Number.parseInt(stackMatch[2], 10), + } + } + const atMatch = stack.match(/at\s+(?:|user-function\.js):(\d+):(\d+)/) + if (atMatch) { + return { + line: Number.parseInt(atMatch[1], 10), + column: Number.parseInt(atMatch[2], 10), + } + } + } + + const msgMatch = errorMessage.match(/:(\d+):(\d+)/) + if (msgMatch) { + return { + line: Number.parseInt(msgMatch[1], 10), + column: Number.parseInt(msgMatch[2], 10), + } + } + + return {} +} + /** * Convert isolated-vm error info to a format compatible with the route's error handling */ -function convertToCompatibleError(errorInfo: { - message: string - name: string - stack?: string -}): IsolatedVMError { - const { message, name } = errorInfo - let { stack } = errorInfo +function convertToCompatibleError( + errorInfo: { + message: string + name: string + stack?: string + }, + userCode: string +): IsolatedVMError { + const { name } = errorInfo + let { message, stack } = errorInfo + + message = message + .replace(/\s*\[user-function\.js:\d+:\d+\]/g, '') + .replace(/\s*\[:\d+:\d+\]/g, '') + .replace(/\s*\(:\d+:\d+\)/g, '') + .trim() + + const lineInfo = extractLineInfo(errorInfo.message, stack) + + let userLine: number | undefined + let lineContent: string | undefined + + if (lineInfo.line !== undefined) { + userLine = lineInfo.line - USER_CODE_START_LINE + const codeLines = userCode.split('\n') + if (userLine > 0 && userLine <= codeLines.length) { + lineContent = codeLines[userLine - 1]?.trim() + } else if (userLine <= 0) { + userLine = 1 + lineContent = codeLines[0]?.trim() + } else { + userLine = codeLines.length + lineContent = codeLines[codeLines.length - 1]?.trim() + } + } if (stack) { stack = stack.replace(/:(\d+):(\d+)/g, (_, line, col) => { - return `user-function.js:${line}:${col}` + const adjustedLine = Number.parseInt(line, 10) - USER_CODE_START_LINE + return `user-function.js:${Math.max(1, adjustedLine)}:${col}` + }) + stack = stack.replace(/at :(\d+):(\d+)/g, (_, line, col) => { + const adjustedLine = Number.parseInt(line, 10) - USER_CODE_START_LINE + return `at user-function.js:${Math.max(1, adjustedLine)}:${col}` }) - stack = stack.replace( - /at :(\d+):(\d+)/g, - (_, line, col) => `at user-function.js:${line}:${col}` - ) } - return { message, name, stack } + return { + message, + name, + stack, + line: userLine, + column: lineInfo.column, + lineContent, + } } /** @@ -255,7 +337,7 @@ export async function executeInIsolatedVM( if (parsed.success) { result = parsed.result } else if (parsed.errorInfo) { - error = convertToCompatibleError(parsed.errorInfo) + error = convertToCompatibleError(parsed.errorInfo, code) } else { error = { message: 'Unknown error', name: 'Error' } } @@ -295,7 +377,7 @@ export async function executeInIsolatedVM( return { result: null, stdout, - error: convertToCompatibleError(errorInfo), + error: convertToCompatibleError(errorInfo, code), } } @@ -305,6 +387,8 @@ export async function executeInIsolatedVM( error: { message: String(err), name: 'Error', + line: 1, + lineContent: code.split('\n')[0]?.trim(), }, } } finally { diff --git a/apps/sim/package.json b/apps/sim/package.json index 83d469da3..b34c4a80f 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -26,8 +26,8 @@ "@anthropic-ai/sdk": "^0.39.0", "@aws-sdk/client-dynamodb": "3.940.0", "@aws-sdk/client-rds-data": "3.940.0", - "@aws-sdk/client-sqs": "3.947.0", "@aws-sdk/client-s3": "^3.779.0", + "@aws-sdk/client-sqs": "3.947.0", "@aws-sdk/lib-dynamodb": "3.940.0", "@aws-sdk/s3-request-presigner": "^3.779.0", "@azure/communication-email": "1.0.0", @@ -72,6 +72,7 @@ "@types/react-window": "2.0.0", "@types/three": "0.177.0", "better-auth": "1.3.12", + "binary-extensions": "^2.0.0", "browser-image-compression": "^2.0.2", "chalk": "5.6.2", "cheerio": "1.1.2", @@ -100,7 +101,7 @@ "mammoth": "^1.9.0", "mysql2": "3.14.3", "nanoid": "^3.3.7", - "next": "16.0.9", + "next": "16.1.0-canary.21", "next-mdx-remote": "^5.0.0", "next-runtime-env": "3.3.0", "next-themes": "^0.4.6", @@ -129,6 +130,7 @@ "stripe": "18.5.0", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", + "thread-stream": "4.0.0", "three": "0.177.0", "unpdf": "1.4.0", "uuid": "^11.1.0", @@ -169,8 +171,8 @@ "sharp" ], "overrides": { - "next": "16.0.9", - "@next/env": "16.0.9", + "next": "16.1.0-canary.21", + "@next/env": "16.1.0-canary.21", "drizzle-orm": "^0.44.5", "postgres": "^3.4.5" } diff --git a/apps/sim/socket-server/index.test.ts b/apps/sim/socket-server/index.test.ts index d21bb9b8a..52c35bc1d 100644 --- a/apps/sim/socket-server/index.test.ts +++ b/apps/sim/socket-server/index.test.ts @@ -93,8 +93,10 @@ describe('Socket Server Index Integration', () => { clearTimeout(timeout) if (err.code === 'EADDRINUSE') { PORT = 3333 + Math.floor(Math.random() * 1000) - httpServer.listen(PORT, '0.0.0.0', () => { - resolve() + httpServer.close(() => { + httpServer.listen(PORT, '0.0.0.0', () => { + resolve() + }) }) } else { reject(err) diff --git a/bun.lock b/bun.lock index 8091925af..004b8e3d5 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,6 @@ { "lockfileVersion": 1, - "configVersion": 0, + "configVersion": 1, "workspaces": { "": { "name": "simstudio", @@ -50,7 +50,7 @@ "fumadocs-mdx": "14.1.0", "fumadocs-ui": "16.2.3", "lucide-react": "^0.511.0", - "next": "16.0.9", + "next": "16.1.0-canary.21", "next-themes": "^0.4.6", "react": "19.2.1", "react-dom": "19.2.1", @@ -121,6 +121,7 @@ "@types/react-window": "2.0.0", "@types/three": "0.177.0", "better-auth": "1.3.12", + "binary-extensions": "^2.0.0", "browser-image-compression": "^2.0.2", "chalk": "5.6.2", "cheerio": "1.1.2", @@ -149,7 +150,7 @@ "mammoth": "^1.9.0", "mysql2": "3.14.3", "nanoid": "^3.3.7", - "next": "16.0.9", + "next": "16.1.0-canary.21", "next-mdx-remote": "^5.0.0", "next-runtime-env": "3.3.0", "next-themes": "^0.4.6", @@ -178,6 +179,7 @@ "stripe": "18.5.0", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", + "thread-stream": "4.0.0", "three": "0.177.0", "unpdf": "1.4.0", "uuid": "^11.1.0", @@ -259,13 +261,13 @@ }, "trustedDependencies": [ "ffmpeg-static", - "sharp", "isolated-vm", + "sharp", ], "overrides": { - "@next/env": "16.0.9", + "@next/env": "16.1.0-canary.21", "drizzle-orm": "^0.44.5", - "next": "16.0.9", + "next": "16.1.0-canary.21", "postgres": "^3.4.5", "react": "19.2.1", "react-dom": "19.2.1", @@ -273,35 +275,37 @@ "packages": { "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], - "@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.54", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6OSFMkt5NkAchH7o0W+dI2h6yR8EPXx7Yl6txyh0gadLlkf1UU/ScyoYlkxAW8UtGju/+apvwVTdLYEQuIsVVQ=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.56", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XHJKu0Yvfu9SPzRfsAFESa+9T7f2YJY6TxykKMfRsAwpeWAiX/Gbx5J5uM15AzYC3Rw8tVP3oH+j7jEivENirQ=="], - "@ai-sdk/azure": ["@ai-sdk/azure@2.0.82", "", { "dependencies": { "@ai-sdk/openai": "2.0.80", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Bpab51ETBB4adZC1xGMYsryL/CB8j1sA+t5aDqhRv3t3WRLTxhaBDcFKtQTIuxiEQTFosz9Q2xQqdfBvQm5jHw=="], + "@ai-sdk/azure": ["@ai-sdk/azure@2.0.88", "", { "dependencies": { "@ai-sdk/openai": "2.0.86", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OMAXXZV7GiFz8qpCpzhaesTfiuiXU92WZWdvtr+K8rjfTNGm9sJWUuSLZ29z5aAeLUSRlwDMUlK4lYr8/1IewQ=="], - "@ai-sdk/cerebras": ["@ai-sdk/cerebras@1.0.32", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.28", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FayoFKD/SORxBPV9yq+8q+diYOc9WSvTMkvRfN+VW2P/beQs+T7wblTWiNz/TEbVHx3nkLJAbCgD6X/4wYOT3g=="], + "@ai-sdk/cerebras": ["@ai-sdk/cerebras@1.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2gSSS/7kunIwMdC4td5oWsUAzoLw84ccGpz6wQbxVnrb1iWnrEnKa5tRBduaP6IXpzLWsu8wME3+dQhZy+gT7w=="], - "@ai-sdk/deepseek": ["@ai-sdk/deepseek@1.0.31", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Il7WJp8bA3CmlreYSl1YzCucGTn2e5P81IANYIIEeLtWrbK0Y9CLoOCROj8xKYyUSMKlINyGZX2uP79cKewtSg=="], + "@ai-sdk/deepseek": ["@ai-sdk/deepseek@1.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DDNZSZn6OuExVBJBAWdk3VeyQPH+pYwSykixePhzll9EnT3aakapMYr5gjw3wMl+eZ0tLplythHL1TfIehUZ0g=="], - "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cybb+k/3Kj9BX+Am1mun3dafZsHQLIzW2A4fu5FVTLSIGXXbcuXwXNNdYMGs+B0y6RYOQ8VHbf1QslMSDIxQMA=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-BwV7DU/lAm3Xn6iyyvZdWgVxgLu3SNXzl5y57gMvkW4nGhAOV5269IrJzQwGt03bb107sa6H6uJwWxc77zXoGA=="], - "@ai-sdk/google": ["@ai-sdk/google@2.0.45", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2kQUjjWGnaWzx92muvS0XQUM0+zy3TOv+kTdtfkD7n/9B3ikRZrq6clTNisg0MxUxHcXvNbBrmqbqIWLzU9Q1w=="], + "@ai-sdk/google": ["@ai-sdk/google@2.0.46", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8PK6u4sGE/kXebd7ZkTp+0aya4kNqzoqpS5m7cHY2NfTK6fhPc6GNvE+MZIZIoHQTp5ed86wGBdeBPpFaaUtyg=="], - "@ai-sdk/groq": ["@ai-sdk/groq@2.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-5kadf9Mjd4Ep6jVhrIy56UL7DV5HDisW8UakwB11IN7lSLi8Qwb1fB9uO34GT7JxYqE4w7qZXVuelOmTH9m2Mg=="], + "@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.91", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.56", "@ai-sdk/google": "2.0.46", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-SonFMMdSIlos0fjBFBff7rcZQx+q3WP4CpXdz7+YEIEWItnR/k9f5MqRCXMZilfyzcpz5wFxa7Sqlnapv3oqsA=="], - "@ai-sdk/mistral": ["@ai-sdk/mistral@2.0.25", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-JRlmXAgG/vuB3ojWjkGzN5W8ZYM8GX6dEkTFJX5sC1NwDgzfiTBIMDeQ/RA6dYZ06OIXtBZTt4oaruP+1xc92g=="], + "@ai-sdk/groq": ["@ai-sdk/groq@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FWGl7xNr88NBveao3y9EcVWYUt9ABPrwLFY7pIutSNgaTf32vgvyhREobaMrLU4Scr5G/2tlNqOPZ5wkYMaZig=="], - "@ai-sdk/openai": ["@ai-sdk/openai@2.0.80", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tNHuraF11db+8xJEDBoU9E3vMcpnHFKRhnLQ3DQX2LnEzfPB9DksZ8rE+yVuDN1WRW9cm2OWAhgHFgVKs7ICuw=="], + "@ai-sdk/mistral": ["@ai-sdk/mistral@2.0.26", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jxDB++4WI1wEx5ONNBI+VbkmYJOYIuS8UQY13/83UGRaiW7oB/WHiH4ETe6KzbKpQPB3XruwTJQjUMsMfKyTXA=="], - "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.28", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yKubDxLYtXyGUzkr9lNStf/lE/I+Okc8tmotvyABhsQHHieLKk6oV5fJeRJxhr67Ejhg+FRnwUOxAmjRoFM4dA=="], + "@ai-sdk/openai": ["@ai-sdk/openai@2.0.86", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-obsLIOyA93lbQiSt1rvBItoVQp1U2RDPs0bNG0JYhm6Gku8Dg/0Cm8e4NUWT5p5PN10/doKSb3SMSKCixwIAKA=="], - "@ai-sdk/perplexity": ["@ai-sdk/perplexity@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-eyADmcZ2Fz2p3cLEB2Cnm2msW6vxsRQPXuqJ+AWPwSBQLxx58o/wNkF2XRYzipqSxWyOinuIUNBa1Is13GE2ow=="], + "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], + + "@ai-sdk/perplexity": ["@ai-sdk/perplexity@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-zwzcnk08R2J3mZcQPn4Ifl4wYGrvANR7jsBB0hCTUSbb+Rx3ybpikSWiGuXQXxdiRc1I5MWXgj70m+bZaLPvHw=="], "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], - "@ai-sdk/togetherai": ["@ai-sdk/togetherai@1.0.29", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.28", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-NUGHmlRTsePi04YC+78gS+Wl2iNP6oNepfuvijgJKVwAZa7xHg4i/lF84OG5aI3r6LlA/779ZZUPoBcj/EHlLg=="], + "@ai-sdk/togetherai": ["@ai-sdk/togetherai@1.0.30", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9bxQbIXnWSN4bNismrza3NvIo+ui/Y3pj3UN6e9vCszCWFCN45RgISi4oDe10RqmzaJ/X8cfO/Tem+K8MT3wGQ=="], - "@ai-sdk/xai": ["@ai-sdk/xai@2.0.39", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.28", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-EtRRHpPb3J6qY8y9C9p1g3FdF8dl6SocmfyS418g+PesK9/bIAbJYWQStdWpJXF/d9VfzeoOp1IhcBgKotAn+A=="], + "@ai-sdk/xai": ["@ai-sdk/xai@2.0.41", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-mztE/1svgeBscJBEcy/AqVZSxTXasnN22o58Vsf7rX7f1X9gqadVoDHH4a/caiklADg/HNu2h1u+vTgVYL8XbA=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -335,9 +339,9 @@ "@aws-sdk/client-rds-data": ["@aws-sdk/client-rds-data@3.940.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.940.0", "@aws-sdk/credential-provider-node": "3.940.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.940.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.940.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.12", "@smithy/middleware-retry": "^4.4.12", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.8", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.11", "@smithy/util-defaults-mode-node": "^4.2.14", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-68NH61MvS48CVPfzBNCPdCG4KnNjM+Uj/3DSw7rT9PJvdML9ARS4M2Uqco9POPw+Aj20KBumsEUd6FMVcYBXAA=="], - "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.948.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.948.0", "@aws-sdk/middleware-bucket-endpoint": "3.936.0", "@aws-sdk/middleware-expect-continue": "3.936.0", "@aws-sdk/middleware-flexible-checksums": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-location-constraint": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-sdk-s3": "3.947.0", "@aws-sdk/middleware-ssec": "3.936.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/signature-v4-multi-region": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-uvEjds8aYA9SzhBS8RKDtsDUhNV9VhqKiHTcmvhM7gJO92q0WTn8/QeFTdNyLc6RxpiDyz+uBxS7PcdNiZzqfA=="], + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.952.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.952.0", "@aws-sdk/middleware-bucket-endpoint": "3.936.0", "@aws-sdk/middleware-expect-continue": "3.936.0", "@aws-sdk/middleware-flexible-checksums": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-location-constraint": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-sdk-s3": "3.947.0", "@aws-sdk/middleware-ssec": "3.936.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/signature-v4-multi-region": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-5EFrOpLL1f29eepx6lHYRlwk5Eeo0PVEMROZQmGAGo/7avjWGPwfgkDdfgP2JCIusiKUG6sHN4FOjd86Q7htLw=="], - "@aws-sdk/client-sesv2": ["@aws-sdk/client-sesv2@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.948.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/signature-v4-multi-region": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7Sl8bRFFLAEQdlvTlaSNFlUHjD+B3N+gbhpS+vH/IlETSmn3fMm4b0Bvve8CWs8jUCctx8nDwXh+0lOWgNDQXw=="], + "@aws-sdk/client-sesv2": ["@aws-sdk/client-sesv2@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.952.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/signature-v4-multi-region": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-0avirspZ7/RkHqp9It12xx6UJ2rkO6B6EeNScIgDkgyELl4tGsmF8bhBSPDqeJMZ1HQGYglanzkDRrYFgTN6iA=="], "@aws-sdk/client-sqs": ["@aws-sdk/client-sqs@3.947.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-sdk-sqs": "3.946.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-8tzFyYGAAnQg+G9eB5zAe0oEo+MJMZ3YEk+8EL4uf2zG5wKxJvTBJZr6U9I1CEXYUde374OyLMyKng+sWyN+wg=="], @@ -393,7 +397,7 @@ "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - "@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.948.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-format-url": "3.936.0", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tlQhMDsDWwUDUzzlo8XYGpmMfjGDmXgbysv1+h1v2xJe0A+ogv/nJ6KFVP94uf1j4ePmCN/gDdxEp2PWZMBPOQ=="], + "@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.952.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-format-url": "3.936.0", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-K/rJxP3O6TKTzDsBoVpExCZZlKbfY3SWNaR7ilm+mwBS/EXqY7sObYZU4Yhl+8aQlRTqDHgOOkR2+Qws0qD54Q=="], "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.947.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-UaYmzoxf9q3mabIA2hc4T6x5YSFUG2BpNjAZ207EA1bnQMiK+d6vZvb83t7dIWL/U1de1sGV19c1C81Jf14rrA=="], @@ -495,7 +499,7 @@ "@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="], - "@better-fetch/fetch": ["@better-fetch/fetch@1.1.19", "", {}, "sha512-cYVhTzWEus0D8bHPsmg9Hkc3XK0c2jkxYwsJGU3JK884paZ2c0QkUbLAfI1kmQSEN4uvk/YnUDWTqS4CFS3/Pw=="], + "@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="], "@biomejs/biome": ["@biomejs/biome@2.0.0-beta.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.0-beta.5", "@biomejs/cli-darwin-x64": "2.0.0-beta.5", "@biomejs/cli-linux-arm64": "2.0.0-beta.5", "@biomejs/cli-linux-arm64-musl": "2.0.0-beta.5", "@biomejs/cli-linux-x64": "2.0.0-beta.5", "@biomejs/cli-linux-x64-musl": "2.0.0-beta.5", "@biomejs/cli-win32-arm64": "2.0.0-beta.5", "@biomejs/cli-win32-x64": "2.0.0-beta.5" }, "bin": { "biome": "bin/biome" } }, "sha512-1ldO4AepieVvg4aLi1ubZkA7NsefQT2UTNssbJbDiQTGem8kCHx/PZCwLxIR6UzFpGIjh0xsDzivyVvhnmqmuA=="], @@ -517,9 +521,9 @@ "@browserbasehq/sdk": ["@browserbasehq/sdk@2.6.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA=="], - "@browserbasehq/stagehand": ["@browserbasehq/stagehand@3.0.5", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@anthropic-ai/sdk": "0.39.0", "@browserbasehq/sdk": "^2.4.0", "@google/genai": "^1.22.0", "@langchain/openai": "^0.4.4", "@modelcontextprotocol/sdk": "^1.17.2", "ai": "^5.0.0", "devtools-protocol": "^0.0.1464554", "fetch-cookie": "^3.1.0", "openai": "^4.87.1", "pino": "^9.6.0", "pino-pretty": "^13.0.0", "playwright": "^1.52.0", "ws": "^8.18.0", "zod-to-json-schema": "^3.25.0" }, "optionalDependencies": { "@ai-sdk/anthropic": "^2.0.34", "@ai-sdk/azure": "^2.0.54", "@ai-sdk/cerebras": "^1.0.25", "@ai-sdk/deepseek": "^1.0.23", "@ai-sdk/google": "^2.0.23", "@ai-sdk/groq": "^2.0.24", "@ai-sdk/mistral": "^2.0.19", "@ai-sdk/openai": "^2.0.53", "@ai-sdk/perplexity": "^2.0.13", "@ai-sdk/togetherai": "^1.0.23", "@ai-sdk/xai": "^2.0.26", "@langchain/core": "^0.3.40", "bufferutil": "^4.0.9", "chrome-launcher": "^1.2.0", "ollama-ai-provider-v2": "^1.5.0", "patchright-core": "^1.55.2", "playwright-core": "^1.54.1", "puppeteer-core": "^22.8.0" }, "peerDependencies": { "deepmerge": "^4.3.1", "dotenv": "^16.4.5", "zod": "3.25.76 || 4.1.8" } }, "sha512-89QPlRKpfq8kJd8oGgodo5I39xDjEPwVIEvdjaICcU33X4yAtkvoR4lM2tEwGiiDu/BPQznB27JFgoGlMjUsTA=="], + "@browserbasehq/stagehand": ["@browserbasehq/stagehand@3.0.6", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@anthropic-ai/sdk": "0.39.0", "@browserbasehq/sdk": "^2.4.0", "@google/genai": "^1.22.0", "@langchain/openai": "^0.4.4", "@modelcontextprotocol/sdk": "^1.17.2", "ai": "^5.0.0", "devtools-protocol": "^0.0.1464554", "fetch-cookie": "^3.1.0", "openai": "^4.87.1", "pino": "^9.6.0", "pino-pretty": "^13.0.0", "playwright": "^1.52.0", "uuid": "^11.1.0", "ws": "^8.18.0", "zod-to-json-schema": "^3.25.0" }, "optionalDependencies": { "@ai-sdk/anthropic": "^2.0.34", "@ai-sdk/azure": "^2.0.54", "@ai-sdk/cerebras": "^1.0.25", "@ai-sdk/deepseek": "^1.0.23", "@ai-sdk/google": "^2.0.23", "@ai-sdk/google-vertex": "^3.0.70", "@ai-sdk/groq": "^2.0.24", "@ai-sdk/mistral": "^2.0.19", "@ai-sdk/openai": "^2.0.53", "@ai-sdk/perplexity": "^2.0.13", "@ai-sdk/togetherai": "^1.0.23", "@ai-sdk/xai": "^2.0.26", "@langchain/core": "^0.3.40", "bufferutil": "^4.0.9", "chrome-launcher": "^1.2.0", "ollama-ai-provider-v2": "^1.5.0", "patchright-core": "^1.55.2", "playwright-core": "^1.54.1", "puppeteer-core": "^22.8.0" }, "peerDependencies": { "deepmerge": "^4.3.1", "dotenv": "^16.4.5", "zod": "^3.25.76 || ^4.1.8" } }, "sha512-WN/GuJMHXyXNsQTErmeTEstxUe0gZf9DLXlPkfJtVUuqErIJgZPiBstlTrdgx7I8n1ZE+q8gaEU/beJsW30+bg=="], - "@bufbuild/protobuf": ["@bufbuild/protobuf@2.10.1", "", {}, "sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg=="], + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.10.2", "", {}, "sha512-uFsRXwIGyu+r6AMdz+XijIIZJYpoWeYzILt5yZ2d3mCjQrWUTVpVD9WL/jZAbvp+Ed04rOhrsk7FiTcEDseB5A=="], "@bugsnag/cuid": ["@bugsnag/cuid@3.2.1", "", {}, "sha512-zpvN8xQ5rdRWakMd/BcVkdn2F8HKlDSbM3l7duueK590WmI1T0ObTLc1V/1e55r14WNjPd5AJTYX4yPEAFVi+Q=="], @@ -621,11 +625,11 @@ "@google-cloud/precise-date": ["@google-cloud/precise-date@4.0.0", "", {}, "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA=="], - "@google/genai": ["@google/genai@1.32.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.24.0" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-46vaEaHAThIBlqWFTti1fo3xYU6DwCOwnIIotLhYUbNha90wk5cZL79zdf+NoAfKVsx4DPmjCtXvbQNNVPl5ZQ=="], + "@google/genai": ["@google/genai@1.33.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.24.0" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-ThUjFZ1N0DU88peFjnQkb8K198EWaW2RmmnDShFQ+O+xkIH9itjpRe358x3L/b4X/A7dimkvq63oz49Vbh7Cog=="], "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], - "@grpc/grpc-js": ["@grpc/grpc-js@1.14.2", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-QzVUtEFyu05UNx2xr0fCQmStUO17uVQhGNowtxs00IgTZT6/W2PBLfUkj30s0FKJ29VtTa3ArVNIhNP6akQhqA=="], + "@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="], "@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=="], @@ -751,23 +755,23 @@ "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.84", "", { "os": "win32", "cpu": "x64" }, "sha512-YSs8ncurc1xzegUMNnQUTYrdrAuaXdPMOa+iYYyAxydOtg0ppV386hyYMsy00Yip1NlTgLCseRG4sHSnjQx6og=="], - "@next/env": ["@next/env@16.0.9", "", {}, "sha512-6284pl8c8n9PQidN63qjPVEu1uXXKjnmbmaLebOzIfTrSXdGiAPsIMRi4pk/+v/ezqweE1/B8bFqiAAfC6lMXg=="], + "@next/env": ["@next/env@16.1.0-canary.21", "", {}, "sha512-J5inWwxC8EpAr/a2GApmQK1KkftG7K2nM6SuzNvciNaPt9Z0AHFeazvFuQxbvXn024p+akBHRlo8P7ZJRoU7kA=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-j06fWg/gPqiWjK+sEpCDsh5gX+Bdy9gnPYjFqMBvBEOIcCFy1/ecF6pY6XAce7WyCJAbBPVb+6GvpmUZKNq0oQ=="], + "@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.0.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-FRYYz5GSKUkfvDSjd5hgHME2LgYjfOLBmhRVltbs3oRNQQf9n5UTQMmIu/u5vpkjJFV4L2tqo8duGqDxdQOFwg=="], + "@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.0.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-EI2klFVL8tOyEIX5J1gXXpm1YuChmDy4R+tHoNjkCHUmBJqXioYErX/O2go4pEhjxkAxHp2i8y5aJcRz2m5NqQ=="], + "@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.0.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-vq/5HeGvowhDPMrpp/KP4GjPVhIXnwNeDPF5D6XK6ta96UIt+C0HwJwuHYlwmn0SWyNANqx1Mp6qSVDXwbFKsw=="], + "@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.0.9", "", { "os": "linux", "cpu": "x64" }, "sha512-GlUdJwy2leA/HnyRYxJ1ZJLCJH+BxZfqV4E0iYLrJipDKxWejWpPtZUdccPmCfIEY9gNBO7bPfbG6IIgkt0qXg=="], + "@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.0.9", "", { "os": "linux", "cpu": "x64" }, "sha512-UCtOVx4N8AHF434VPwg4L0KkFLAd7pgJShzlX/hhv9+FDrT7/xCuVdlBsCXH7l9yCA/wHl3OqhMbIkgUluriWA=="], + "@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.0.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-tQjtDGtv63mV3n/cZ4TH8BgUvKTSFlrF06yT5DyRmgQuj5WEjBUDy0W3myIW5kTRYMPrLn42H3VfCNwBH6YYiA=="], + "@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.0.9", "", { "os": "win32", "cpu": "x64" }, "sha512-y9AGACHTBwnWFLq5B5Fiv3FEbXBusdPb60pgoerB04CV/pwjY1xQNdoTNxAv7eUhU2k1CKnkN4XWVuiK07uOqA=="], + "@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=="], @@ -861,7 +865,7 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.38.0", "", {}, "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg=="], - "@orama/orama": ["@orama/orama@3.1.16", "", {}, "sha512-scSmQBD8eANlMUOglxHrN1JdSW8tDghsPuS83otqealBiIeMukCQMOf/wc0JJjDXomqwNdEQFLXLGHrU6PGxuA=="], + "@orama/orama": ["@orama/orama@3.1.17", "", {}, "sha512-APwpZ+FTGMryo4QEeD6ti+Ei8suBkvxe8PeWdUcQHVfJDpjpt4c1dKojjNswcBmdeWSiiTYcnkKKH+yuo6727g=="], "@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=="], @@ -1075,69 +1079,69 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.4", "", { "os": "android", "cpu": "arm" }, "sha512-PWU3Y92H4DD0bOqorEPp1Y0tbzwAurFmIYpjcObv5axGVOtcTlB0b2UKMd2echo08MgN7jO8WQZSSysvfisFSQ=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.4", "", { "os": "android", "cpu": "arm64" }, "sha512-Gw0/DuVm3rGsqhMGYkSOXXIx20cC3kTlivZeuaGt4gEgILivykNyBWxeUV5Cf2tDA2nPLah26vq3emlRrWVbng=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+w06QvXsgzKwdVg5qRLZpTHh1bigHZIqoIUPtiqh05ZiJVUQ6ymOxaPkXTvRPRLH88575ZCRSRM3PwIoNma01Q=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-EB4Na9G2GsrRNRNFPuxfwvDRDUwQEzJPpiK1vo2zMVhEeufZ1k7J1bKnT0JYDfnPC7RNZ2H5YNQhW6/p2QKATw=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-bldA8XEqPcs6OYdknoTMaGhjytnwQ0NClSPpWpmufOuGPN5dDmvIa32FygC2gneKK4A1oSx86V1l55hyUWUYFQ=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-3T8GPjH6mixCd0YPn0bXtcuSXi1Lj+15Ujw2CEb7dd24j9thcKscCf88IV7n76WaAdorOzAgSSbuVRg4C8V8Qw=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.4", "", { "os": "linux", "cpu": "arm" }, "sha512-UPMMNeC4LXW7ZSHxeP3Edv09aLsFUMaD1TSVW6n1CWMECnUIJMFFB7+XC2lZTdPtvB36tYC0cJWc86mzSsaviw=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.4", "", { "os": "linux", "cpu": "arm" }, "sha512-H8uwlV0otHs5Q7WAMSoyvjV9DJPiy5nJ/xnHolY0QptLPjaSsuX7tw+SPIfiYH6cnVx3fe4EWFafo6gH6ekZKA=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-BLRwSRwICXz0TXkbIbqJ1ibK+/dSBpTJqDClF61GWIrxTXZWQE78ROeIhgl5MjVs4B4gSLPCFeD4xML9vbzvCQ=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-6bySEjOTbmVcPJAywjpGLckK793A0TJWSbIa0sVwtVGfe/Nz6gOWHOwkshUIAp9j7wg2WKcA4Snu7Y1nUZyQew=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.4", "", { "os": "linux", "cpu": "none" }, "sha512-U0ow3bXYJZ5MIbchVusxEycBw7bO6C2u5UvD31i5IMTrnt2p4Fh4ZbHSdc/31TScIJQYHwxbj05BpevB3201ug=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-iujDk07ZNwGLVn0YIWM80SFN039bHZHCdCCuX9nyx3Jsa2d9V/0Y32F+YadzwbvDxhSeVo9zefkoPnXEImnM5w=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.4", "", { "os": "linux", "cpu": "none" }, "sha512-MUtAktiOUSu+AXBpx1fkuG/Bi5rhlorGs3lw5QeJ2X3ziEGAq7vFNdWVde6XGaVqi0LGSvugwjoxSNJfHFTC0g=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.4", "", { "os": "linux", "cpu": "none" }, "sha512-btm35eAbDfPtcFEgaXCI5l3c2WXyzwiE8pArhd66SDtoLWmgK5/M7CUxmUglkwtniPzwvWioBKKl6IXLbPf2sQ=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-uJlhKE9ccUTCUlK+HUz/80cVtx2RayadC5ldDrrDUFaJK0SNb8/cCmC9RhBhIWuZ71Nqj4Uoa9+xljKWRogdhA=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.4", "", { "os": "linux", "cpu": "x64" }, "sha512-jjEMkzvASQBbzzlzf4os7nzSBd/cvPrpqXCUOqoeCh1dQ4BP3RZCJk8XBeik4MUln3m+8LeTJcY54C/u8wb3DQ=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lu90KG06NNH19shC5rBPkrh6mrTpq5kviFylPBXQVpdEu0yzb0mDgyxLr6XdcGdBIQTH/UAhDJnL+APZTBu1aQ=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.4", "", { "os": "none", "cpu": "arm64" }, "sha512-dFDcmLwsUzhAm/dn0+dMOQZoONVYBtgik0VuY/d5IJUUb787L3Ko/ibvTvddqhb3RaB7vFEozYevHN4ox22R/w=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-WvUpUAWmUxZKtRnQWpRKnLW2DEO8HB/l8z6oFFMNuHndMzFTJEXzaYJ5ZAmzNw0L21QQJZsUQFt2oPf3ykAD/w=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-JGbeF2/FDU0x2OLySw/jgvkwWUo05BSiJK0dtuI4LyuXbz3wKiC1xHhLB1Tqm5VU6ZZDmAorj45r/IgWNWku5g=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.4", "", { "os": "win32", "cpu": "x64" }, "sha512-zuuC7AyxLWLubP+mlUwEyR8M1ixW1ERNPHJfXm8x7eQNP4Pzkd7hS3qBuKBR70VRiQ04Kw8FNfRMF5TNxuZq2g=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.4", "", { "os": "win32", "cpu": "x64" }, "sha512-Sbx45u/Lbb5RyptSbX7/3deP+/lzEmZ0BTSHxwxN/IMOZDZf8S0AGo0hJD5n/LQssxb5Z3B4og4P2X6Dd8acCA=="], "@s2-dev/streamstore": ["@s2-dev/streamstore@0.17.3", "", { "dependencies": { "@protobuf-ts/runtime": "^2.11.1" }, "peerDependencies": { "typescript": "^5.9.3" } }, "sha512-UeXL5+MgZQfNkbhCgEDVm7PrV5B3bxh6Zp4C5pUzQQwaoA+iGh2QiiIptRZynWgayzRv4vh0PYfnKpTzJEXegQ=="], "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="], - "@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="], + "@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="], - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ=="], + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg=="], - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg=="], + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ=="], - "@shikijs/langs": ["@shikijs/langs@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg=="], + "@shikijs/langs": ["@shikijs/langs@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA=="], - "@shikijs/rehype": ["@shikijs/rehype@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.1", "shiki": "3.19.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" } }, "sha512-pzp/JVxrTd95HgMimHgYb9lCGSzVYEp1BweWUprFAEgGOF15d9IyX+IVW/+1Z5ZxdT9IUUF27UbC5YdA5oCzjw=="], + "@shikijs/rehype": ["@shikijs/rehype@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.1", "shiki": "3.20.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" } }, "sha512-/sqob3V/lJK0m2mZ64nkcWPN88im0D9atkI3S3PUBvtJZTHnJXVwZhHQFRDyObgEIa37IpHYHR3CuFtXB5bT2g=="], - "@shikijs/themes": ["@shikijs/themes@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-H36qw+oh91Y0s6OlFfdSuQ0Ld+5CgB/VE6gNPK+Hk4VRbVG/XQgkjnt4KzfnnoO6tZPtKJKHPjwebOCfjd6F8A=="], + "@shikijs/themes": ["@shikijs/themes@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ=="], - "@shikijs/transformers": ["@shikijs/transformers@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/types": "3.19.0" } }, "sha512-e6vwrsyw+wx4OkcrDbL+FVCxwx8jgKiCoXzakVur++mIWVcgpzIi8vxf4/b4dVTYrV/nUx5RjinMf4tq8YV8Fw=="], + "@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="], - "@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="], + "@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], @@ -1149,75 +1153,75 @@ "@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.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA=="], + "@smithy/abort-controller": ["@smithy/abort-controller@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-P7JD4J+wxHMpGxqIg6SHno2tPkZbBUBLbPpR5/T1DEUvw/mEaINBMaPFZNM7lA+ToSCZ36j6nMHa+5kej+fhGg=="], "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA=="], "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.1", "", { "dependencies": { "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ=="], - "@smithy/config-resolver": ["@smithy/config-resolver@4.4.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw=="], + "@smithy/config-resolver": ["@smithy/config-resolver@4.4.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-s3U5ChS21DwU54kMmZ0UJumoS5cg0+rGVZvN6f5Lp6EbAVi0ZyP+qDSHdewfmXKUgNK1j3z45JyzulkDukrjAA=="], - "@smithy/core": ["@smithy/core@3.18.7", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.6", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw=="], + "@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="], - "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ=="], + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA=="], - "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA=="], + "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.6", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OZfsI+YRG26XZik/jKMMg37acnBSbUiK/8nETW3uM3mLj+0tMmFXdHQw1e5WEd/IHN8BGOh3te91SNDe2o4RHg=="], - "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.5", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw=="], + "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-6OiaAaEbLB6dEkRbQyNzFSJv5HDvly3Mc6q/qcPd2uS/g3szR8wAIkh7UndAFKfMypNSTuZ6eCBmgCLR5LacTg=="], - "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ=="], + "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-xP5YXbOVRVN8A4pDnSUkEUsL9fYFU6VNhxo8tgr13YnMbf3Pn4xVr+hSyLVjS1Frfi1Uk03ET5Bwml4+0CeYEw=="], - "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.5", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg=="], + "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-jhH7nJuaOpnTFcuZpWK9dqb6Ge2yGi1okTo0W6wkJrfwAm2vwmO74tF1v07JmrSyHBcKLQATEexclJw9K1Vj7w=="], - "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.5", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q=="], + "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.6", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw=="], - "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg=="], + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], - "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.6", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.0", "@smithy/chunked-blob-reader-native": "^4.2.1", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw=="], + "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.7", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.0", "@smithy/chunked-blob-reader-native": "^4.2.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-CIbCTGGX5CI7tfewBPSYD9ycp2Vb2GW5xnXD1n7GcO9mu37EN7A6DvCHM9MX7pOeS1adMn5D+1yRwI3eABVbcA=="], - "@smithy/hash-node": ["@smithy/hash-node@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA=="], + "@smithy/hash-node": ["@smithy/hash-node@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k3Dy9VNR37wfMh2/1RHkFf/e0rMyN0pjY0FdyY6ItJRjENYyVPRMwad6ZR1S9HFm6tTuIOd9pqKBmtJ4VHxvxg=="], - "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q=="], + "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-+3T8LkH39YIhYHsv/Ec8lF+92nykZpU+XMBvAyXF/uLcTp86pxa5oSJk1vzaRY9N++qgDLYjzJ6OVbtAgDGwfw=="], - "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A=="], + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-E4t/V/q2T46RY21fpfznd1iSLTvCXKNKo4zJ1QuEFN4SE9gKfu2vb6bgq35LpufkQ+SETWIC7ZAf2GGvTlBaMQ=="], "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@smithy/md5-js": ["@smithy/md5-js@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg=="], + "@smithy/md5-js": ["@smithy/md5-js@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZXeh8UmH31JdcNsrQ1o9v1IVuva9JFwxIc6zTMxWX7wcmWvVR7Ai9aUEw5LraNKqdkAsb06clpM2sRH4Iy55Sg=="], - "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A=="], + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-0cjqjyfj+Gls30ntq45SsBtqF3dfJQCeqQPyGz58Pk8OgrAr5YiB7ZvDzjCA94p4r6DCI4qLm7FKobqBjf515w=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.14", "", { "dependencies": { "@smithy/core": "^3.18.7", "@smithy/middleware-serde": "^4.2.6", "@smithy/node-config-provider": "^4.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-middleware": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg=="], + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.14", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/service-error-classification": "^4.2.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q=="], + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.16", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/protocol-http": "^5.3.6", "@smithy/service-error-classification": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-retry": "^4.2.6", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-XPpNhNRzm3vhYm7YCsyw3AtmWggJbg1wNGAoqb7NBYr5XA5isMRv14jgbYyUV6IvbTBFZQdf2QpeW43LrRdStQ=="], - "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ=="], + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="], - "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ=="], + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="], - "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.5", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg=="], + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="], - "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.5", "", { "dependencies": { "@smithy/abort-controller": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw=="], + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.6", "", { "dependencies": { "@smithy/abort-controller": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Gsb9jf4ido5BhPfani4ggyrKDd3ZK+vTFWmUaZeFg5G3E5nhFmqiTzAIbHqmPs1sARuJawDiGMGR/nY+Gw6+aQ=="], - "@smithy/property-provider": ["@smithy/property-provider@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg=="], + "@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - "@smithy/protocol-http": ["@smithy/protocol-http@5.3.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ=="], + "@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], - "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg=="], + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-MeM9fTAiD3HvoInK/aA8mgJaKQDvm8N0dKy6EiFaCfgpovQr4CaOkJC28XqlSRABM+sHdSQXbC8NZ0DShBMHqg=="], - "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ=="], + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="], - "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0" } }, "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ=="], + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="], - "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA=="], + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], - "@smithy/signature-v4": ["@smithy/signature-v4@5.3.5", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w=="], + "@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="], - "@smithy/smithy-client": ["@smithy/smithy-client@4.9.10", "", { "dependencies": { "@smithy/core": "^3.18.7", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-stack": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ=="], + "@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="], - "@smithy/types": ["@smithy/types@4.9.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA=="], + "@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], - "@smithy/url-parser": ["@smithy/url-parser@4.2.5", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ=="], + "@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="], "@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], @@ -1229,31 +1233,31 @@ "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.13", "", { "dependencies": { "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA=="], + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.15", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-LiZQVAg/oO8kueX4c+oMls5njaD2cRLXRfcjlTYjhIqmwHnCwkQO5B3dMQH0c5PACILxGAQf6Mxsq7CjlDc76A=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.16", "", { "dependencies": { "@smithy/config-resolver": "^4.4.3", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg=="], + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.18", "", { "dependencies": { "@smithy/config-resolver": "^4.4.4", "@smithy/credential-provider-imds": "^4.2.6", "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Kw2J+KzYm9C9Z9nY6+W0tEnoZOofstVCMTshli9jhQbQCy64rueGfKzPfuFBnVUqZD9JobxTh2DzHmPkp/Va/Q=="], - "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A=="], + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-v60VNM2+mPvgHCBXEfMCYrQ0RepP6u6xvbAkMenfe4Mi872CqNkJzgcnQL837e8NdeDxBgrWQRTluKq5Lqdhfg=="], "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], - "@smithy/util-middleware": ["@smithy/util-middleware@4.2.5", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA=="], + "@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="], - "@smithy/util-retry": ["@smithy/util-retry@4.2.5", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg=="], + "@smithy/util-retry": ["@smithy/util-retry@4.2.6", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-x7CeDQLPQ9cb6xN7fRJEjlP9NyGW/YeXWc4j/RUhg4I+H60F0PEeRc2c/z3rm9zmsdiMFzpV/rT+4UHW6KM1SA=="], - "@smithy/util-stream": ["@smithy/util-stream@4.5.6", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ=="], + "@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], "@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - "@smithy/util-waiter": ["@smithy/util-waiter@4.2.5", "", { "dependencies": { "@smithy/abort-controller": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g=="], + "@smithy/util-waiter": ["@smithy/util-waiter@4.2.6", "", { "dependencies": { "@smithy/abort-controller": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-xU9HwUSik9UUCJmm530yvBy0AwlQFICveKmqvaaTukKkXEAhyiBdHtSrhPrH3rH+uz0ykyaE3LdgsX86C6mDCQ=="], "@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], "@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="], - "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], @@ -1263,39 +1267,39 @@ "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.4", "", { "dependencies": { "@t3-oss/env-core": "0.13.4" }, "peerDependencies": { "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["typescript", "valibot", "zod"] }, "sha512-6ecXR7SH7zJKVcBODIkB7wV9QLMU23uV8D9ec6P+ULHJ5Ea/YXEHo+Z/2hSYip5i9ptD/qZh8VuOXyldspvTTg=="], - "@tabler/icons": ["@tabler/icons@3.35.0", "", {}, "sha512-yYXe+gJ56xlZFiXwV9zVoe3FWCGuZ/D7/G4ZIlDtGxSx5CGQK110wrnT29gUj52kEZoxqF7oURTk97GQxELOFQ=="], + "@tabler/icons": ["@tabler/icons@3.36.0", "", {}, "sha512-z9OfTEG6QbaQWM9KBOxxUdpgvMUn0atageXyiaSc2gmYm51ORO8Ua7eUcjlks+Dc0YMK4rrodAFdK9SfjJ4ZcA=="], - "@tabler/icons-react": ["@tabler/icons-react@3.35.0", "", { "dependencies": { "@tabler/icons": "3.35.0" }, "peerDependencies": { "react": ">= 16" } }, "sha512-XG7t2DYf3DyHT5jxFNp5xyLVbL4hMJYJhiSdHADzAjLRYfL7AnjlRfiHDHeXxkb2N103rEIvTsBRazxXtAUz2g=="], + "@tabler/icons-react": ["@tabler/icons-react@3.36.0", "", { "dependencies": { "@tabler/icons": "3.36.0" }, "peerDependencies": { "react": ">= 16" } }, "sha512-sSZ00bEjTdTTskVFykq294RJq+9cFatwy4uYa78HcYBCXU1kSD1DIp5yoFsQXmybkIOKCjp18OnhAYk553UIfQ=="], - "@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="], + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], - "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", "tailwindcss": "4.1.17" } }, "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw=="], + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="], "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], @@ -1427,7 +1431,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@22.19.2", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw=="], + "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], @@ -1495,7 +1499,7 @@ "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - "@webgpu/types": ["@webgpu/types@0.1.67", "", {}, "sha512-uk53+2ECGUkWoDFez/hymwpRfdgdIn6y1ref70fEecGMe5607f4sozNFgBk0oxlr7j2CRGWBEc3IBYMmFdGGTQ=="], + "@webgpu/types": ["@webgpu/types@0.1.68", "", {}, "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA=="], "@xmldom/is-dom-node": ["@xmldom/is-dom-node@1.0.1", "", {}, "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q=="], @@ -1517,11 +1521,11 @@ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], - "ai": ["ai@5.0.110", "", { "dependencies": { "@ai-sdk/gateway": "2.0.19", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZBq+5bvef4e5qoIG4U6NJ1UpCPWGjuaWERHXbHu2T2ND3c02nJ2zlnjm+N6zAAplQPxwqm7Sb16mrRX5uQNWtQ=="], + "ai": ["ai@5.0.113", "", { "dependencies": { "@ai-sdk/gateway": "2.0.21", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-26vivpSO/mzZj0k1Si2IpsFspp26ttQICHRySQiMrtWcRd5mnJMX2a8sG28vmZ38C+JUn1cWmfZrsLMxkSMw9g=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - "ansi-color": ["ansi-color@0.2.1", "", {}, "sha512-bF6xLaZBLpOQzgYUtYEhJx090nPSZk1BQ/q2oyBK9aMMcJHzx9uXGCjI2Y+LebsN4Jwoykr0V9whbPiogdyHoQ=="], + "ansi-color": ["ansi-color@0.2.2", "", {}, "sha512-qPx7iZZDHITYrrfzaUFXQpIcF2xYifcQHQflP1pFz8yY3lfU6GgCHb0+hJD7nimYKO7f2iaYYwBpZ+GaNcAhcA=="], "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], @@ -1541,9 +1545,9 @@ "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], - "arkregex": ["arkregex@0.0.4", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-biS/FkvSwQq59TZ453piUp8bxMui11pgOMV9WHAnli1F8o0ayNCZzUwQadL/bGIUic5TkS/QlPcyMuI8ZIwedQ=="], + "arkregex": ["arkregex@0.0.5", "", { "dependencies": { "@ark/util": "0.56.0" } }, "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw=="], - "arktype": ["arktype@2.1.28", "", { "dependencies": { "@ark/schema": "0.56.0", "@ark/util": "0.56.0", "arkregex": "0.0.4" } }, "sha512-LVZqXl2zWRpNFnbITrtFmqeqNkPPo+KemuzbGSY6jvJwCb4v8NsDzrWOLHnQgWl26TkJeWWcUNUeBpq2Mst1/Q=="], + "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=="], @@ -1593,7 +1597,7 @@ "base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.6", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.7", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg=="], "basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="], @@ -1953,7 +1957,7 @@ "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], - "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -2061,7 +2065,7 @@ "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="], - "fast-xml-parser": ["fast-xml-parser@5.3.2", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-n8v8b6p4Z1sMgqRmqLJm3awW4NX7NkaKPfb3uJIBTSH7Pdvufi3PQ3/lJLQrvxcMYl7JI2jnDO90siPEpD8JBA=="], + "fast-xml-parser": ["fast-xml-parser@5.3.3", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA=="], "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], @@ -2071,7 +2075,7 @@ "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], - "fetch-cookie": ["fetch-cookie@3.1.0", "", { "dependencies": { "set-cookie-parser": "^2.4.8", "tough-cookie": "^5.0.0" } }, "sha512-s/XhhreJpqH0ftkGVcQt8JE9bqk+zRn4jF5mPJXWZeQMCI5odV9K+wEWYbnzFPHgQZlvPSMjS4n4yawWE8RINw=="], + "fetch-cookie": ["fetch-cookie@3.2.0", "", { "dependencies": { "set-cookie-parser": "^2.4.8", "tough-cookie": "^6.0.0" } }, "sha512-n61pQIxP25C6DRhcJxn7BDzgHP/+S56Urowb5WFxtcRMpU6drqXD90xjyAsVQYsNSNNVbaCcYY1DuHsdkZLuiA=="], "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], @@ -2363,9 +2367,9 @@ "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], - "kysely": ["kysely@0.28.8", "", {}, "sha512-QUOgl5ZrS9IRuhq5FvOKFSsD/3+IA6MLE81/bOOTRA/YQpKDza2sFdN5g6JCB9BOpqMJDGefLCQ9F12hRS13TA=="], + "kysely": ["kysely@0.28.9", "", {}, "sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA=="], - "langsmith": ["langsmith@0.3.85", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-Txuaxnpcra57qld4+hkqHhd9L2D6G6kEAReWXdr/sddxPu6ycBHXStqDciituC642lJmzPdarrYtll2vSwMbnQ=="], + "langsmith": ["langsmith@0.3.87", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q=="], "leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="], @@ -2639,7 +2643,7 @@ "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - "named-placeholders": ["named-placeholders@1.1.4", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-/qfG0Kk/bLJIvej4FcPQ2KYUJP8iQdU1CTxysNb/U2wUNb+/4K485yeio8iNoiwfqJnsTInXoRPTza0dZWHVJQ=="], + "named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="], "nan": ["nan@2.24.0", "", {}, "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg=="], @@ -2661,7 +2665,7 @@ "netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], - "next": ["next@16.0.9", "", { "dependencies": { "@next/env": "16.0.9", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.9", "@next/swc-darwin-x64": "16.0.9", "@next/swc-linux-arm64-gnu": "16.0.9", "@next/swc-linux-arm64-musl": "16.0.9", "@next/swc-linux-x64-gnu": "16.0.9", "@next/swc-linux-x64-musl": "16.0.9", "@next/swc-win32-arm64-msvc": "16.0.9", "@next/swc-win32-x64-msvc": "16.0.9", "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-Xk5x/wEk6ADIAtQECLo1uyE5OagbQCiZ+gW4XEv24FjQ3O2PdSkvgsn22aaseSXC7xg84oONvQjFbSTX5YsMhQ=="], + "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=="], @@ -3015,7 +3019,7 @@ "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], - "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], + "rollup": ["rollup@4.53.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.4", "@rollup/rollup-android-arm64": "4.53.4", "@rollup/rollup-darwin-arm64": "4.53.4", "@rollup/rollup-darwin-x64": "4.53.4", "@rollup/rollup-freebsd-arm64": "4.53.4", "@rollup/rollup-freebsd-x64": "4.53.4", "@rollup/rollup-linux-arm-gnueabihf": "4.53.4", "@rollup/rollup-linux-arm-musleabihf": "4.53.4", "@rollup/rollup-linux-arm64-gnu": "4.53.4", "@rollup/rollup-linux-arm64-musl": "4.53.4", "@rollup/rollup-linux-loong64-gnu": "4.53.4", "@rollup/rollup-linux-ppc64-gnu": "4.53.4", "@rollup/rollup-linux-riscv64-gnu": "4.53.4", "@rollup/rollup-linux-riscv64-musl": "4.53.4", "@rollup/rollup-linux-s390x-gnu": "4.53.4", "@rollup/rollup-linux-x64-gnu": "4.53.4", "@rollup/rollup-linux-x64-musl": "4.53.4", "@rollup/rollup-openharmony-arm64": "4.53.4", "@rollup/rollup-win32-arm64-msvc": "4.53.4", "@rollup/rollup-win32-ia32-msvc": "4.53.4", "@rollup/rollup-win32-x64-gnu": "4.53.4", "@rollup/rollup-win32-x64-msvc": "4.53.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-YpXaaArg0MvrnJpvduEDYIp7uGOqKXbH9NsHGQ6SxKCOsNAjZF018MmxefFUulVP2KLtiGw1UvZbr+/ekjvlDg=="], "rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="], @@ -3061,11 +3065,11 @@ "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], "seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="], - "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], @@ -3081,7 +3085,7 @@ "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], - "shiki": ["shiki@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/engine-oniguruma": "3.19.0", "@shikijs/langs": "3.19.0", "@shikijs/themes": "3.19.0", "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-77VJr3OR/VUZzPiStyRhADmO2jApMM0V2b1qf0RpfWya8Zr1PeZev5AEpPGAAKWdiYUtcZGBE4F5QvJml1PvWA=="], + "shiki": ["shiki@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/engine-javascript": "3.20.0", "@shikijs/engine-oniguruma": "3.20.0", "@shikijs/langs": "3.20.0", "@shikijs/themes": "3.20.0", "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg=="], "shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="], @@ -3197,7 +3201,7 @@ "stripe": ["stripe@18.5.0", "", { "dependencies": { "qs": "^6.11.0" }, "peerDependencies": { "@types/node": ">=12.x.x" }, "optionalPeers": ["@types/node"] }, "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA=="], - "strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + "strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], "strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="], @@ -3219,7 +3223,7 @@ "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], - "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], + "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="], @@ -3241,7 +3245,7 @@ "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], - "thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="], + "thread-stream": ["thread-stream@4.0.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA=="], "three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="], @@ -3393,7 +3397,7 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@7.2.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ=="], + "vite": ["vite@7.3.0", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg=="], "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], @@ -3503,7 +3507,7 @@ "@aws-sdk/client-s3/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.948.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-ini": "3.948.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.948.0", "@aws-sdk/credential-provider-web-identity": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-ep5vRLnrRdcsP17Ef31sNN4g8Nqk/4JBydcUJuFRbGuyQtrZZrVT81UeH2xhz6d0BK6ejafDB9+ZpBjXuWT5/Q=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.952.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-ini": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pj7nidLrb3Dz9llcUPh6N0Yv1dBYTS9xJqi8u0kI8D5sn72HJMB+fIOhcDQVXXAw/dpVolOAH9FOAbog5JDAMg=="], "@aws-sdk/client-s3/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.948.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ=="], @@ -3513,7 +3517,7 @@ "@aws-sdk/client-sesv2/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.948.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-ini": "3.948.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.948.0", "@aws-sdk/credential-provider-web-identity": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-ep5vRLnrRdcsP17Ef31sNN4g8Nqk/4JBydcUJuFRbGuyQtrZZrVT81UeH2xhz6d0BK6ejafDB9+ZpBjXuWT5/Q=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.952.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-ini": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pj7nidLrb3Dz9llcUPh6N0Yv1dBYTS9xJqi8u0kI8D5sn72HJMB+fIOhcDQVXXAw/dpVolOAH9FOAbog5JDAMg=="], "@aws-sdk/client-sesv2/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.948.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ=="], @@ -3547,9 +3551,9 @@ "@better-auth/sso/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], - "@better-auth/sso/zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], + "@better-auth/sso/zod": ["zod@4.2.0", "", {}, "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw=="], - "@better-auth/stripe/zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], + "@better-auth/stripe/zod": ["zod@4.2.0", "", {}, "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw=="], "@browserbasehq/sdk/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], @@ -3561,7 +3565,7 @@ "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], - "@inquirer/external-editor/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], + "@inquirer/external-editor/iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="], "@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=="], @@ -3769,18 +3773,20 @@ "better-auth/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], - "better-auth/zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], + "better-auth/zod": ["zod@4.2.0", "", {}, "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw=="], "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - "body-parser/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], + "body-parser/iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="], "c12/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "c12/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + "c12/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "c12/pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], "cheerio/htmlparser2": ["htmlparser2@10.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.1", "entities": "^6.0.0" } }, "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g=="], @@ -3829,6 +3835,8 @@ "fetch-blob/web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "fetch-cookie/tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], + "figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -3837,7 +3845,7 @@ "fumadocs-mdx/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "fumadocs-mdx/zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], + "fumadocs-mdx/zod": ["zod@4.2.0", "", {}, "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw=="], "fumadocs-ui/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], @@ -3923,7 +3931,7 @@ "oauth2-mock-server/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], - "ollama-ai-provider-v2/zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], + "ollama-ai-provider-v2/zod": ["zod@4.2.0", "", {}, "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw=="], "openai/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], @@ -3947,6 +3955,8 @@ "path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + "pino/thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="], + "pino-pretty/pino-abstract-transport": ["pino-abstract-transport@3.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg=="], "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], @@ -3965,7 +3975,7 @@ "puppeteer-core/devtools-protocol": ["devtools-protocol@0.0.1312386", "", {}, "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA=="], - "raw-body/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], + "raw-body/iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="], "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], @@ -3999,11 +4009,11 @@ "sim/tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="], - "simstudio/@types/node": ["@types/node@20.19.26", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg=="], + "simstudio/@types/node": ["@types/node@20.19.27", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug=="], "simstudio/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "simstudio-ts-sdk/@types/node": ["@types/node@20.19.26", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg=="], + "simstudio-ts-sdk/@types/node": ["@types/node@20.19.27", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug=="], "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -4041,8 +4051,6 @@ "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], - "tunnel-agent/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "unbzip2-stream/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "unicode-trie/pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="], @@ -4053,6 +4061,8 @@ "unist-util-remove/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="], + "vite/esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="], + "vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], "xml-crypto/xpath": ["xpath@0.0.33", "", {}, "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA=="], @@ -4073,25 +4083,25 @@ "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-login": "3.948.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.948.0", "@aws-sdk/credential-provider-web-identity": "3.948.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Cl//Qh88e8HBL7yYkJNpF5eq76IO6rq8GsatKcfVBm7RFVxCqYEPSSBtkHdbtNwQdRQqAMXc6E/lEB/CZUDxnA=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-login": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-N5B15SwzMkZ8/LLopNksTlPEWWZn5tbafZAUfMY5Xde4rSHGWmv5H/ws2M3P8L0X77E2wKnOJsNmu+GsArBreQ=="], "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.948.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.948.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/token-providers": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gqLhX1L+zb/ZDnnYbILQqJ46j735StfWV5PbDjxRzBKS7GzsiYoaf6MyHseEopmWrez5zl5l6aWzig7UpzSeQQ=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.952.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.948.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/token-providers": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-1CQdP5RzxeXuEfytbAD5TgreY1c9OacjtCdO8+n9m05tpzBABoNBof0hcjzw1dtrWFH7deyUgfwCl1TAN3yBWQ=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-MvYQlXVoJyfF3/SmnNzOVEtANRAiJIObEUYYyjTqKZTmcRIVVky0tPuG26XnB8LmTYgtESwJIZJj/Eyyc9WURQ=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5hJbfaZdHDAP8JlwplNbXJAat9Vv7L0AbTZzkbPIgjHhC3vrMf5r3a6I1HWFp5i5pXo7J45xyuf5uQGZJxJlCg=="], "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA=="], "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-login": "3.948.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.948.0", "@aws-sdk/credential-provider-web-identity": "3.948.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Cl//Qh88e8HBL7yYkJNpF5eq76IO6rq8GsatKcfVBm7RFVxCqYEPSSBtkHdbtNwQdRQqAMXc6E/lEB/CZUDxnA=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-login": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-N5B15SwzMkZ8/LLopNksTlPEWWZn5tbafZAUfMY5Xde4rSHGWmv5H/ws2M3P8L0X77E2wKnOJsNmu+GsArBreQ=="], "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.948.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.948.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/token-providers": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gqLhX1L+zb/ZDnnYbILQqJ46j735StfWV5PbDjxRzBKS7GzsiYoaf6MyHseEopmWrez5zl5l6aWzig7UpzSeQQ=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.952.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.948.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/token-providers": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-1CQdP5RzxeXuEfytbAD5TgreY1c9OacjtCdO8+n9m05tpzBABoNBof0hcjzw1dtrWFH7deyUgfwCl1TAN3yBWQ=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-MvYQlXVoJyfF3/SmnNzOVEtANRAiJIObEUYYyjTqKZTmcRIVVky0tPuG26XnB8LmTYgtESwJIZJj/Eyyc9WURQ=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5hJbfaZdHDAP8JlwplNbXJAat9Vv7L0AbTZzkbPIgjHhC3vrMf5r3a6I1HWFp5i5pXo7J45xyuf5uQGZJxJlCg=="], "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA=="], @@ -4243,6 +4253,8 @@ "engine.io/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], + "fetch-cookie/tough-cookie/tldts": ["tldts@7.0.19", "", { "dependencies": { "tldts-core": "^7.0.19" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA=="], + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "fumadocs-mdx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], @@ -4405,9 +4417,9 @@ "oauth2-mock-server/express/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "oauth2-mock-server/express/send": ["send@0.19.1", "", { "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.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg=="], + "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.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], + "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=="], @@ -4445,6 +4457,58 @@ "test-exclude/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], + + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], + + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="], + + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="], + + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="], + + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="], + + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="], + + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="], + + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="], + + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="], + + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="], + + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="], + + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="], + + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="], + + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="], + + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="], + + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="], + + "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="], + + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="], + + "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="], + + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="], + + "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="], + + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="], + + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="], + + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="], + + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="], + "@anthropic-ai/sdk/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "@anthropic-ai/sdk/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -4455,25 +4519,25 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gcKO2b6eeTuZGp3Vvgr/9OxajMrD3W+FZ2FCyJox363ZgMoYJsyNid1vuZrEuAGkx0jvveLXfwiVS0UXyPkgtw=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-jL9zc+e+7sZeJrHzYKK9GOjl1Ktinh0ORU3cM2uRBi7fuH/0zV9pdMN8PQnGXz0i4tJaKcZ1lrE4V0V6LB9NQg=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="], "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-V487/kM4Teq5dcr1t5K6eoUKuqlGr9FRWL3MIMukMERJXHZvio6kox60FZ/YtciRHRI75u14YUqm2Dzddcu3+A=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-IpQVC9WOeXQlCEcFVNXWDIKy92CH1Az37u9K0H3DF/HT56AjhyDVKQQfHUy00nt7bHFe3u0K5+zlwErBeKy5ZA=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-gcKO2b6eeTuZGp3Vvgr/9OxajMrD3W+FZ2FCyJox363ZgMoYJsyNid1vuZrEuAGkx0jvveLXfwiVS0UXyPkgtw=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-jL9zc+e+7sZeJrHzYKK9GOjl1Ktinh0ORU3cM2uRBi7fuH/0zV9pdMN8PQnGXz0i4tJaKcZ1lrE4V0V6LB9NQg=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="], "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.948.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-V487/kM4Teq5dcr1t5K6eoUKuqlGr9FRWL3MIMukMERJXHZvio6kox60FZ/YtciRHRI75u14YUqm2Dzddcu3+A=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-IpQVC9WOeXQlCEcFVNXWDIKy92CH1Az37u9K0H3DF/HT56AjhyDVKQQfHUy00nt7bHFe3u0K5+zlwErBeKy5ZA=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="], "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-u7M3hazcB7aJiVwosNdJRbIJDzbwQ861NTtl6S0HmvWpixaVb7iyhJZWg8/plyUznboZGBm7JVEdxtxv3u0bTA=="], @@ -4509,6 +4573,8 @@ "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "fetch-cookie/tough-cookie/tldts/tldts-core": ["tldts-core@7.0.19", "", {}, "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A=="], + "groq-sdk/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "groq-sdk/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -4549,12 +4615,6 @@ "oauth2-mock-server/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "oauth2-mock-server/express/send/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], - - "oauth2-mock-server/express/send/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], - - "oauth2-mock-server/express/serve-static/send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], - "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=="], @@ -4573,9 +4633,9 @@ "test-exclude/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw=="], + "@aws-sdk/client-s3/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="], - "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw=="], + "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="], "@aws-sdk/client-sqs/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.947.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.936.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw=="], @@ -4595,12 +4655,6 @@ "log-update/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "oauth2-mock-server/express/serve-static/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], - - "oauth2-mock-server/express/serve-static/send/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], - - "oauth2-mock-server/express/serve-static/send/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], - "oauth2-mock-server/express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "sim/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], diff --git a/package.json b/package.json index 5c23d50b5..4e0b8b9f4 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,8 @@ "overrides": { "react": "19.2.1", "react-dom": "19.2.1", - "next": "16.0.9", - "@next/env": "16.0.9", + "next": "16.1.0-canary.21", + "@next/env": "16.1.0-canary.21", "drizzle-orm": "^0.44.5", "postgres": "^3.4.5" }, @@ -72,6 +72,8 @@ ] }, "trustedDependencies": [ - "ffmpeg-static" + "ffmpeg-static", + "isolated-vm", + "sharp" ] } From bdcc42e566742537237ec90a41b19c9399091bdd Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 17:14:33 -0800 Subject: [PATCH 09/17] fix(permissions): add client-side hints to prevent read-only users from creating workflows or folders (#2390) --- .../[workspaceId]/w/components/sidebar/sidebar.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index dbe51643b..b6e15b8b9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -9,6 +9,7 @@ import { useSession } from '@/lib/auth/auth-client' import { getEnv, isTruthy } from '@/lib/core/config/env' import { createLogger } from '@/lib/logs/console/logger' import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { createCommands } from '@/app/workspace/[workspaceId]/utils/commands-utils' import { HelpModal, @@ -65,6 +66,7 @@ export function Sidebar() { const scrollContainerRef = useRef(null) const { data: sessionData, isPending: sessionLoading } = useSession() + const { canEdit } = useUserPermissionsContext() /** * Sidebar state from store with hydration tracking to prevent SSR mismatch. @@ -516,7 +518,7 @@ export function Sidebar() { variant='ghost' className='translate-y-[-0.25px] p-[1px]' onClick={handleImportWorkflow} - disabled={isImporting} + disabled={isImporting || !canEdit} > @@ -531,7 +533,7 @@ export function Sidebar() { variant='ghost' className='mr-[1px] translate-y-[-0.25px] p-[1px]' onClick={handleCreateFolder} - disabled={isCreatingFolder} + disabled={isCreatingFolder || !canEdit} > @@ -546,7 +548,7 @@ export function Sidebar() { variant='outline' className='translate-y-[-0.25px] p-[1px]' onClick={handleCreateWorkflow} - disabled={isCreatingWorkflow} + disabled={isCreatingWorkflow || !canEdit} > From 300aaa5368ad91fcd45ff6c5648914894e222c85 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 15 Dec 2025 17:39:53 -0800 Subject: [PATCH 10/17] feat(slack): ability to have DM channels as destination for slack tools (#2388) * feat(slack): tool to allow dms * don't make new tool but separate out destination * add log for message limit * consolidate slack selector code * add scopes correctly * fix zod validation * update message logs * add console logs * fix * remove from tools where feature not needed * add correct condition * fix type * fix cond eval logic --- .../app/api/tools/slack/add-reaction/route.ts | 31 +- .../api/tools/slack/delete-message/route.ts | 29 +- .../api/tools/slack/read-messages/route.ts | 207 +++++++++++++ .../app/api/tools/slack/send-message/route.ts | 243 ++------------- .../api/tools/slack/update-message/route.ts | 2 +- apps/sim/app/api/tools/slack/users/route.ts | 113 +++++++ apps/sim/app/api/tools/slack/utils.ts | 288 ++++++++++++++++++ .../components/oauth-required-modal.tsx | 3 + .../components/sub-block/components/index.ts | 2 +- .../slack-selector-input.tsx} | 59 ++-- .../components/tool-input/tool-input.tsx | 6 +- .../editor/components/sub-block/sub-block.tsx | 5 +- apps/sim/blocks/blocks/slack.ts | 82 ++++- apps/sim/blocks/types.ts | 2 + apps/sim/hooks/selectors/registry.ts | 25 ++ apps/sim/hooks/selectors/resolution.ts | 17 ++ apps/sim/hooks/selectors/types.ts | 1 + apps/sim/lib/auth/auth.ts | 3 + apps/sim/lib/oauth/oauth.ts | 3 + apps/sim/serializer/index.ts | 120 ++++---- apps/sim/tools/slack/message.ts | 11 +- apps/sim/tools/slack/message_reader.ts | 113 ++----- apps/sim/tools/slack/types.ts | 6 +- 23 files changed, 911 insertions(+), 460 deletions(-) create mode 100644 apps/sim/app/api/tools/slack/read-messages/route.ts create mode 100644 apps/sim/app/api/tools/slack/users/route.ts create mode 100644 apps/sim/app/api/tools/slack/utils.ts rename apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/{channel-selector/channel-selector-input.tsx => slack-selector/slack-selector-input.tsx} (74%) diff --git a/apps/sim/app/api/tools/slack/add-reaction/route.ts b/apps/sim/app/api/tools/slack/add-reaction/route.ts index f6fba4a90..79a48008b 100644 --- a/apps/sim/app/api/tools/slack/add-reaction/route.ts +++ b/apps/sim/app/api/tools/slack/add-reaction/route.ts @@ -1,28 +1,21 @@ import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' -import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' -const logger = createLogger('SlackAddReactionAPI') - const SlackAddReactionSchema = z.object({ accessToken: z.string().min(1, 'Access token is required'), - channel: z.string().min(1, 'Channel ID is required'), + channel: z.string().min(1, 'Channel is required'), timestamp: z.string().min(1, 'Message timestamp is required'), name: z.string().min(1, 'Emoji name is required'), }) export async function POST(request: NextRequest) { - const requestId = generateRequestId() - try { const authResult = await checkHybridAuth(request, { requireWorkflowId: false }) if (!authResult.success) { - logger.warn(`[${requestId}] Unauthorized Slack add reaction attempt: ${authResult.error}`) return NextResponse.json( { success: false, @@ -32,22 +25,9 @@ export async function POST(request: NextRequest) { ) } - logger.info( - `[${requestId}] Authenticated Slack add reaction request via ${authResult.authType}`, - { - userId: authResult.userId, - } - ) - const body = await request.json() const validatedData = SlackAddReactionSchema.parse(body) - logger.info(`[${requestId}] Adding Slack reaction`, { - channel: validatedData.channel, - timestamp: validatedData.timestamp, - emoji: validatedData.name, - }) - const slackResponse = await fetch('https://slack.com/api/reactions.add', { method: 'POST', headers: { @@ -64,7 +44,6 @@ export async function POST(request: NextRequest) { const data = await slackResponse.json() if (!data.ok) { - logger.error(`[${requestId}] Slack API error:`, data) return NextResponse.json( { success: false, @@ -74,12 +53,6 @@ export async function POST(request: NextRequest) { ) } - logger.info(`[${requestId}] Reaction added successfully`, { - channel: validatedData.channel, - timestamp: validatedData.timestamp, - reaction: validatedData.name, - }) - return NextResponse.json({ success: true, output: { @@ -93,7 +66,6 @@ export async function POST(request: NextRequest) { }) } catch (error) { if (error instanceof z.ZodError) { - logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors }) return NextResponse.json( { success: false, @@ -104,7 +76,6 @@ export async function POST(request: NextRequest) { ) } - logger.error(`[${requestId}] Error adding Slack reaction:`, error) return NextResponse.json( { success: false, diff --git a/apps/sim/app/api/tools/slack/delete-message/route.ts b/apps/sim/app/api/tools/slack/delete-message/route.ts index 02116bec5..25cea4c01 100644 --- a/apps/sim/app/api/tools/slack/delete-message/route.ts +++ b/apps/sim/app/api/tools/slack/delete-message/route.ts @@ -1,27 +1,20 @@ import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' -import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' -const logger = createLogger('SlackDeleteMessageAPI') - const SlackDeleteMessageSchema = z.object({ accessToken: z.string().min(1, 'Access token is required'), - channel: z.string().min(1, 'Channel ID is required'), + channel: z.string().min(1, 'Channel is required'), timestamp: z.string().min(1, 'Message timestamp is required'), }) export async function POST(request: NextRequest) { - const requestId = generateRequestId() - try { const authResult = await checkHybridAuth(request, { requireWorkflowId: false }) if (!authResult.success) { - logger.warn(`[${requestId}] Unauthorized Slack delete message attempt: ${authResult.error}`) return NextResponse.json( { success: false, @@ -31,21 +24,9 @@ export async function POST(request: NextRequest) { ) } - logger.info( - `[${requestId}] Authenticated Slack delete message request via ${authResult.authType}`, - { - userId: authResult.userId, - } - ) - const body = await request.json() const validatedData = SlackDeleteMessageSchema.parse(body) - logger.info(`[${requestId}] Deleting Slack message`, { - channel: validatedData.channel, - timestamp: validatedData.timestamp, - }) - const slackResponse = await fetch('https://slack.com/api/chat.delete', { method: 'POST', headers: { @@ -61,7 +42,6 @@ export async function POST(request: NextRequest) { const data = await slackResponse.json() if (!data.ok) { - logger.error(`[${requestId}] Slack API error:`, data) return NextResponse.json( { success: false, @@ -71,11 +51,6 @@ export async function POST(request: NextRequest) { ) } - logger.info(`[${requestId}] Message deleted successfully`, { - channel: data.channel, - timestamp: data.ts, - }) - return NextResponse.json({ success: true, output: { @@ -88,7 +63,6 @@ export async function POST(request: NextRequest) { }) } catch (error) { if (error instanceof z.ZodError) { - logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors }) return NextResponse.json( { success: false, @@ -99,7 +73,6 @@ export async function POST(request: NextRequest) { ) } - logger.error(`[${requestId}] Error deleting Slack message:`, error) return NextResponse.json( { success: false, diff --git a/apps/sim/app/api/tools/slack/read-messages/route.ts b/apps/sim/app/api/tools/slack/read-messages/route.ts new file mode 100644 index 000000000..74d9d9742 --- /dev/null +++ b/apps/sim/app/api/tools/slack/read-messages/route.ts @@ -0,0 +1,207 @@ +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { checkHybridAuth } from '@/lib/auth/hybrid' +import { generateRequestId } from '@/lib/core/utils/request' +import { createLogger } from '@/lib/logs/console/logger' +import { openDMChannel } from '../utils' + +export const dynamic = 'force-dynamic' + +const logger = createLogger('SlackReadMessagesAPI') + +const SlackReadMessagesSchema = z + .object({ + accessToken: z.string().min(1, 'Access token is required'), + channel: z.string().optional().nullable(), + userId: z.string().optional().nullable(), + limit: z.number().optional().nullable(), + oldest: z.string().optional().nullable(), + latest: z.string().optional().nullable(), + }) + .refine((data) => data.channel || data.userId, { + message: 'Either channel or userId is required', + }) + +export async function POST(request: NextRequest) { + const requestId = generateRequestId() + + try { + const authResult = await checkHybridAuth(request, { requireWorkflowId: false }) + + if (!authResult.success) { + logger.warn(`[${requestId}] Unauthorized Slack read messages attempt: ${authResult.error}`) + return NextResponse.json( + { + success: false, + error: authResult.error || 'Authentication required', + }, + { status: 401 } + ) + } + + logger.info( + `[${requestId}] Authenticated Slack read messages request via ${authResult.authType}`, + { + userId: authResult.userId, + } + ) + + const body = await request.json() + const validatedData = SlackReadMessagesSchema.parse(body) + + let channel = validatedData.channel + if (!channel && validatedData.userId) { + logger.info(`[${requestId}] Opening DM channel for user: ${validatedData.userId}`) + channel = await openDMChannel( + validatedData.accessToken, + validatedData.userId, + requestId, + logger + ) + } + + const url = new URL('https://slack.com/api/conversations.history') + url.searchParams.append('channel', channel!) + const limit = validatedData.limit ? Number(validatedData.limit) : 10 + url.searchParams.append('limit', String(Math.min(limit, 15))) + + if (validatedData.oldest) { + url.searchParams.append('oldest', validatedData.oldest) + } + if (validatedData.latest) { + url.searchParams.append('latest', validatedData.latest) + } + + logger.info(`[${requestId}] Reading Slack messages`, { + channel, + limit, + }) + + const slackResponse = await fetch(url.toString(), { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${validatedData.accessToken}`, + }, + }) + + const data = await slackResponse.json() + + if (!data.ok) { + logger.error(`[${requestId}] Slack API error:`, data) + + if (data.error === 'not_in_channel') { + return NextResponse.json( + { + success: false, + error: + 'Bot is not in the channel. Please invite the Sim bot to your Slack channel by typing: /invite @Sim Studio', + }, + { status: 400 } + ) + } + if (data.error === 'channel_not_found') { + return NextResponse.json( + { + success: false, + error: 'Channel not found. Please check the channel ID and try again.', + }, + { status: 400 } + ) + } + if (data.error === 'missing_scope') { + return NextResponse.json( + { + success: false, + error: + 'Missing required permissions. Please reconnect your Slack account with the necessary scopes (channels:history, groups:history, im:history).', + }, + { status: 400 } + ) + } + + return NextResponse.json( + { + success: false, + error: data.error || 'Failed to fetch messages', + }, + { status: 400 } + ) + } + + const messages = (data.messages || []).map((message: any) => ({ + type: message.type || 'message', + ts: message.ts, + text: message.text || '', + user: message.user, + bot_id: message.bot_id, + username: message.username, + channel: message.channel, + team: message.team, + thread_ts: message.thread_ts, + parent_user_id: message.parent_user_id, + reply_count: message.reply_count, + reply_users_count: message.reply_users_count, + latest_reply: message.latest_reply, + subscribed: message.subscribed, + last_read: message.last_read, + unread_count: message.unread_count, + subtype: message.subtype, + reactions: message.reactions?.map((reaction: any) => ({ + name: reaction.name, + count: reaction.count, + users: reaction.users || [], + })), + is_starred: message.is_starred, + pinned_to: message.pinned_to, + files: message.files?.map((file: any) => ({ + id: file.id, + name: file.name, + mimetype: file.mimetype, + size: file.size, + url_private: file.url_private, + permalink: file.permalink, + mode: file.mode, + })), + attachments: message.attachments, + blocks: message.blocks, + edited: message.edited + ? { + user: message.edited.user, + ts: message.edited.ts, + } + : undefined, + permalink: message.permalink, + })) + + logger.info(`[${requestId}] Successfully read ${messages.length} messages`) + + return NextResponse.json({ + success: true, + output: { + messages, + }, + }) + } catch (error) { + if (error instanceof z.ZodError) { + logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors }) + return NextResponse.json( + { + success: false, + error: 'Invalid request data', + details: error.errors, + }, + { status: 400 } + ) + } + + logger.error(`[${requestId}] Error reading Slack messages:`, error) + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error occurred', + }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/tools/slack/send-message/route.ts b/apps/sim/app/api/tools/slack/send-message/route.ts index 9a82b6e5a..592721d0d 100644 --- a/apps/sim/app/api/tools/slack/send-message/route.ts +++ b/apps/sim/app/api/tools/slack/send-message/route.ts @@ -3,20 +3,24 @@ import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { createLogger } from '@/lib/logs/console/logger' -import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' -import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' +import { sendSlackMessage } from '../utils' export const dynamic = 'force-dynamic' const logger = createLogger('SlackSendMessageAPI') -const SlackSendMessageSchema = z.object({ - accessToken: z.string().min(1, 'Access token is required'), - channel: z.string().min(1, 'Channel is required'), - text: z.string().min(1, 'Message text is required'), - thread_ts: z.string().optional().nullable(), - files: z.array(z.any()).optional().nullable(), -}) +const SlackSendMessageSchema = z + .object({ + accessToken: z.string().min(1, 'Access token is required'), + channel: z.string().optional().nullable(), + userId: z.string().optional().nullable(), + text: z.string().min(1, 'Message text is required'), + thread_ts: z.string().optional().nullable(), + files: z.array(z.any()).optional().nullable(), + }) + .refine((data) => data.channel || data.userId, { + message: 'Either channel or userId is required', + }) export async function POST(request: NextRequest) { const requestId = generateRequestId() @@ -42,222 +46,33 @@ export async function POST(request: NextRequest) { const body = await request.json() const validatedData = SlackSendMessageSchema.parse(body) + const isDM = !!validatedData.userId logger.info(`[${requestId}] Sending Slack message`, { channel: validatedData.channel, + userId: validatedData.userId, + isDM, hasFiles: !!(validatedData.files && validatedData.files.length > 0), fileCount: validatedData.files?.length || 0, }) - if (!validatedData.files || validatedData.files.length === 0) { - logger.info(`[${requestId}] No files, using chat.postMessage`) - - const response = await fetch('https://slack.com/api/chat.postMessage', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${validatedData.accessToken}`, - }, - body: JSON.stringify({ - channel: validatedData.channel, - text: validatedData.text, - ...(validatedData.thread_ts && { thread_ts: validatedData.thread_ts }), - }), - }) - - const data = await response.json() - - if (!data.ok) { - logger.error(`[${requestId}] Slack API error:`, data.error) - return NextResponse.json( - { - success: false, - error: data.error || 'Failed to send message', - }, - { status: 400 } - ) - } - - logger.info(`[${requestId}] Message sent successfully`) - const messageObj = data.message || { - type: 'message', - ts: data.ts, + const result = await sendSlackMessage( + { + accessToken: validatedData.accessToken, + channel: validatedData.channel ?? undefined, + userId: validatedData.userId ?? undefined, text: validatedData.text, - channel: data.channel, - } - return NextResponse.json({ - success: true, - output: { - message: messageObj, - ts: data.ts, - channel: data.channel, - }, - }) - } - - logger.info(`[${requestId}] Processing ${validatedData.files.length} file(s)`) - - const userFiles = processFilesToUserFiles(validatedData.files, requestId, logger) - - if (userFiles.length === 0) { - logger.warn(`[${requestId}] No valid files to upload`) - const response = await fetch('https://slack.com/api/chat.postMessage', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${validatedData.accessToken}`, - }, - body: JSON.stringify({ - channel: validatedData.channel, - text: validatedData.text, - ...(validatedData.thread_ts && { thread_ts: validatedData.thread_ts }), - }), - }) - - const data = await response.json() - const messageObj = data.message || { - type: 'message', - ts: data.ts, - text: validatedData.text, - channel: data.channel, - } - return NextResponse.json({ - success: true, - output: { - message: messageObj, - ts: data.ts, - channel: data.channel, - }, - }) - } - - const uploadedFileIds: string[] = [] - - for (const userFile of userFiles) { - logger.info(`[${requestId}] Uploading file: ${userFile.name}`) - - const buffer = await downloadFileFromStorage(userFile, requestId, logger) - - const getUrlResponse = await fetch('https://slack.com/api/files.getUploadURLExternal', { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: `Bearer ${validatedData.accessToken}`, - }, - body: new URLSearchParams({ - filename: userFile.name, - length: buffer.length.toString(), - }), - }) - - const urlData = await getUrlResponse.json() - - if (!urlData.ok) { - logger.error(`[${requestId}] Failed to get upload URL:`, urlData.error) - continue - } - - logger.info(`[${requestId}] Got upload URL for ${userFile.name}, file_id: ${urlData.file_id}`) - - const uploadResponse = await fetch(urlData.upload_url, { - method: 'POST', - body: new Uint8Array(buffer), - }) - - if (!uploadResponse.ok) { - logger.error(`[${requestId}] Failed to upload file data: ${uploadResponse.status}`) - continue - } - - logger.info(`[${requestId}] File data uploaded successfully`) - uploadedFileIds.push(urlData.file_id) - } - - if (uploadedFileIds.length === 0) { - logger.warn(`[${requestId}] No files uploaded successfully, sending text-only message`) - const response = await fetch('https://slack.com/api/chat.postMessage', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${validatedData.accessToken}`, - }, - body: JSON.stringify({ - channel: validatedData.channel, - text: validatedData.text, - ...(validatedData.thread_ts && { thread_ts: validatedData.thread_ts }), - }), - }) - - const data = await response.json() - const messageObj = data.message || { - type: 'message', - ts: data.ts, - text: validatedData.text, - channel: data.channel, - } - return NextResponse.json({ - success: true, - output: { - message: messageObj, - ts: data.ts, - channel: data.channel, - }, - }) - } - - const completeResponse = await fetch('https://slack.com/api/files.completeUploadExternal', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${validatedData.accessToken}`, + threadTs: validatedData.thread_ts ?? undefined, + files: validatedData.files ?? undefined, }, - body: JSON.stringify({ - files: uploadedFileIds.map((id) => ({ id })), - channel_id: validatedData.channel, - initial_comment: validatedData.text, - }), - }) + requestId, + logger + ) - const completeData = await completeResponse.json() - - if (!completeData.ok) { - logger.error(`[${requestId}] Failed to complete upload:`, completeData.error) - return NextResponse.json( - { - success: false, - error: completeData.error || 'Failed to complete file upload', - }, - { status: 400 } - ) + if (!result.success) { + return NextResponse.json({ success: false, error: result.error }, { status: 400 }) } - logger.info(`[${requestId}] Files uploaded and shared successfully`) - - // For file uploads, construct a message object - const fileTs = completeData.files?.[0]?.created?.toString() || (Date.now() / 1000).toString() - const fileMessage = { - type: 'message', - ts: fileTs, - text: validatedData.text, - channel: validatedData.channel, - files: completeData.files?.map((file: any) => ({ - id: file?.id, - name: file?.name, - mimetype: file?.mimetype, - size: file?.size, - url_private: file?.url_private, - permalink: file?.permalink, - })), - } - - return NextResponse.json({ - success: true, - output: { - message: fileMessage, - ts: fileTs, - channel: validatedData.channel, - fileCount: uploadedFileIds.length, - }, - }) + return NextResponse.json({ success: true, output: result.output }) } catch (error) { logger.error(`[${requestId}] Error sending Slack message:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/slack/update-message/route.ts b/apps/sim/app/api/tools/slack/update-message/route.ts index c40b6d34c..d89f9b0a9 100644 --- a/apps/sim/app/api/tools/slack/update-message/route.ts +++ b/apps/sim/app/api/tools/slack/update-message/route.ts @@ -10,7 +10,7 @@ const logger = createLogger('SlackUpdateMessageAPI') const SlackUpdateMessageSchema = z.object({ accessToken: z.string().min(1, 'Access token is required'), - channel: z.string().min(1, 'Channel ID is required'), + channel: z.string().min(1, 'Channel is required'), timestamp: z.string().min(1, 'Message timestamp is required'), text: z.string().min(1, 'Message text is required'), }) diff --git a/apps/sim/app/api/tools/slack/users/route.ts b/apps/sim/app/api/tools/slack/users/route.ts new file mode 100644 index 000000000..97d73c88d --- /dev/null +++ b/apps/sim/app/api/tools/slack/users/route.ts @@ -0,0 +1,113 @@ +import { NextResponse } from 'next/server' +import { authorizeCredentialUse } from '@/lib/auth/credential-access' +import { generateRequestId } from '@/lib/core/utils/request' +import { createLogger } from '@/lib/logs/console/logger' +import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' + +export const dynamic = 'force-dynamic' + +const logger = createLogger('SlackUsersAPI') + +interface SlackUser { + id: string + name: string + real_name: string + deleted: boolean + is_bot: boolean +} + +export async function POST(request: Request) { + try { + const requestId = generateRequestId() + const body = await request.json() + const { credential, workflowId } = body + + if (!credential) { + logger.error('Missing credential in request') + return NextResponse.json({ error: 'Credential is required' }, { status: 400 }) + } + + let accessToken: string + const isBotToken = credential.startsWith('xoxb-') + + if (isBotToken) { + accessToken = credential + logger.info('Using direct bot token for Slack API') + } else { + const authz = await authorizeCredentialUse(request as any, { + credentialId: credential, + workflowId, + }) + if (!authz.ok || !authz.credentialOwnerUserId) { + return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 }) + } + const resolvedToken = await refreshAccessTokenIfNeeded( + credential, + authz.credentialOwnerUserId, + requestId + ) + if (!resolvedToken) { + logger.error('Failed to get access token', { + credentialId: credential, + userId: authz.credentialOwnerUserId, + }) + return NextResponse.json( + { + error: 'Could not retrieve access token', + authRequired: true, + }, + { status: 401 } + ) + } + accessToken = resolvedToken + logger.info('Using OAuth token for Slack API') + } + + const data = await fetchSlackUsers(accessToken) + + const users = (data.members || []) + .filter((user: SlackUser) => !user.deleted && !user.is_bot) + .map((user: SlackUser) => ({ + id: user.id, + name: user.name, + real_name: user.real_name || user.name, + })) + + logger.info(`Successfully fetched ${users.length} Slack users`, { + total: data.members?.length || 0, + tokenType: isBotToken ? 'bot_token' : 'oauth', + }) + return NextResponse.json({ users }) + } catch (error) { + logger.error('Error processing Slack users request:', error) + return NextResponse.json( + { error: 'Failed to retrieve Slack users', details: (error as Error).message }, + { status: 500 } + ) + } +} + +async function fetchSlackUsers(accessToken: string) { + const url = new URL('https://slack.com/api/users.list') + url.searchParams.append('limit', '200') + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + }) + + if (!response.ok) { + throw new Error(`Slack API error: ${response.status} ${response.statusText}`) + } + + const data = await response.json() + + if (!data.ok) { + throw new Error(data.error || 'Failed to fetch users') + } + + return data +} diff --git a/apps/sim/app/api/tools/slack/utils.ts b/apps/sim/app/api/tools/slack/utils.ts new file mode 100644 index 000000000..b52d73420 --- /dev/null +++ b/apps/sim/app/api/tools/slack/utils.ts @@ -0,0 +1,288 @@ +import type { Logger } from '@/lib/logs/console/logger' +import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' +import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' + +/** + * Sends a message to a Slack channel using chat.postMessage + */ +export async function postSlackMessage( + accessToken: string, + channel: string, + text: string, + threadTs?: string | null +): Promise<{ ok: boolean; ts?: string; channel?: string; message?: any; error?: string }> { + const response = await fetch('https://slack.com/api/chat.postMessage', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + channel, + text, + ...(threadTs && { thread_ts: threadTs }), + }), + }) + + return response.json() +} + +/** + * Creates a default message object when the API doesn't return one + */ +export function createDefaultMessageObject( + ts: string, + text: string, + channel: string +): Record { + return { + type: 'message', + ts, + text, + channel, + } +} + +/** + * Formats the success response for a sent message + */ +export function formatMessageSuccessResponse( + data: any, + text: string +): { + message: any + ts: string + channel: string +} { + const messageObj = data.message || createDefaultMessageObject(data.ts, text, data.channel) + return { + message: messageObj, + ts: data.ts, + channel: data.channel, + } +} + +/** + * Uploads files to Slack and returns the uploaded file IDs + */ +export async function uploadFilesToSlack( + files: any[], + accessToken: string, + requestId: string, + logger: Logger +): Promise { + const userFiles = processFilesToUserFiles(files, requestId, logger) + const uploadedFileIds: string[] = [] + + for (const userFile of userFiles) { + logger.info(`[${requestId}] Uploading file: ${userFile.name}`) + + const buffer = await downloadFileFromStorage(userFile, requestId, logger) + + const getUrlResponse = await fetch('https://slack.com/api/files.getUploadURLExternal', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Bearer ${accessToken}`, + }, + body: new URLSearchParams({ + filename: userFile.name, + length: buffer.length.toString(), + }), + }) + + const urlData = await getUrlResponse.json() + + if (!urlData.ok) { + logger.error(`[${requestId}] Failed to get upload URL:`, urlData.error) + continue + } + + logger.info(`[${requestId}] Got upload URL for ${userFile.name}, file_id: ${urlData.file_id}`) + + const uploadResponse = await fetch(urlData.upload_url, { + method: 'POST', + body: new Uint8Array(buffer), + }) + + if (!uploadResponse.ok) { + logger.error(`[${requestId}] Failed to upload file data: ${uploadResponse.status}`) + continue + } + + logger.info(`[${requestId}] File data uploaded successfully`) + uploadedFileIds.push(urlData.file_id) + } + + return uploadedFileIds +} + +/** + * Completes the file upload process by associating files with a channel + */ +export async function completeSlackFileUpload( + uploadedFileIds: string[], + channel: string, + text: string, + accessToken: string +): Promise<{ ok: boolean; files?: any[]; error?: string }> { + const response = await fetch('https://slack.com/api/files.completeUploadExternal', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + files: uploadedFileIds.map((id) => ({ id })), + channel_id: channel, + initial_comment: text, + }), + }) + + return response.json() +} + +/** + * Creates a message object for file uploads + */ +export function createFileMessageObject( + text: string, + channel: string, + files: any[] +): Record { + const fileTs = files?.[0]?.created?.toString() || (Date.now() / 1000).toString() + return { + type: 'message', + ts: fileTs, + text, + channel, + files: files?.map((file: any) => ({ + id: file?.id, + name: file?.name, + mimetype: file?.mimetype, + size: file?.size, + url_private: file?.url_private, + permalink: file?.permalink, + })), + } +} + +/** + * Opens a DM channel with a user and returns the channel ID + */ +export async function openDMChannel( + accessToken: string, + userId: string, + requestId: string, + logger: Logger +): Promise { + const response = await fetch('https://slack.com/api/conversations.open', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + users: userId, + }), + }) + + const data = await response.json() + + if (!data.ok) { + logger.error(`[${requestId}] Failed to open DM channel:`, data.error) + throw new Error(data.error || 'Failed to open DM channel with user') + } + + logger.info(`[${requestId}] Opened DM channel: ${data.channel.id}`) + return data.channel.id +} + +export interface SlackMessageParams { + accessToken: string + channel?: string + userId?: string + text: string + threadTs?: string | null + files?: any[] | null +} + +/** + * Sends a Slack message with optional file attachments + * Supports both channel messages and direct messages via userId + */ +export async function sendSlackMessage( + params: SlackMessageParams, + requestId: string, + logger: Logger +): Promise<{ + success: boolean + output?: { message: any; ts: string; channel: string; fileCount?: number } + error?: string +}> { + const { accessToken, text, threadTs, files } = params + let { channel } = params + + if (!channel && params.userId) { + logger.info(`[${requestId}] Opening DM channel for user: ${params.userId}`) + channel = await openDMChannel(accessToken, params.userId, requestId, logger) + } + + if (!channel) { + return { success: false, error: 'Either channel or userId is required' } + } + + // No files - simple message + if (!files || files.length === 0) { + logger.info(`[${requestId}] No files, using chat.postMessage`) + + const data = await postSlackMessage(accessToken, channel, text, threadTs) + + if (!data.ok) { + logger.error(`[${requestId}] Slack API error:`, data.error) + return { success: false, error: data.error || 'Failed to send message' } + } + + logger.info(`[${requestId}] Message sent successfully`) + return { success: true, output: formatMessageSuccessResponse(data, text) } + } + + // Process files + logger.info(`[${requestId}] Processing ${files.length} file(s)`) + const uploadedFileIds = await uploadFilesToSlack(files, accessToken, requestId, logger) + + // No valid files uploaded - send text-only + if (uploadedFileIds.length === 0) { + logger.warn(`[${requestId}] No valid files to upload, sending text-only message`) + + const data = await postSlackMessage(accessToken, channel, text, threadTs) + + if (!data.ok) { + return { success: false, error: data.error || 'Failed to send message' } + } + + return { success: true, output: formatMessageSuccessResponse(data, text) } + } + + // Complete file upload + const completeData = await completeSlackFileUpload(uploadedFileIds, channel, text, accessToken) + + if (!completeData.ok) { + logger.error(`[${requestId}] Failed to complete upload:`, completeData.error) + return { success: false, error: completeData.error || 'Failed to complete file upload' } + } + + logger.info(`[${requestId}] Files uploaded and shared successfully`) + + const fileMessage = createFileMessageObject(text, channel, completeData.files || []) + + return { + success: true, + output: { + message: fileMessage, + ts: fileMessage.ts, + channel, + fileCount: uploadedFileIds.length, + }, + } +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx index 3a3078c95..818defe02 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx @@ -179,6 +179,9 @@ const SCOPE_DESCRIPTIONS: Record = { 'groups:history': 'Read private messages', 'chat:write': 'Send messages', 'chat:write.public': 'Post to public channels', + 'im:write': 'Send direct messages', + 'im:history': 'Read direct message history', + 'im:read': 'View direct message channels', 'users:read': 'View workspace users', 'files:write': 'Upload files', 'files:read': 'Download and read files', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts index 28f9a609a..dc5ba115e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts @@ -1,4 +1,3 @@ -export { ChannelSelectorInput } from './channel-selector/channel-selector-input' export { CheckboxList } from './checkbox-list/checkbox-list' export { Code } from './code/code' export { ComboBox } from './combobox/combobox' @@ -24,6 +23,7 @@ export { ProjectSelectorInput } from './project-selector/project-selector-input' export { ResponseFormat } from './response/response-format' export { ScheduleSave } from './schedule-save/schedule-save' export { ShortInput } from './short-input/short-input' +export { SlackSelectorInput } from './slack-selector/slack-selector-input' export { SliderInput } from './slider-input/slider-input' export { InputFormat } from './starter/input-format' export { SubBlockInputController } from './sub-block-input-controller' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/channel-selector/channel-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-selector/slack-selector-input.tsx similarity index 74% rename from apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/channel-selector/channel-selector-input.tsx rename to apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-selector/slack-selector-input.tsx index 245dbd51c..9267ff174 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/channel-selector/channel-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-selector/slack-selector-input.tsx @@ -9,30 +9,51 @@ import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/c import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import type { SubBlockConfig } from '@/blocks/types' -import type { SelectorContext } from '@/hooks/selectors/types' +import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types' -interface ChannelSelectorInputProps { +type SlackSelectorType = 'channel-selector' | 'user-selector' + +const SELECTOR_CONFIG: Record< + SlackSelectorType, + { selectorKey: SelectorKey; placeholder: string; label: string } +> = { + 'channel-selector': { + selectorKey: 'slack.channels', + placeholder: 'Select Slack channel', + label: 'Channel', + }, + 'user-selector': { + selectorKey: 'slack.users', + placeholder: 'Select Slack user', + label: 'User', + }, +} + +interface SlackSelectorInputProps { blockId: string subBlock: SubBlockConfig disabled?: boolean - onChannelSelect?: (channelId: string) => void + onSelect?: (value: string) => void isPreview?: boolean previewValue?: any | null previewContextValues?: Record } -export function ChannelSelectorInput({ +export function SlackSelectorInput({ blockId, subBlock, disabled = false, - onChannelSelect, + onSelect, isPreview = false, previewValue, previewContextValues, -}: ChannelSelectorInputProps) { +}: SlackSelectorInputProps) { + const selectorType = subBlock.type as SlackSelectorType + const config = SELECTOR_CONFIG[selectorType] + const params = useParams() const workflowIdFromUrl = (params?.workflowId as string) || '' - const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id) + const [storeValue] = useSubBlockValue(blockId, subBlock.id) const [authMethod] = useSubBlockValue(blockId, 'authMethod') const [botToken] = useSubBlockValue(blockId, 'botToken') const [connectedCredential] = useSubBlockValue(blockId, 'credential') @@ -40,37 +61,32 @@ export function ChannelSelectorInput({ const effectiveAuthMethod = previewContextValues?.authMethod ?? authMethod const effectiveBotToken = previewContextValues?.botToken ?? botToken const effectiveCredential = previewContextValues?.credential ?? connectedCredential - const [_channelInfo, setChannelInfo] = useState(null) + const [_selectedValue, setSelectedValue] = useState(null) - // Use serviceId to identify the service and derive providerId for credential lookup const serviceId = subBlock.serviceId || '' const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId]) const isSlack = serviceId === 'slack' - // Central dependsOn gating const { finalDisabled, dependsOn } = useDependsOnGate(blockId, subBlock, { disabled, isPreview, previewContextValues, }) - // Choose credential strictly based on auth method - use effective values const credential: string = (effectiveAuthMethod as string) === 'bot_token' ? (effectiveBotToken as string) || '' : (effectiveCredential as string) || '' - // Determine if connected OAuth credential is foreign (not applicable for bot tokens) const { isForeignCredential } = useForeignCredential( effectiveProviderId, (effectiveAuthMethod as string) === 'bot_token' ? '' : (effectiveCredential as string) || '' ) - // Get the current value from the store or prop value if in preview mode (same pattern as file-selector) useEffect(() => { const val = isPreview && previewValue !== undefined ? previewValue : storeValue if (typeof val === 'string') { - setChannelInfo(val) + setSelectedValue(val) } }, [isPreview, previewValue, storeValue]) @@ -91,11 +107,14 @@ export function ChannelSelectorInput({
- Channel selector not supported for service: {serviceId || 'unknown'} + {config.label} selector not supported for service: {serviceId || 'unknown'}
-

This channel selector is not yet implemented for {serviceId || 'unknown'}

+

+ This {config.label.toLowerCase()} selector is not yet implemented for{' '} + {serviceId || 'unknown'} +

) @@ -108,16 +127,16 @@ export function ChannelSelectorInput({ { - setChannelInfo(value) + setSelectedValue(value) if (!isPreview) { - onChannelSelect?.(value) + onSelect?.(value) } }} /> diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx index eaeecce09..d3f9534d9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx @@ -24,7 +24,6 @@ import { type OAuthService, } from '@/lib/oauth/oauth' import { - ChannelSelectorInput, CheckboxList, Code, ComboBox, @@ -33,6 +32,7 @@ import { LongInput, ProjectSelectorInput, ShortInput, + SlackSelectorInput, SliderInput, Table, TimeInput, @@ -520,7 +520,7 @@ function ChannelSelectorSyncWrapper({ }) { return ( - diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index 395c4c61d..0fb5dc6ed 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -5,7 +5,6 @@ import { Button } from '@/components/ui/button' import { cn } from '@/lib/core/utils/cn' import type { FieldDiffStatus } from '@/lib/workflows/diff/types' import { - ChannelSelectorInput, CheckboxList, Code, ComboBox, @@ -32,6 +31,7 @@ import { ResponseFormat, ScheduleSave, ShortInput, + SlackSelectorInput, SliderInput, Switch, Table, @@ -732,8 +732,9 @@ function SubBlockComponent({ ) case 'channel-selector': + case 'user-selector': return ( - = { value: () => 'oauth', required: true, }, + { + id: 'destinationType', + title: 'Destination', + type: 'dropdown', + options: [ + { label: 'Channel', id: 'channel' }, + { label: 'Direct Message', id: 'dm' }, + ], + value: () => 'channel', + condition: { + field: 'operation', + value: ['send', 'read'], + }, + }, { id: 'credential', title: 'Slack Account', @@ -60,6 +74,9 @@ export const SlackBlock: BlockConfig = { 'groups:history', 'chat:write', 'chat:write.public', + 'im:write', + 'im:history', + 'im:read', 'users:read', 'files:write', 'files:read', @@ -98,9 +115,13 @@ export const SlackBlock: BlockConfig = { field: 'operation', value: ['list_channels', 'list_users', 'get_user'], not: true, + and: { + field: 'destinationType', + value: 'dm', + not: true, + }, }, }, - // Manual channel ID input (advanced mode) { id: 'manualChannel', title: 'Channel ID', @@ -112,6 +133,37 @@ export const SlackBlock: BlockConfig = { field: 'operation', value: ['list_channels', 'list_users', 'get_user'], not: true, + and: { + field: 'destinationType', + value: 'dm', + not: true, + }, + }, + }, + { + id: 'dmUserId', + title: 'User', + type: 'user-selector', + canonicalParamId: 'dmUserId', + serviceId: 'slack', + placeholder: 'Select Slack user', + mode: 'basic', + dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] }, + condition: { + field: 'destinationType', + value: 'dm', + }, + }, + { + id: 'manualDmUserId', + title: 'User ID', + type: 'short-input', + canonicalParamId: 'dmUserId', + placeholder: 'Enter Slack user ID (e.g., U1234567890)', + mode: 'advanced', + condition: { + field: 'destinationType', + value: 'dm', }, }, { @@ -137,7 +189,6 @@ export const SlackBlock: BlockConfig = { }, required: false, }, - // File upload (basic mode) { id: 'attachmentFiles', title: 'Attachments', @@ -149,7 +200,6 @@ export const SlackBlock: BlockConfig = { multiple: true, required: false, }, - // Variable reference (advanced mode) { id: 'files', title: 'File Attachments', @@ -416,8 +466,11 @@ export const SlackBlock: BlockConfig = { authMethod, botToken, operation, + destinationType, channel, manualChannel, + dmUserId, + manualDmUserId, text, title, content, @@ -440,21 +493,26 @@ export const SlackBlock: BlockConfig = { ...rest } = params - // Handle both selector and manual channel input + const isDM = destinationType === 'dm' const effectiveChannel = (channel || manualChannel || '').trim() + const effectiveUserId = (dmUserId || manualDmUserId || '').trim() - // Operations that don't require a channel const noChannelOperations = ['list_channels', 'list_users', 'get_user'] + const dmSupportedOperations = ['send', 'read'] - // Channel is required for most operations - if (!effectiveChannel && !noChannelOperations.includes(operation)) { + if (isDM && dmSupportedOperations.includes(operation)) { + if (!effectiveUserId) { + throw new Error('User is required for DM operations.') + } + } else if (!effectiveChannel && !noChannelOperations.includes(operation)) { throw new Error('Channel is required.') } const baseParams: Record = {} - // Only add channel if we have one (not needed for list_channels) - if (effectiveChannel) { + if (isDM && dmSupportedOperations.includes(operation)) { + baseParams.userId = effectiveUserId + } else if (effectiveChannel) { baseParams.channel = effectiveChannel } @@ -472,18 +530,15 @@ export const SlackBlock: BlockConfig = { baseParams.credential = credential } - // Handle operation-specific params switch (operation) { case 'send': { if (!text || text.trim() === '') { throw new Error('Message text is required for send operation') } baseParams.text = text - // Add thread_ts if provided if (threadTs) { baseParams.thread_ts = threadTs } - // Add files if provided const fileParam = attachmentFiles || files if (fileParam) { baseParams.files = fileParam @@ -592,10 +647,13 @@ export const SlackBlock: BlockConfig = { inputs: { operation: { type: 'string', description: 'Operation to perform' }, authMethod: { type: 'string', description: 'Authentication method' }, + destinationType: { type: 'string', description: 'Destination type (channel or dm)' }, credential: { type: 'string', description: 'Slack access token' }, botToken: { type: 'string', description: 'Bot token' }, channel: { type: 'string', description: 'Channel identifier' }, manualChannel: { type: 'string', description: 'Manual channel identifier' }, + dmUserId: { type: 'string', description: 'User ID for DM recipient (selector)' }, + manualDmUserId: { type: 'string', description: 'User ID for DM recipient (manual input)' }, text: { type: 'string', description: 'Message text' }, attachmentFiles: { type: 'json', description: 'Files to attach (UI upload)' }, files: { type: 'array', description: 'Files to attach (UserFile array)' }, diff --git a/apps/sim/blocks/types.ts b/apps/sim/blocks/types.ts index 7cc0116c9..64c1a89a2 100644 --- a/apps/sim/blocks/types.ts +++ b/apps/sim/blocks/types.ts @@ -59,6 +59,7 @@ export type SubBlockType = | 'file-selector' // File selector for Google Drive, etc. | 'project-selector' // Project selector for Jira, Discord, etc. | 'channel-selector' // Channel selector for Slack, Discord, etc. + | 'user-selector' // User selector for Slack, etc. | 'folder-selector' // Folder selector for Gmail, etc. | 'knowledge-base-selector' // Knowledge base selector | 'knowledge-tag-filters' // Multiple tag filters for knowledge bases @@ -85,6 +86,7 @@ export type SubBlockType = export const SELECTOR_TYPES_HYDRATION_REQUIRED: SubBlockType[] = [ 'oauth-input', 'channel-selector', + 'user-selector', 'file-selector', 'folder-selector', 'project-selector', diff --git a/apps/sim/hooks/selectors/registry.ts b/apps/sim/hooks/selectors/registry.ts index 4137e3065..39844c297 100644 --- a/apps/sim/hooks/selectors/registry.ts +++ b/apps/sim/hooks/selectors/registry.ts @@ -10,6 +10,7 @@ import type { const SELECTOR_STALE = 60 * 1000 type SlackChannel = { id: string; name: string } +type SlackUser = { id: string; name: string; real_name: string } type FolderResponse = { id: string; name: string } type PlannerTask = { id: string; title: string } @@ -59,6 +60,30 @@ const registry: Record = { })) }, }, + 'slack.users': { + key: 'slack.users', + staleTime: SELECTOR_STALE, + getQueryKey: ({ context }: SelectorQueryArgs) => [ + 'selectors', + 'slack.users', + context.credentialId ?? 'none', + ], + enabled: ({ context }) => Boolean(context.credentialId), + fetchList: async ({ context }: SelectorQueryArgs) => { + const body = JSON.stringify({ + credential: context.credentialId, + workflowId: context.workflowId, + }) + const data = await fetchJson<{ users: SlackUser[] }>('/api/tools/slack/users', { + method: 'POST', + body, + }) + return (data.users || []).map((user) => ({ + id: user.id, + label: user.real_name || user.name, + })) + }, + }, 'gmail.labels': { key: 'gmail.labels', staleTime: SELECTOR_STALE, diff --git a/apps/sim/hooks/selectors/resolution.ts b/apps/sim/hooks/selectors/resolution.ts index 76e3f2117..78af03f93 100644 --- a/apps/sim/hooks/selectors/resolution.ts +++ b/apps/sim/hooks/selectors/resolution.ts @@ -32,6 +32,8 @@ export function resolveSelectorForSubBlock( return resolveFolderSelector(subBlock, args) case 'channel-selector': return resolveChannelSelector(subBlock, args) + case 'user-selector': + return resolveUserSelector(subBlock, args) case 'project-selector': return resolveProjectSelector(subBlock, args) case 'document-selector': @@ -157,6 +159,21 @@ function resolveChannelSelector( } } +function resolveUserSelector( + subBlock: SubBlockConfig, + args: SelectorResolutionArgs +): SelectorResolution { + const serviceId = subBlock.serviceId + if (serviceId !== 'slack') { + return { key: null, context: buildBaseContext(args), allowSearch: true } + } + return { + key: 'slack.users', + context: buildBaseContext(args), + allowSearch: true, + } +} + function resolveProjectSelector( subBlock: SubBlockConfig, args: SelectorResolutionArgs diff --git a/apps/sim/hooks/selectors/types.ts b/apps/sim/hooks/selectors/types.ts index d186c4d50..e9da5996a 100644 --- a/apps/sim/hooks/selectors/types.ts +++ b/apps/sim/hooks/selectors/types.ts @@ -3,6 +3,7 @@ import type { QueryKey } from '@tanstack/react-query' export type SelectorKey = | 'slack.channels' + | 'slack.users' | 'gmail.labels' | 'outlook.folders' | 'google.calendar' diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 0efdee78f..eec70eaa7 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -1640,6 +1640,9 @@ export const auth = betterAuth({ 'groups:history', 'chat:write', 'chat:write.public', + 'im:write', + 'im:history', + 'im:read', 'users:read', 'files:write', 'files:read', diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index 7b4f53caa..847ff59e6 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -637,6 +637,9 @@ export const OAUTH_PROVIDERS: Record = { 'groups:history', 'chat:write', 'chat:write.public', + 'im:write', + 'im:history', + 'im:read', 'users:read', 'files:write', 'files:read', diff --git a/apps/sim/serializer/index.ts b/apps/sim/serializer/index.ts index d61c3c344..db9401824 100644 --- a/apps/sim/serializer/index.ts +++ b/apps/sim/serializer/index.ts @@ -38,6 +38,57 @@ function shouldIncludeField(subBlockConfig: SubBlockConfig, isAdvancedMode: bool return true } +/** + * Evaluates a condition object against current field values. + * Used to determine if a conditionally-visible field should be included in params. + */ +function evaluateCondition( + condition: + | { + field: string + value: any + not?: boolean + and?: { field: string; value: any; not?: boolean } + } + | (() => { + field: string + value: any + not?: boolean + and?: { field: string; value: any; not?: boolean } + }) + | undefined, + values: Record +): boolean { + if (!condition) return true + + const actual = typeof condition === 'function' ? condition() : condition + const fieldValue = values[actual.field] + + const valueMatch = Array.isArray(actual.value) + ? fieldValue != null && + (actual.not ? !actual.value.includes(fieldValue) : actual.value.includes(fieldValue)) + : actual.not + ? fieldValue !== actual.value + : fieldValue === actual.value + + const andMatch = !actual.and + ? true + : (() => { + const andFieldValue = values[actual.and!.field] + const andValueMatch = Array.isArray(actual.and!.value) + ? andFieldValue != null && + (actual.and!.not + ? !actual.and!.value.includes(andFieldValue) + : actual.and!.value.includes(andFieldValue)) + : actual.and!.not + ? andFieldValue !== actual.and!.value + : andFieldValue === actual.and!.value + return andValueMatch + })() + + return valueMatch && andMatch +} + /** * Helper function to migrate agent block params from old format to messages array * Transforms systemPrompt/userPrompt into messages array format @@ -343,9 +394,15 @@ export class Serializer { const isStarterBlock = block.type === 'starter' const isAgentBlock = block.type === 'agent' - // First collect all current values from subBlocks, filtering by mode + // First pass: collect ALL raw values for condition evaluation + const allValues: Record = {} Object.entries(block.subBlocks).forEach(([id, subBlock]) => { - // Find the corresponding subblock config to check its mode + allValues[id] = subBlock.value + }) + + // Second pass: filter by mode and conditions + Object.entries(block.subBlocks).forEach(([id, subBlock]) => { + // Find the corresponding subblock config to check its mode and condition const subBlockConfig = blockConfig.subBlocks.find((config) => config.id === id) // Include field if it matches current mode OR if it's the starter inputFormat with values @@ -360,9 +417,14 @@ export class Serializer { const isLegacyAgentField = isAgentBlock && ['systemPrompt', 'userPrompt', 'memories'].includes(id) + // Check if field's condition is met (conditionally-hidden fields should be excluded) + const conditionMet = subBlockConfig + ? evaluateCondition(subBlockConfig.condition, allValues) + : true + if ( - (subBlockConfig && - (shouldIncludeField(subBlockConfig, isAdvancedMode) || hasStarterInputFormatValues)) || + (subBlockConfig && shouldIncludeField(subBlockConfig, isAdvancedMode) && conditionMet) || + hasStarterInputFormatValues || isLegacyAgentField ) { params[id] = subBlock.value @@ -475,52 +537,6 @@ export class Serializer { // Check required user-only parameters for the current tool const missingFields: string[] = [] - // Helper function to evaluate conditions - const evalCond = ( - condition: - | { - field: string - value: any - not?: boolean - and?: { field: string; value: any; not?: boolean } - } - | (() => { - field: string - value: any - not?: boolean - and?: { field: string; value: any; not?: boolean } - }) - | undefined, - values: Record - ): boolean => { - if (!condition) return true - const actual = typeof condition === 'function' ? condition() : condition - const fieldValue = values[actual.field] - - const valueMatch = Array.isArray(actual.value) - ? fieldValue != null && - (actual.not ? !actual.value.includes(fieldValue) : actual.value.includes(fieldValue)) - : actual.not - ? fieldValue !== actual.value - : fieldValue === actual.value - - const andMatch = !actual.and - ? true - : (() => { - const andFieldValue = values[actual.and!.field] - return Array.isArray(actual.and!.value) - ? andFieldValue != null && - (actual.and!.not - ? !actual.and!.value.includes(andFieldValue) - : actual.and!.value.includes(andFieldValue)) - : actual.and!.not - ? andFieldValue !== actual.and!.value - : andFieldValue === actual.and!.value - })() - - return valueMatch && andMatch - } - // Iterate through the tool's parameters, not the block's subBlocks Object.entries(currentTool.params || {}).forEach(([paramId, paramConfig]) => { if (paramConfig.required && paramConfig.visibility === 'user-only') { @@ -533,14 +549,14 @@ export class Serializer { const includedByMode = shouldIncludeField(subBlockConfig, isAdvancedMode) // Check visibility condition - const includedByCondition = evalCond(subBlockConfig.condition, params) + const includedByCondition = evaluateCondition(subBlockConfig.condition, params) // Check if field is required based on its required condition (if it's a condition object) const isRequired = (() => { if (!subBlockConfig.required) return false if (typeof subBlockConfig.required === 'boolean') return subBlockConfig.required // If required is a condition object, evaluate it - return evalCond(subBlockConfig.required, params) + return evaluateCondition(subBlockConfig.required, params) })() shouldValidateParam = includedByMode && includedByCondition && isRequired diff --git a/apps/sim/tools/slack/message.ts b/apps/sim/tools/slack/message.ts index 55d92bd87..d8c9e36b8 100644 --- a/apps/sim/tools/slack/message.ts +++ b/apps/sim/tools/slack/message.ts @@ -5,7 +5,7 @@ export const slackMessageTool: ToolConfig { - const url = new URL('https://slack.com/api/conversations.history') - url.searchParams.append('channel', params.channel) - // Cap limit at 15 due to Slack API restrictions for non-Marketplace apps - const limit = params.limit ? Number(params.limit) : 10 - url.searchParams.append('limit', String(Math.min(limit, 15))) - - if (params.oldest) { - url.searchParams.append('oldest', params.oldest) - } - if (params.latest) { - url.searchParams.append('latest', params.latest) - } - - return url.toString() - }, - method: 'GET', - headers: (params: SlackMessageReaderParams) => ({ + url: '/api/tools/slack/read-messages', + method: 'POST', + headers: () => ({ 'Content-Type': 'application/json', - Authorization: `Bearer ${params.accessToken || params.botToken}`, + }), + body: (params: SlackMessageReaderParams) => ({ + accessToken: params.accessToken || params.botToken, + channel: params.channel, + userId: params.userId, + limit: params.limit, + oldest: params.oldest, + latest: params.latest, }), }, transformResponse: async (response: Response) => { const data = await response.json() - if (!data.ok) { - if (data.error === 'not_in_channel') { - throw new Error( - 'Bot is not in the channel. Please invite the Sim bot to your Slack channel by typing: /invite @Sim Studio' - ) - } - if (data.error === 'channel_not_found') { - throw new Error('Channel not found. Please check the channel ID and try again.') - } - if (data.error === 'missing_scope') { - throw new Error( - 'Missing required permissions. Please reconnect your Slack account with the necessary scopes (channels:history, groups:history).' - ) - } + if (!data.success) { throw new Error(data.error || 'Failed to fetch messages from Slack') } - const messages = (data.messages || []).map((message: any) => ({ - // Core properties - type: message.type || 'message', - ts: message.ts, - text: message.text || '', - user: message.user, - bot_id: message.bot_id, - username: message.username, - channel: message.channel, - team: message.team, - - // Thread properties - thread_ts: message.thread_ts, - parent_user_id: message.parent_user_id, - reply_count: message.reply_count, - reply_users_count: message.reply_users_count, - latest_reply: message.latest_reply, - subscribed: message.subscribed, - last_read: message.last_read, - unread_count: message.unread_count, - - // Message subtype - subtype: message.subtype, - - // Reactions and interactions - reactions: message.reactions?.map((reaction: any) => ({ - name: reaction.name, - count: reaction.count, - users: reaction.users || [], - })), - is_starred: message.is_starred, - pinned_to: message.pinned_to, - - // Content attachments - files: message.files?.map((file: any) => ({ - id: file.id, - name: file.name, - mimetype: file.mimetype, - size: file.size, - url_private: file.url_private, - permalink: file.permalink, - mode: file.mode, - })), - attachments: message.attachments, - blocks: message.blocks, - - // Metadata - edited: message.edited - ? { - user: message.edited.user, - ts: message.edited.ts, - } - : undefined, - permalink: message.permalink, - })) - return { success: true, - output: { - messages, - }, + output: data.output, } }, diff --git a/apps/sim/tools/slack/types.ts b/apps/sim/tools/slack/types.ts index 38d3bc7ff..b6eada4c9 100644 --- a/apps/sim/tools/slack/types.ts +++ b/apps/sim/tools/slack/types.ts @@ -7,7 +7,8 @@ export interface SlackBaseParams { } export interface SlackMessageParams extends SlackBaseParams { - channel: string + channel?: string + userId?: string text: string thread_ts?: string files?: any[] @@ -21,7 +22,8 @@ export interface SlackCanvasParams extends SlackBaseParams { } export interface SlackMessageReaderParams extends SlackBaseParams { - channel: string + channel?: string + userId?: string limit?: number oldest?: string latest?: string From b72e111e223bebdc8446a9713cfe6cdfe7c7aa02 Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 17:52:19 -0800 Subject: [PATCH 11/17] fix(vm): use node child process for RCE (#2389) * fix(vm): use node child process for RCE * ack PR comments * cleanup oprhaned processes * cleaned up * ack pr comment * fix path * use spawn instead of fork * acked PR comments --- apps/sim/app/api/function/execute/route.ts | 22 +- apps/sim/lib/execution/isolated-vm-worker.cjs | 357 +++++++++++ apps/sim/lib/execution/isolated-vm.ts | 572 +++++++----------- bun.lock | 6 + docker/app.Dockerfile | 42 +- 5 files changed, 639 insertions(+), 360 deletions(-) create mode 100644 apps/sim/lib/execution/isolated-vm-worker.cjs diff --git a/apps/sim/app/api/function/execute/route.ts b/apps/sim/app/api/function/execute/route.ts index 9cc78d6ca..b09fde257 100644 --- a/apps/sim/app/api/function/execute/route.ts +++ b/apps/sim/app/api/function/execute/route.ts @@ -895,11 +895,12 @@ export async function POST(req: NextRequest) { userCodeStartLine = wrapperLines.length + 1 let codeToExecute = resolvedCode + let prependedLineCount = 0 if (isCustomTool) { - const paramDestructuring = Object.keys(executionParams) - .map((key) => `const ${key} = params.${key};`) - .join('\n') + const paramKeys = Object.keys(executionParams) + const paramDestructuring = paramKeys.map((key) => `const ${key} = params.${key};`).join('\n') codeToExecute = `${paramDestructuring}\n${resolvedCode}` + prependedLineCount = paramKeys.length } const isolatedResult = await executeInIsolatedVM({ @@ -920,14 +921,25 @@ export async function POST(req: NextRequest) { }) const ivmError = isolatedResult.error + // Adjust line number for prepended param destructuring in custom tools + let adjustedLine = ivmError.line + let adjustedLineContent = ivmError.lineContent + if (prependedLineCount > 0 && ivmError.line !== undefined) { + adjustedLine = Math.max(1, ivmError.line - prependedLineCount) + // Get line content from original user code, not the prepended code + const codeLines = resolvedCode.split('\n') + if (adjustedLine <= codeLines.length) { + adjustedLineContent = codeLines[adjustedLine - 1]?.trim() + } + } const enhancedError: EnhancedError = { message: ivmError.message, name: ivmError.name, stack: ivmError.stack, originalError: ivmError, - line: ivmError.line, + line: adjustedLine, column: ivmError.column, - lineContent: ivmError.lineContent, + lineContent: adjustedLineContent, } const userFriendlyErrorMessage = createUserFriendlyErrorMessage( diff --git a/apps/sim/lib/execution/isolated-vm-worker.cjs b/apps/sim/lib/execution/isolated-vm-worker.cjs new file mode 100644 index 000000000..53aa5b6fc --- /dev/null +++ b/apps/sim/lib/execution/isolated-vm-worker.cjs @@ -0,0 +1,357 @@ +/** + * Node.js worker for isolated-vm execution. + * Runs in a separate Node.js process, communicates with parent via IPC. + */ + +const ivm = require('isolated-vm') + +const USER_CODE_START_LINE = 4 +const pendingFetches = new Map() +let fetchIdCounter = 0 +const FETCH_TIMEOUT_MS = 30000 + +/** + * Extract line and column from error stack or message + */ +function extractLineInfo(errorMessage, stack) { + if (stack) { + const stackMatch = stack.match(/(?:|user-function\.js):(\d+):(\d+)/) + if (stackMatch) { + return { + line: Number.parseInt(stackMatch[1], 10), + column: Number.parseInt(stackMatch[2], 10), + } + } + const atMatch = stack.match(/at\s+(?:|user-function\.js):(\d+):(\d+)/) + if (atMatch) { + return { + line: Number.parseInt(atMatch[1], 10), + column: Number.parseInt(atMatch[2], 10), + } + } + } + + const msgMatch = errorMessage.match(/:(\d+):(\d+)/) + if (msgMatch) { + return { + line: Number.parseInt(msgMatch[1], 10), + column: Number.parseInt(msgMatch[2], 10), + } + } + + return {} +} + +/** + * Convert isolated-vm error info to a format compatible with the route's error handling + */ +function convertToCompatibleError(errorInfo, userCode) { + const { name } = errorInfo + let { message, stack } = errorInfo + + message = message + .replace(/\s*\[user-function\.js:\d+:\d+\]/g, '') + .replace(/\s*\[:\d+:\d+\]/g, '') + .replace(/\s*\(:\d+:\d+\)/g, '') + .trim() + + const lineInfo = extractLineInfo(errorInfo.message, stack) + + let userLine + let lineContent + + if (lineInfo.line !== undefined) { + userLine = lineInfo.line - USER_CODE_START_LINE + const codeLines = userCode.split('\n') + if (userLine > 0 && userLine <= codeLines.length) { + lineContent = codeLines[userLine - 1]?.trim() + } else if (userLine <= 0) { + userLine = 1 + lineContent = codeLines[0]?.trim() + } else { + userLine = codeLines.length + lineContent = codeLines[codeLines.length - 1]?.trim() + } + } + + if (stack) { + stack = stack.replace(/:(\d+):(\d+)/g, (_, line, col) => { + const adjustedLine = Number.parseInt(line, 10) - USER_CODE_START_LINE + return `user-function.js:${Math.max(1, adjustedLine)}:${col}` + }) + stack = stack.replace(/at :(\d+):(\d+)/g, (_, line, col) => { + const adjustedLine = Number.parseInt(line, 10) - USER_CODE_START_LINE + return `at user-function.js:${Math.max(1, adjustedLine)}:${col}` + }) + } + + return { + message, + name, + stack, + line: userLine, + column: lineInfo.column, + lineContent, + } +} + +/** + * Execute code in isolated-vm + */ +async function executeCode(request) { + const { code, params, envVars, contextVariables, timeoutMs, requestId } = request + const stdoutChunks = [] + let isolate = null + + try { + isolate = new ivm.Isolate({ memoryLimit: 128 }) + const context = await isolate.createContext() + const jail = context.global + + await jail.set('global', jail.derefInto()) + + const logCallback = new ivm.Callback((...args) => { + const message = args + .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) + .join(' ') + stdoutChunks.push(`${message}\n`) + }) + await jail.set('__log', logCallback) + + const errorCallback = new ivm.Callback((...args) => { + const message = args + .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) + .join(' ') + stdoutChunks.push(`ERROR: ${message}\n`) + }) + await jail.set('__error', errorCallback) + + await jail.set('params', new ivm.ExternalCopy(params).copyInto()) + await jail.set('environmentVariables', new ivm.ExternalCopy(envVars).copyInto()) + + for (const [key, value] of Object.entries(contextVariables)) { + await jail.set(key, new ivm.ExternalCopy(value).copyInto()) + } + + const fetchCallback = new ivm.Reference(async (url, optionsJson) => { + return new Promise((resolve) => { + const fetchId = ++fetchIdCounter + const timeout = setTimeout(() => { + if (pendingFetches.has(fetchId)) { + pendingFetches.delete(fetchId) + resolve(JSON.stringify({ error: 'Fetch request timed out' })) + } + }, FETCH_TIMEOUT_MS) + pendingFetches.set(fetchId, { resolve, timeout }) + if (process.send && process.connected) { + process.send({ type: 'fetch', fetchId, requestId, url, optionsJson }) + } else { + clearTimeout(timeout) + pendingFetches.delete(fetchId) + resolve(JSON.stringify({ error: 'Parent process disconnected' })) + } + }) + }) + await jail.set('__fetchRef', fetchCallback) + + const bootstrap = ` + // Set up console object + const console = { + log: (...args) => __log(...args), + error: (...args) => __error(...args), + warn: (...args) => __log('WARN:', ...args), + info: (...args) => __log(...args), + }; + + // Set up fetch function that uses the host's secure fetch + async function fetch(url, options) { + let optionsJson; + if (options) { + try { + optionsJson = JSON.stringify(options); + } catch { + throw new Error('fetch options must be JSON-serializable'); + } + } + const resultJson = await __fetchRef.apply(undefined, [url, optionsJson], { result: { promise: true } }); + let result; + try { + result = JSON.parse(resultJson); + } catch { + throw new Error('Invalid fetch response'); + } + + if (result.error) { + throw new Error(result.error); + } + + // Create a Response-like object + return { + ok: result.ok, + status: result.status, + statusText: result.statusText, + headers: { + get: (name) => result.headers[name.toLowerCase()] || null, + entries: () => Object.entries(result.headers), + }, + text: async () => result.body, + json: async () => { + try { + return JSON.parse(result.body); + } catch (e) { + throw new Error('Failed to parse response as JSON: ' + e.message); + } + }, + blob: async () => { throw new Error('blob() not supported in sandbox'); }, + arrayBuffer: async () => { throw new Error('arrayBuffer() not supported in sandbox'); }, + }; + } + + // Prevent access to dangerous globals with stronger protection + const undefined_globals = [ + 'Isolate', 'Context', 'Script', 'Module', 'Callback', 'Reference', + 'ExternalCopy', 'process', 'require', 'module', 'exports', '__dirname', '__filename' + ]; + for (const name of undefined_globals) { + try { + Object.defineProperty(global, name, { + value: undefined, + writable: false, + configurable: false + }); + } catch {} + } + ` + + const bootstrapScript = await isolate.compileScript(bootstrap) + await bootstrapScript.run(context) + + const wrappedCode = ` + (async () => { + try { + const __userResult = await (async () => { + ${code} + })(); + return JSON.stringify({ success: true, result: __userResult }); + } catch (error) { + // Capture full error details including stack trace + const errorInfo = { + message: error.message || String(error), + name: error.name || 'Error', + stack: error.stack || '' + }; + console.error(error.stack || error.message || error); + return JSON.stringify({ success: false, errorInfo }); + } + })() + ` + + const userScript = await isolate.compileScript(wrappedCode, { filename: 'user-function.js' }) + const resultJson = await userScript.run(context, { timeout: timeoutMs, promise: true }) + + let result = null + let error + + if (typeof resultJson === 'string') { + try { + const parsed = JSON.parse(resultJson) + if (parsed.success) { + result = parsed.result + } else if (parsed.errorInfo) { + error = convertToCompatibleError(parsed.errorInfo, code) + } else { + error = { message: 'Unknown error', name: 'Error' } + } + } catch { + result = resultJson + } + } + + const stdout = stdoutChunks.join('') + + if (error) { + return { result: null, stdout, error } + } + + return { result, stdout } + } catch (err) { + const stdout = stdoutChunks.join('') + + if (err instanceof Error) { + const errorInfo = { + message: err.message, + name: err.name, + stack: err.stack, + } + + if (err.message.includes('Script execution timed out')) { + return { + result: null, + stdout, + error: { + message: `Execution timed out after ${timeoutMs}ms`, + name: 'TimeoutError', + }, + } + } + + return { + result: null, + stdout, + error: convertToCompatibleError(errorInfo, code), + } + } + + return { + result: null, + stdout, + error: { + message: String(err), + name: 'Error', + line: 1, + lineContent: code.split('\n')[0]?.trim(), + }, + } + } finally { + if (isolate) { + isolate.dispose() + } + } +} + +process.on('message', async (msg) => { + try { + if (msg.type === 'execute') { + const result = await executeCode(msg.request) + if (process.send && process.connected) { + process.send({ type: 'result', executionId: msg.executionId, result }) + } + } else if (msg.type === 'fetchResponse') { + const pending = pendingFetches.get(msg.fetchId) + if (pending) { + clearTimeout(pending.timeout) + pendingFetches.delete(msg.fetchId) + pending.resolve(msg.response) + } + } + } catch (err) { + if (msg.type === 'execute' && process.send && process.connected) { + process.send({ + type: 'result', + executionId: msg.executionId, + result: { + result: null, + stdout: '', + error: { + message: err instanceof Error ? err.message : 'Worker error', + name: 'WorkerError', + }, + }, + }) + } + } +}) + +if (process.send) { + process.send({ type: 'ready' }) +} diff --git a/apps/sim/lib/execution/isolated-vm.ts b/apps/sim/lib/execution/isolated-vm.ts index 5411fc125..6af990eae 100644 --- a/apps/sim/lib/execution/isolated-vm.ts +++ b/apps/sim/lib/execution/isolated-vm.ts @@ -1,14 +1,12 @@ -import type ivm from 'isolated-vm' +import { type ChildProcess, spawn } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' import { validateProxyUrl } from '@/lib/core/security/input-validation' import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('IsolatedVMExecution') -async function loadIsolatedVM(): Promise { - const ivmModule = await import('isolated-vm') - return ivmModule.default -} - export interface IsolatedVMExecutionRequest { code: string params: Record @@ -33,367 +31,257 @@ export interface IsolatedVMError { lineContent?: string } -/** - * Number of lines added before user code in the wrapper. - * The wrapper structure is: - * Line 1: (empty - newline after template literal backtick) - * Line 2: (async () => { - * Line 3: try { - * Line 4: const __userResult = await (async () => { - * Line 5+: user code starts here - */ -const USER_CODE_START_LINE = 4 +interface PendingExecution { + resolve: (result: IsolatedVMExecutionResult) => void + timeout: ReturnType +} + +let worker: ChildProcess | null = null +let workerReady = false +let workerReadyPromise: Promise | null = null +let workerIdleTimeout: ReturnType | null = null +const pendingExecutions = new Map() +let executionIdCounter = 0 + +const WORKER_IDLE_TIMEOUT_MS = 60000 + +function cleanupWorker() { + if (workerIdleTimeout) { + clearTimeout(workerIdleTimeout) + workerIdleTimeout = null + } + if (worker) { + worker.kill() + worker = null + } + workerReady = false + workerReadyPromise = null +} + +function resetIdleTimeout() { + if (workerIdleTimeout) { + clearTimeout(workerIdleTimeout) + } + workerIdleTimeout = setTimeout(() => { + if (pendingExecutions.size === 0) { + logger.info('Cleaning up idle isolated-vm worker') + cleanupWorker() + } + }, WORKER_IDLE_TIMEOUT_MS) +} /** * Secure fetch wrapper that validates URLs to prevent SSRF attacks */ -async function secureFetch( - requestId: string, - url: string, - options?: RequestInit -): Promise<{ - ok: boolean - status: number - statusText: string - body: string - headers: Record -}> { +async function secureFetch(requestId: string, url: string, options?: RequestInit): Promise { const validation = validateProxyUrl(url) if (!validation.isValid) { logger.warn(`[${requestId}] Blocked fetch request due to SSRF validation`, { url: url.substring(0, 100), error: validation.error, }) - throw new Error(`Security Error: ${validation.error}`) + return JSON.stringify({ error: `Security Error: ${validation.error}` }) } - const response = await fetch(url, options) - const body = await response.text() - const headers: Record = {} - response.headers.forEach((value, key) => { - headers[key] = value + try { + const response = await fetch(url, options) + const body = await response.text() + const headers: Record = {} + response.headers.forEach((value, key) => { + headers[key] = value + }) + return JSON.stringify({ + ok: response.ok, + status: response.status, + statusText: response.statusText, + body, + headers, + }) + } catch (error: unknown) { + return JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown fetch error' }) + } +} + +/** + * Handle IPC messages from the Node.js worker + */ +function handleWorkerMessage(message: unknown) { + if (typeof message !== 'object' || message === null) return + const msg = message as Record + + if (msg.type === 'result') { + const pending = pendingExecutions.get(msg.executionId as number) + if (pending) { + clearTimeout(pending.timeout) + pendingExecutions.delete(msg.executionId as number) + pending.resolve(msg.result as IsolatedVMExecutionResult) + } + return + } + + if (msg.type === 'fetch') { + const { fetchId, requestId, url, optionsJson } = msg as { + fetchId: number + requestId: string + url: string + optionsJson?: string + } + let options: RequestInit | undefined + if (optionsJson) { + try { + options = JSON.parse(optionsJson) + } catch { + worker?.send({ + type: 'fetchResponse', + fetchId, + response: JSON.stringify({ error: 'Invalid fetch options JSON' }), + }) + return + } + } + secureFetch(requestId, url, options) + .then((response) => { + try { + worker?.send({ type: 'fetchResponse', fetchId, response }) + } catch (err) { + logger.error('Failed to send fetch response to worker', { err, fetchId }) + } + }) + .catch((err) => { + try { + worker?.send({ + type: 'fetchResponse', + fetchId, + response: JSON.stringify({ + error: err instanceof Error ? err.message : 'Fetch failed', + }), + }) + } catch (sendErr) { + logger.error('Failed to send fetch error to worker', { sendErr, fetchId }) + } + }) + } +} + +/** + * Start the Node.js worker process + */ +async function ensureWorker(): Promise { + if (workerReady && worker) return + if (workerReadyPromise) return workerReadyPromise + + workerReadyPromise = new Promise((resolve, reject) => { + const currentDir = path.dirname(fileURLToPath(import.meta.url)) + const workerPath = path.join(currentDir, 'isolated-vm-worker.cjs') + + if (!fs.existsSync(workerPath)) { + reject(new Error(`Worker file not found at ${workerPath}`)) + return + } + + worker = spawn(process.execPath, [workerPath], { + stdio: ['ignore', 'pipe', 'inherit', 'ipc'], + serialization: 'json', + }) + + worker.on('message', handleWorkerMessage) + + const startTimeout = setTimeout(() => { + worker?.kill() + worker = null + workerReady = false + workerReadyPromise = null + reject(new Error('Worker failed to start within timeout')) + }, 10000) + + const readyHandler = (message: unknown) => { + if ( + typeof message === 'object' && + message !== null && + (message as { type?: string }).type === 'ready' + ) { + workerReady = true + clearTimeout(startTimeout) + worker?.off('message', readyHandler) + resolve() + } + } + worker.on('message', readyHandler) + + worker.on('exit', (code) => { + logger.warn('Isolated-vm worker exited', { code }) + if (workerIdleTimeout) { + clearTimeout(workerIdleTimeout) + workerIdleTimeout = null + } + worker = null + workerReady = false + workerReadyPromise = null + for (const [id, pending] of pendingExecutions) { + clearTimeout(pending.timeout) + pending.resolve({ + result: null, + stdout: '', + error: { message: 'Worker process exited unexpectedly', name: 'WorkerError' }, + }) + pendingExecutions.delete(id) + } + }) }) - return { - ok: response.ok, - status: response.status, - statusText: response.statusText, - body, - headers, - } + return workerReadyPromise } /** - * Extract line and column from error stack or message - */ -function extractLineInfo(errorMessage: string, stack?: string): { line?: number; column?: number } { - if (stack) { - const stackMatch = stack.match(/(?:|user-function\.js):(\d+):(\d+)/) - if (stackMatch) { - return { - line: Number.parseInt(stackMatch[1], 10), - column: Number.parseInt(stackMatch[2], 10), - } - } - const atMatch = stack.match(/at\s+(?:|user-function\.js):(\d+):(\d+)/) - if (atMatch) { - return { - line: Number.parseInt(atMatch[1], 10), - column: Number.parseInt(atMatch[2], 10), - } - } - } - - const msgMatch = errorMessage.match(/:(\d+):(\d+)/) - if (msgMatch) { - return { - line: Number.parseInt(msgMatch[1], 10), - column: Number.parseInt(msgMatch[2], 10), - } - } - - return {} -} - -/** - * Convert isolated-vm error info to a format compatible with the route's error handling - */ -function convertToCompatibleError( - errorInfo: { - message: string - name: string - stack?: string - }, - userCode: string -): IsolatedVMError { - const { name } = errorInfo - let { message, stack } = errorInfo - - message = message - .replace(/\s*\[user-function\.js:\d+:\d+\]/g, '') - .replace(/\s*\[:\d+:\d+\]/g, '') - .replace(/\s*\(:\d+:\d+\)/g, '') - .trim() - - const lineInfo = extractLineInfo(errorInfo.message, stack) - - let userLine: number | undefined - let lineContent: string | undefined - - if (lineInfo.line !== undefined) { - userLine = lineInfo.line - USER_CODE_START_LINE - const codeLines = userCode.split('\n') - if (userLine > 0 && userLine <= codeLines.length) { - lineContent = codeLines[userLine - 1]?.trim() - } else if (userLine <= 0) { - userLine = 1 - lineContent = codeLines[0]?.trim() - } else { - userLine = codeLines.length - lineContent = codeLines[codeLines.length - 1]?.trim() - } - } - - if (stack) { - stack = stack.replace(/:(\d+):(\d+)/g, (_, line, col) => { - const adjustedLine = Number.parseInt(line, 10) - USER_CODE_START_LINE - return `user-function.js:${Math.max(1, adjustedLine)}:${col}` - }) - stack = stack.replace(/at :(\d+):(\d+)/g, (_, line, col) => { - const adjustedLine = Number.parseInt(line, 10) - USER_CODE_START_LINE - return `at user-function.js:${Math.max(1, adjustedLine)}:${col}` - }) - } - - return { - message, - name, - stack, - line: userLine, - column: lineInfo.column, - lineContent, - } -} - -/** - * Execute JavaScript code in an isolated V8 isolate + * Execute JavaScript code in an isolated V8 isolate via Node.js subprocess. + * The worker's V8 isolate enforces timeoutMs internally. The parent timeout + * (timeoutMs + 1000) is a safety buffer for IPC communication. */ export async function executeInIsolatedVM( req: IsolatedVMExecutionRequest ): Promise { - const { code, params, envVars, contextVariables, timeoutMs, requestId } = req + if (workerIdleTimeout) { + clearTimeout(workerIdleTimeout) + workerIdleTimeout = null + } - const stdoutChunks: string[] = [] - let isolate: ivm.Isolate | null = null - - const ivm = await loadIsolatedVM() - - try { - isolate = new ivm.Isolate({ memoryLimit: 128 }) - const context = await isolate.createContext() - - const jail = context.global - - await jail.set('global', jail.derefInto()) - - const logCallback = new ivm.Callback((...args: unknown[]) => { - const message = args - .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) - .join(' ') - stdoutChunks.push(`${message}\n`) - }) - await jail.set('__log', logCallback) - - const errorCallback = new ivm.Callback((...args: unknown[]) => { - const message = args - .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) - .join(' ') - logger.error(`[${requestId}] Code Console Error: ${message}`) - stdoutChunks.push(`ERROR: ${message}\n`) - }) - await jail.set('__error', errorCallback) - - await jail.set('params', new ivm.ExternalCopy(params).copyInto()) - - await jail.set('environmentVariables', new ivm.ExternalCopy(envVars).copyInto()) - - for (const [key, value] of Object.entries(contextVariables)) { - await jail.set(key, new ivm.ExternalCopy(value).copyInto()) - } - - const fetchCallback = new ivm.Reference(async (url: string, optionsJson?: string) => { - try { - const options = optionsJson ? JSON.parse(optionsJson) : undefined - const result = await secureFetch(requestId, url, options) - return JSON.stringify(result) - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : 'Unknown fetch error' - return JSON.stringify({ error: errorMessage }) - } - }) - await jail.set('__fetchRef', fetchCallback) - - const bootstrap = ` - // Set up console object - const console = { - log: (...args) => __log(...args), - error: (...args) => __error(...args), - warn: (...args) => __log('WARN:', ...args), - info: (...args) => __log(...args), - }; - - // Set up fetch function that uses the host's secure fetch - async function fetch(url, options) { - let optionsJson; - if (options) { - try { - optionsJson = JSON.stringify(options); - } catch { - throw new Error('fetch options must be JSON-serializable'); - } - } - const resultJson = await __fetchRef.apply(undefined, [url, optionsJson], { result: { promise: true } }); - let result; - try { - result = JSON.parse(resultJson); - } catch { - throw new Error('Invalid fetch response'); - } - - if (result.error) { - throw new Error(result.error); - } - - // Create a Response-like object - return { - ok: result.ok, - status: result.status, - statusText: result.statusText, - headers: { - get: (name) => result.headers[name.toLowerCase()] || null, - entries: () => Object.entries(result.headers), - }, - text: async () => result.body, - json: async () => { - try { - return JSON.parse(result.body); - } catch (e) { - throw new Error('Failed to parse response as JSON: ' + e.message); - } - }, - blob: async () => { throw new Error('blob() not supported in sandbox'); }, - arrayBuffer: async () => { throw new Error('arrayBuffer() not supported in sandbox'); }, - }; - } - - // Prevent access to dangerous globals with stronger protection - const undefined_globals = [ - 'Isolate', 'Context', 'Script', 'Module', 'Callback', 'Reference', - 'ExternalCopy', 'process', 'require', 'module', 'exports', '__dirname', '__filename' - ]; - for (const name of undefined_globals) { - try { - Object.defineProperty(global, name, { - value: undefined, - writable: false, - configurable: false - }); - } catch {} - } - ` - - const bootstrapScript = await isolate.compileScript(bootstrap) - await bootstrapScript.run(context) - - const wrappedCode = ` - (async () => { - try { - const __userResult = await (async () => { - ${code} - })(); - return JSON.stringify({ success: true, result: __userResult }); - } catch (error) { - // Capture full error details including stack trace - const errorInfo = { - message: error.message || String(error), - name: error.name || 'Error', - stack: error.stack || '' - }; - console.error(error.stack || error.message || error); - return JSON.stringify({ success: false, errorInfo }); - } - })() - ` - - const userScript = await isolate.compileScript(wrappedCode, { filename: 'user-function.js' }) - const resultJson = await userScript.run(context, { timeout: timeoutMs, promise: true }) - - let result: unknown = null - let error: IsolatedVMError | undefined - - if (typeof resultJson === 'string') { - try { - const parsed = JSON.parse(resultJson) - if (parsed.success) { - result = parsed.result - } else if (parsed.errorInfo) { - error = convertToCompatibleError(parsed.errorInfo, code) - } else { - error = { message: 'Unknown error', name: 'Error' } - } - } catch { - result = resultJson - } - } - - const stdout = stdoutChunks.join('') - - if (error) { - return { result: null, stdout, error } - } - - return { result, stdout } - } catch (err: unknown) { - const stdout = stdoutChunks.join('') - - if (err instanceof Error) { - const errorInfo = { - message: err.message, - name: err.name, - stack: err.stack, - } - - if (err.message.includes('Script execution timed out')) { - return { - result: null, - stdout, - error: { - message: `Execution timed out after ${timeoutMs}ms`, - name: 'TimeoutError', - }, - } - } - - return { - result: null, - stdout, - error: convertToCompatibleError(errorInfo, code), - } - } + await ensureWorker() + if (!worker) { return { result: null, - stdout, - error: { - message: String(err), - name: 'Error', - line: 1, - lineContent: code.split('\n')[0]?.trim(), - }, - } - } finally { - if (isolate) { - isolate.dispose() + stdout: '', + error: { message: 'Failed to start isolated-vm worker', name: 'WorkerError' }, } } + + const executionId = ++executionIdCounter + + return new Promise((resolve) => { + const timeout = setTimeout(() => { + pendingExecutions.delete(executionId) + resolve({ + result: null, + stdout: '', + error: { message: `Execution timed out after ${req.timeoutMs}ms`, name: 'TimeoutError' }, + }) + }, req.timeoutMs + 1000) + + pendingExecutions.set(executionId, { resolve, timeout }) + + try { + worker!.send({ type: 'execute', executionId, request: req }) + } catch { + clearTimeout(timeout) + pendingExecutions.delete(executionId) + resolve({ + result: null, + stdout: '', + error: { message: 'Failed to send execution request to worker', name: 'WorkerError' }, + }) + return + } + + resetIdleTimeout() + }) } diff --git a/bun.lock b/bun.lock index 004b8e3d5..fa7ace079 100644 --- a/bun.lock +++ b/bun.lock @@ -1651,6 +1651,8 @@ "buildcheck": ["buildcheck@0.0.7", "", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="], + "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], "c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="], @@ -3781,6 +3783,8 @@ "body-parser/iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="], + "bun-types/@types/node": ["@types/node@24.2.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="], + "c12/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "c12/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], @@ -4237,6 +4241,8 @@ "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "bun-types/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], + "c12/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "chrome-launcher/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], diff --git a/docker/app.Dockerfile b/docker/app.Dockerfile index ba1236860..4d5a7be32 100644 --- a/docker/app.Dockerfile +++ b/docker/app.Dockerfile @@ -1,24 +1,31 @@ # ======================================== -# Base Stage: Alpine Linux with Bun +# Base Stage: Debian-based Bun # ======================================== -FROM oven/bun:1.3.3-alpine AS base +FROM oven/bun:1.3.3-slim AS base # ======================================== # Dependencies Stage: Install Dependencies # ======================================== FROM base AS deps -RUN apk add --no-cache libc6-compat WORKDIR /app +# Install Node.js 22 for isolated-vm compilation (requires node-gyp and V8) +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 make g++ curl ca-certificates \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + COPY package.json bun.lock turbo.json ./ RUN mkdir -p apps packages/db COPY apps/sim/package.json ./apps/sim/package.json COPY packages/db/package.json ./packages/db/package.json -# Install turbo globally and dependencies with cache mount for faster builds +# Install turbo globally, then dependencies, then rebuild isolated-vm for Node.js RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \ bun install -g turbo && \ - bun install --omit=dev --ignore-scripts + HUSKY=0 bun install --omit=dev --ignore-scripts && \ + cd $(readlink -f node_modules/isolated-vm) && npx node-gyp rebuild --release && cd /app # ======================================== # Builder Stage: Build the Application @@ -51,7 +58,7 @@ COPY packages ./packages # Required for standalone nextjs build WORKDIR /app/apps/sim RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \ - bun install sharp + HUSKY=0 bun install sharp ENV NEXT_TELEMETRY_DISABLED=1 \ VERCEL_TELEMETRY_DISABLED=1 \ @@ -78,21 +85,30 @@ RUN bun run build FROM base AS runner WORKDIR /app -# Install Python and dependencies for guardrails PII detection (cached separately) -# Also install ffmpeg for audio/video processing in STT -RUN apk add --no-cache python3 py3-pip bash ffmpeg +# Install Node.js 22 (for isolated-vm worker), Python, and other runtime dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 python3-pip python3-venv bash ffmpeg curl ca-certificates \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* ENV NODE_ENV=production -# Create non-root user and group (cached separately) -RUN addgroup -g 1001 -S nodejs && \ - adduser -S nextjs -u 1001 +# Create non-root user and group +RUN groupadd -g 1001 nodejs && \ + useradd -u 1001 -g nodejs nextjs -# Copy application artifacts from builder (these change on every build) +# Copy application artifacts from builder COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/public ./apps/sim/public COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/.next/static ./apps/sim/.next/static +# Copy isolated-vm native module (compiled for Node.js in deps stage) +COPY --from=deps --chown=nextjs:nodejs /app/node_modules/isolated-vm ./node_modules/isolated-vm + +# Copy the isolated-vm worker script +COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/execution/isolated-vm-worker.cjs ./apps/sim/lib/execution/isolated-vm-worker.cjs + # Guardrails setup (files need to be owned by nextjs for runtime) COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/guardrails/setup.sh ./apps/sim/lib/guardrails/setup.sh COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/guardrails/requirements.txt ./apps/sim/lib/guardrails/requirements.txt From 57e6a0b6218d5edd6a46927e9cd6b49cca6933ba Mon Sep 17 00:00:00 2001 From: Adam Gough <77861281+aadamgough@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:16:50 -0800 Subject: [PATCH 12/17] fixed jira output (#2392) --- apps/sim/blocks/blocks/jira.ts | 1 - apps/sim/tools/jira/add_comment.ts | 13 ++++--------- apps/sim/tools/jira/add_watcher.ts | 12 +++--------- apps/sim/tools/jira/add_worklog.ts | 13 ++++--------- apps/sim/tools/jira/assign_issue.ts | 11 +++-------- apps/sim/tools/jira/bulk_read.ts | 9 +++------ apps/sim/tools/jira/create_issue_link.ts | 14 +++++--------- apps/sim/tools/jira/delete_attachment.ts | 10 ++-------- apps/sim/tools/jira/delete_comment.ts | 11 +++-------- apps/sim/tools/jira/delete_issue.ts | 10 ++-------- apps/sim/tools/jira/delete_issue_link.ts | 10 ++-------- apps/sim/tools/jira/delete_worklog.ts | 11 +++-------- apps/sim/tools/jira/get_attachments.ts | 12 +++++------- apps/sim/tools/jira/get_comments.ts | 13 ++++++------- apps/sim/tools/jira/get_worklogs.ts | 14 +++++++------- apps/sim/tools/jira/remove_watcher.ts | 12 +++--------- apps/sim/tools/jira/retrieve.ts | 16 +++++++--------- apps/sim/tools/jira/search_issues.ts | 15 +++++++-------- apps/sim/tools/jira/transition_issue.ts | 12 +++--------- apps/sim/tools/jira/update.ts | 12 +++--------- apps/sim/tools/jira/update_comment.ts | 13 ++++--------- apps/sim/tools/jira/update_worklog.ts | 12 +++--------- apps/sim/tools/jira/write.ts | 13 ++++--------- 23 files changed, 86 insertions(+), 183 deletions(-) diff --git a/apps/sim/blocks/blocks/jira.ts b/apps/sim/blocks/blocks/jira.ts index 54885e711..98cd7166b 100644 --- a/apps/sim/blocks/blocks/jira.ts +++ b/apps/sim/blocks/blocks/jira.ts @@ -762,7 +762,6 @@ export const JiraBlock: BlockConfig = { outputs: { // Common outputs across all Jira operations ts: { type: 'string', description: 'Timestamp of the operation' }, - success: { type: 'boolean', description: 'Whether the operation was successful' }, // jira_retrieve (read) outputs issueKey: { type: 'string', description: 'Issue key (e.g., PROJ-123)' }, diff --git a/apps/sim/tools/jira/add_comment.ts b/apps/sim/tools/jira/add_comment.ts index 94d990356..9d87cc8e5 100644 --- a/apps/sim/tools/jira/add_comment.ts +++ b/apps/sim/tools/jira/add_comment.ts @@ -163,14 +163,9 @@ export const jiraAddCommentTool: ToolConfig = }, outputs: { - success: { - type: 'boolean', - description: 'Operation success status', - }, - output: { - type: 'object', - description: - 'Updated Jira issue details with timestamp, issue key, summary, and success status', - }, + ts: { type: 'string', description: 'Timestamp of the operation' }, + issueKey: { type: 'string', description: 'Updated issue key (e.g., PROJ-123)' }, + summary: { type: 'string', description: 'Issue summary after update' }, }, } diff --git a/apps/sim/tools/jira/update_comment.ts b/apps/sim/tools/jira/update_comment.ts index 0d00d3578..b469e24f0 100644 --- a/apps/sim/tools/jira/update_comment.ts +++ b/apps/sim/tools/jira/update_comment.ts @@ -170,14 +170,9 @@ export const jiraUpdateCommentTool: ToolConfig = { }, outputs: { - success: { - type: 'boolean', - description: 'Operation success status', - }, - output: { - type: 'object', - description: - 'Created Jira issue details with timestamp, issue key, summary, success status, and URL', - }, + ts: { type: 'string', description: 'Timestamp of the operation' }, + issueKey: { type: 'string', description: 'Created issue key (e.g., PROJ-123)' }, + summary: { type: 'string', description: 'Issue summary' }, + url: { type: 'string', description: 'URL to the created issue' }, }, } From a5b714837504112c8a3fa0a7c3e769d1cc50a0e2 Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 18:18:26 -0800 Subject: [PATCH 13/17] fix(node): use node subprocess explicitly (#2391) * fix(node): use node subprocess explicitly * add explicit documentation * fixed build --- README.md | 1 + apps/sim/lib/execution/isolated-vm.ts | 110 ++++++++++++++++---------- 2 files changed, 68 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index aa25eda53..bed751ac4 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ When running with Docker, use `host.docker.internal` if vLLM is on your host mac **Requirements:** - [Bun](https://bun.sh/) runtime +- [Node.js](https://nodejs.org/) v20+ (required for sandboxed code execution) - PostgreSQL 12+ with [pgvector extension](https://github.com/pgvector/pgvector) (required for AI embeddings) **Note:** Sim uses vector embeddings for AI features like knowledge bases and semantic search, which requires the `pgvector` PostgreSQL extension. diff --git a/apps/sim/lib/execution/isolated-vm.ts b/apps/sim/lib/execution/isolated-vm.ts index 6af990eae..142f59ff3 100644 --- a/apps/sim/lib/execution/isolated-vm.ts +++ b/apps/sim/lib/execution/isolated-vm.ts @@ -1,4 +1,4 @@ -import { type ChildProcess, spawn } from 'node:child_process' +import { type ChildProcess, execSync } from 'node:child_process' import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' @@ -7,6 +7,19 @@ import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('IsolatedVMExecution') +let nodeAvailable: boolean | null = null + +function checkNodeAvailable(): boolean { + if (nodeAvailable !== null) return nodeAvailable + try { + execSync('node --version', { stdio: 'ignore' }) + nodeAvailable = true + } catch { + nodeAvailable = false + } + return nodeAvailable +} + export interface IsolatedVMExecutionRequest { code: string params: Record @@ -171,6 +184,16 @@ async function ensureWorker(): Promise { if (workerReadyPromise) return workerReadyPromise workerReadyPromise = new Promise((resolve, reject) => { + if (!checkNodeAvailable()) { + reject( + new Error( + 'Node.js is required for code execution but was not found. ' + + 'Please install Node.js (v20+) from https://nodejs.org' + ) + ) + return + } + const currentDir = path.dirname(fileURLToPath(import.meta.url)) const workerPath = path.join(currentDir, 'isolated-vm-worker.cjs') @@ -179,53 +202,54 @@ async function ensureWorker(): Promise { return } - worker = spawn(process.execPath, [workerPath], { - stdio: ['ignore', 'pipe', 'inherit', 'ipc'], - serialization: 'json', - }) + import('node:child_process').then(({ spawn }) => { + worker = spawn('node', [workerPath], { + stdio: ['ignore', 'pipe', 'inherit', 'ipc'], + serialization: 'json', + }) - worker.on('message', handleWorkerMessage) + worker.on('message', handleWorkerMessage) - const startTimeout = setTimeout(() => { - worker?.kill() - worker = null - workerReady = false - workerReadyPromise = null - reject(new Error('Worker failed to start within timeout')) - }, 10000) + const startTimeout = setTimeout(() => { + worker?.kill() + worker = null + workerReady = false + workerReadyPromise = null + reject(new Error('Worker failed to start within timeout')) + }, 10000) - const readyHandler = (message: unknown) => { - if ( - typeof message === 'object' && - message !== null && - (message as { type?: string }).type === 'ready' - ) { - workerReady = true - clearTimeout(startTimeout) - worker?.off('message', readyHandler) - resolve() + const readyHandler = (message: unknown) => { + if ( + typeof message === 'object' && + message !== null && + (message as { type?: string }).type === 'ready' + ) { + workerReady = true + clearTimeout(startTimeout) + worker?.off('message', readyHandler) + resolve() + } } - } - worker.on('message', readyHandler) + worker.on('message', readyHandler) - worker.on('exit', (code) => { - logger.warn('Isolated-vm worker exited', { code }) - if (workerIdleTimeout) { - clearTimeout(workerIdleTimeout) - workerIdleTimeout = null - } - worker = null - workerReady = false - workerReadyPromise = null - for (const [id, pending] of pendingExecutions) { - clearTimeout(pending.timeout) - pending.resolve({ - result: null, - stdout: '', - error: { message: 'Worker process exited unexpectedly', name: 'WorkerError' }, - }) - pendingExecutions.delete(id) - } + worker.on('exit', () => { + if (workerIdleTimeout) { + clearTimeout(workerIdleTimeout) + workerIdleTimeout = null + } + worker = null + workerReady = false + workerReadyPromise = null + for (const [id, pending] of pendingExecutions) { + clearTimeout(pending.timeout) + pending.resolve({ + result: null, + stdout: '', + error: { message: 'Worker process exited unexpectedly', name: 'WorkerError' }, + }) + pendingExecutions.delete(id) + } + }) }) }) From 12d42e29ac738daeb8f7f075942063977eec732e Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 18:30:48 -0800 Subject: [PATCH 14/17] fix(docs): regen docs (#2393) --- apps/docs/components/ui/icon-mapping.ts | 206 +++++++++++----------- apps/docs/content/docs/en/tools/jira.mdx | 119 ++++++++----- apps/docs/content/docs/en/tools/slack.mdx | 8 +- 3 files changed, 183 insertions(+), 150 deletions(-) diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index 77b3769a9..977138187 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -119,116 +119,116 @@ import { type IconComponent = ComponentType> export const blockTypeToIconMap: Record = { - zoom: ZoomIcon, - zep: ZepIcon, + calendly: CalendlyIcon, + mailchimp: MailchimpIcon, + postgresql: PostgresIcon, + twilio_voice: TwilioIcon, + elasticsearch: ElasticsearchIcon, + rds: RDSIcon, + translate: TranslateIcon, + dynamodb: DynamoDBIcon, + wordpress: WordpressIcon, + tavily: TavilyIcon, zendesk: ZendeskIcon, youtube: YouTubeIcon, - x: xIcon, - wordpress: WordpressIcon, - wikipedia: WikipediaIcon, - whatsapp: WhatsAppIcon, - webflow: WebflowIcon, - wealthbox: WealthboxIcon, - vision: EyeIcon, - video_generator: VideoIcon, - typeform: TypeformIcon, - twilio_voice: TwilioIcon, - twilio_sms: TwilioIcon, - tts: TTSIcon, - trello: TrelloIcon, - translate: TranslateIcon, - thinking: BrainIcon, - telegram: TelegramIcon, - tavily: TavilyIcon, supabase: SupabaseIcon, - stt: STTIcon, - stripe: StripeIcon, - stagehand: StagehandIcon, - ssh: SshIcon, - sqs: SQSIcon, - spotify: SpotifyIcon, - smtp: SmtpIcon, - slack: SlackIcon, - shopify: ShopifyIcon, - sharepoint: MicrosoftSharepointIcon, - sftp: SftpIcon, - serper: SerperIcon, - sentry: SentryIcon, - sendgrid: SendgridIcon, - search: SearchIcon, - salesforce: SalesforceIcon, - s3: S3Icon, - resend: ResendIcon, - reddit: RedditIcon, - rds: RDSIcon, - qdrant: QdrantIcon, - posthog: PosthogIcon, - postgresql: PostgresIcon, - polymarket: PolymarketIcon, - pipedrive: PipedriveIcon, - pinecone: PineconeIcon, - perplexity: PerplexityIcon, - parallel_ai: ParallelIcon, - outlook: OutlookIcon, - openai: OpenAIIcon, - onedrive: MicrosoftOneDriveIcon, - notion: NotionIcon, - neo4j: Neo4jIcon, - mysql: MySQLIcon, - mongodb: MongoDBIcon, - mistral_parse: MistralIcon, - microsoft_teams: MicrosoftTeamsIcon, - microsoft_planner: MicrosoftPlannerIcon, - microsoft_excel: MicrosoftExcelIcon, - memory: BrainIcon, - mem0: Mem0Icon, - mailgun: MailgunIcon, - mailchimp: MailchimpIcon, - linkup: LinkupIcon, - linkedin: LinkedInIcon, - linear: LinearIcon, - knowledge: PackageSearchIcon, - kalshi: KalshiIcon, - jira: JiraIcon, - jina: JinaAIIcon, - intercom: IntercomIcon, - incidentio: IncidentioIcon, - image_generator: ImageIcon, - hunter: HunterIOIcon, - huggingface: HuggingFaceIcon, - hubspot: HubspotIcon, - grafana: GrafanaIcon, - google_vault: GoogleVaultIcon, - google_slides: GoogleSlidesIcon, - google_sheets: GoogleSheetsIcon, - google_groups: GoogleGroupsIcon, - google_forms: GoogleFormsIcon, - google_drive: GoogleDriveIcon, - google_docs: GoogleDocsIcon, - google_calendar: GoogleCalendarIcon, - google_search: GoogleIcon, - gmail: GmailIcon, - gitlab: GitLabIcon, - github: GithubIcon, - firecrawl: FirecrawlIcon, - file: DocumentIcon, - exa: ExaAIIcon, - elevenlabs: ElevenLabsIcon, - elasticsearch: ElasticsearchIcon, - dynamodb: DynamoDBIcon, - duckduckgo: DuckDuckGoIcon, - dropbox: DropboxIcon, - discord: DiscordIcon, - datadog: DatadogIcon, - cursor: CursorIcon, + vision: EyeIcon, + zoom: ZoomIcon, confluence: ConfluenceIcon, - clay: ClayIcon, - calendly: CalendlyIcon, - browser_use: BrowserUseIcon, - asana: AsanaIcon, arxiv: ArxivIcon, + webflow: WebflowIcon, + pinecone: PineconeIcon, apollo: ApolloIcon, + whatsapp: WhatsAppIcon, + typeform: TypeformIcon, + qdrant: QdrantIcon, + shopify: ShopifyIcon, + asana: AsanaIcon, + sqs: SQSIcon, apify: ApifyIcon, + memory: BrainIcon, + gitlab: GitLabIcon, + polymarket: PolymarketIcon, + serper: SerperIcon, + linear: LinearIcon, + exa: ExaAIIcon, + telegram: TelegramIcon, + salesforce: SalesforceIcon, + hubspot: HubspotIcon, + hunter: HunterIOIcon, + linkup: LinkupIcon, + mongodb: MongoDBIcon, airtable: AirtableIcon, + discord: DiscordIcon, ahrefs: AhrefsIcon, + neo4j: Neo4jIcon, + tts: TTSIcon, + jina: JinaAIIcon, + google_docs: GoogleDocsIcon, + perplexity: PerplexityIcon, + google_search: GoogleIcon, + x: xIcon, + kalshi: KalshiIcon, + google_calendar: GoogleCalendarIcon, + zep: ZepIcon, + posthog: PosthogIcon, + grafana: GrafanaIcon, + google_slides: GoogleSlidesIcon, + microsoft_planner: MicrosoftPlannerIcon, + thinking: BrainIcon, + pipedrive: PipedriveIcon, + dropbox: DropboxIcon, + stagehand: StagehandIcon, + google_forms: GoogleFormsIcon, + file: DocumentIcon, + mistral_parse: MistralIcon, + gmail: GmailIcon, + openai: OpenAIIcon, + outlook: OutlookIcon, + incidentio: IncidentioIcon, + onedrive: MicrosoftOneDriveIcon, + resend: ResendIcon, + google_vault: GoogleVaultIcon, + sharepoint: MicrosoftSharepointIcon, + huggingface: HuggingFaceIcon, + sendgrid: SendgridIcon, + video_generator: VideoIcon, + smtp: SmtpIcon, + google_groups: GoogleGroupsIcon, + mailgun: MailgunIcon, + clay: ClayIcon, + jira: JiraIcon, + search: SearchIcon, + linkedin: LinkedInIcon, + wealthbox: WealthboxIcon, + notion: NotionIcon, + elevenlabs: ElevenLabsIcon, + microsoft_teams: MicrosoftTeamsIcon, + github: GithubIcon, + sftp: SftpIcon, + ssh: SshIcon, + google_drive: GoogleDriveIcon, + sentry: SentryIcon, + reddit: RedditIcon, + parallel_ai: ParallelIcon, + spotify: SpotifyIcon, + stripe: StripeIcon, + s3: S3Icon, + trello: TrelloIcon, + mem0: Mem0Icon, + knowledge: PackageSearchIcon, + intercom: IntercomIcon, + twilio_sms: TwilioIcon, + duckduckgo: DuckDuckGoIcon, + slack: SlackIcon, + datadog: DatadogIcon, + microsoft_excel: MicrosoftExcelIcon, + image_generator: ImageIcon, + google_sheets: GoogleSheetsIcon, + wikipedia: WikipediaIcon, + cursor: CursorIcon, + firecrawl: FirecrawlIcon, + mysql: MySQLIcon, + browser_use: BrowserUseIcon, + stt: STTIcon, } diff --git a/apps/docs/content/docs/en/tools/jira.mdx b/apps/docs/content/docs/en/tools/jira.mdx index f9ba95657..ddd28524c 100644 --- a/apps/docs/content/docs/en/tools/jira.mdx +++ b/apps/docs/content/docs/en/tools/jira.mdx @@ -51,8 +51,13 @@ Retrieve detailed information about a specific Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Jira issue details with issue key, summary, description, created and updated timestamps | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key \(e.g., PROJ-123\) | +| `summary` | string | Issue summary | +| `description` | json | Issue description content | +| `created` | string | Issue creation timestamp | +| `updated` | string | Issue last updated timestamp | +| `issue` | json | Complete issue object with all fields | ### `jira_update` @@ -76,8 +81,9 @@ Update a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Updated Jira issue details with timestamp, issue key, summary, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Updated issue key \(e.g., PROJ-123\) | +| `summary` | string | Issue summary after update | ### `jira_write` @@ -100,8 +106,10 @@ Write a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Created Jira issue details with timestamp, issue key, summary, success status, and URL | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Created issue key \(e.g., PROJ-123\) | +| `summary` | string | Issue summary | +| `url` | string | URL to the created issue | ### `jira_bulk_read` @@ -119,8 +127,7 @@ Retrieve multiple Jira issues in bulk | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | array | Array of Jira issues with summary, description, created and updated timestamps | +| `issues` | array | Array of Jira issues with ts, summary, description, created, and updated timestamps | ### `jira_delete_issue` @@ -139,8 +146,8 @@ Delete a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Deleted issue details with timestamp, issue key, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Deleted issue key | ### `jira_assign_issue` @@ -159,8 +166,9 @@ Assign a Jira issue to a user | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Assignment details with timestamp, issue key, assignee ID, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key that was assigned | +| `assigneeId` | string | Account ID of the assignee | ### `jira_transition_issue` @@ -180,8 +188,9 @@ Move a Jira issue between workflow statuses (e.g., To Do -> In Progress) | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Transition details with timestamp, issue key, transition ID, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key that was transitioned | +| `transitionId` | string | Applied transition ID | ### `jira_search_issues` @@ -202,8 +211,11 @@ Search for Jira issues using JQL (Jira Query Language) | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Search results with timestamp, total count, pagination details, and array of matching issues | +| `ts` | string | Timestamp of the operation | +| `total` | number | Total number of matching issues | +| `startAt` | number | Pagination start index | +| `maxResults` | number | Maximum results per page | +| `issues` | array | Array of matching issues with key, summary, status, assignee, created, updated | ### `jira_add_comment` @@ -222,8 +234,10 @@ Add a comment to a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Comment details with timestamp, issue key, comment ID, body, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key the comment was added to | +| `commentId` | string | Created comment ID | +| `body` | string | Comment text content | ### `jira_get_comments` @@ -243,8 +257,10 @@ Get all comments from a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Comments data with timestamp, issue key, total count, and array of comments | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `total` | number | Total number of comments | +| `comments` | array | Array of comments with id, author, body, created, updated | ### `jira_update_comment` @@ -264,8 +280,10 @@ Update an existing comment on a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Updated comment details with timestamp, issue key, comment ID, body text, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `commentId` | string | Updated comment ID | +| `body` | string | Updated comment text | ### `jira_delete_comment` @@ -284,8 +302,9 @@ Delete a comment from a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Deletion details with timestamp, issue key, comment ID, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `commentId` | string | Deleted comment ID | ### `jira_get_attachments` @@ -303,8 +322,9 @@ Get all attachments from a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Attachments data with timestamp, issue key, and array of attachments | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `attachments` | array | Array of attachments with id, filename, size, mimeType, created, author | ### `jira_delete_attachment` @@ -322,8 +342,8 @@ Delete an attachment from a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Deletion details with timestamp, attachment ID, and success status | +| `ts` | string | Timestamp of the operation | +| `attachmentId` | string | Deleted attachment ID | ### `jira_add_worklog` @@ -344,8 +364,10 @@ Add a time tracking worklog entry to a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Worklog details with timestamp, issue key, worklog ID, time spent in seconds, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key the worklog was added to | +| `worklogId` | string | Created worklog ID | +| `timeSpentSeconds` | number | Time spent in seconds | ### `jira_get_worklogs` @@ -365,8 +387,10 @@ Get all worklog entries from a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Worklogs data with timestamp, issue key, total count, and array of worklogs | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `total` | number | Total number of worklogs | +| `worklogs` | array | Array of worklogs with id, author, timeSpentSeconds, timeSpent, comment, created, updated, started | ### `jira_update_worklog` @@ -388,8 +412,9 @@ Update an existing worklog entry on a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Worklog update details with timestamp, issue key, worklog ID, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `worklogId` | string | Updated worklog ID | ### `jira_delete_worklog` @@ -408,8 +433,9 @@ Delete a worklog entry from a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Deletion details with timestamp, issue key, worklog ID, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `worklogId` | string | Deleted worklog ID | ### `jira_create_issue_link` @@ -430,8 +456,11 @@ Create a link relationship between two Jira issues | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Issue link details with timestamp, inward issue key, outward issue key, link type, and success status | +| `ts` | string | Timestamp of the operation | +| `inwardIssue` | string | Inward issue key | +| `outwardIssue` | string | Outward issue key | +| `linkType` | string | Type of issue link | +| `linkId` | string | Created link ID | ### `jira_delete_issue_link` @@ -449,8 +478,8 @@ Delete a link between two Jira issues | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Deletion details with timestamp, link ID, and success status | +| `ts` | string | Timestamp of the operation | +| `linkId` | string | Deleted link ID | ### `jira_add_watcher` @@ -469,8 +498,9 @@ Add a watcher to a Jira issue to receive notifications about updates | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Watcher details with timestamp, issue key, watcher account ID, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `watcherAccountId` | string | Added watcher account ID | ### `jira_remove_watcher` @@ -489,8 +519,9 @@ Remove a watcher from a Jira issue | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Removal details with timestamp, issue key, watcher account ID, and success status | +| `ts` | string | Timestamp of the operation | +| `issueKey` | string | Issue key | +| `watcherAccountId` | string | Removed watcher account ID | diff --git a/apps/docs/content/docs/en/tools/slack.mdx b/apps/docs/content/docs/en/tools/slack.mdx index daf9a92aa..3ebb64d63 100644 --- a/apps/docs/content/docs/en/tools/slack.mdx +++ b/apps/docs/content/docs/en/tools/slack.mdx @@ -56,7 +56,7 @@ Integrate Slack into the workflow. Can send, update, and delete messages, create ### `slack_message` -Send messages to Slack channels or users through the Slack API. Supports Slack mrkdwn formatting. +Send messages to Slack channels or direct messages. Supports Slack mrkdwn formatting. #### Input @@ -64,7 +64,8 @@ Send messages to Slack channels or users through the Slack API. Supports Slack m | --------- | ---- | -------- | ----------- | | `authMethod` | string | No | Authentication method: oauth or bot_token | | `botToken` | string | No | Bot token for Custom Bot | -| `channel` | string | Yes | Target Slack channel \(e.g., #general\) | +| `channel` | string | No | Target Slack channel \(e.g., #general\) | +| `userId` | string | No | Target Slack user ID for direct messages \(e.g., U1234567890\) | | `text` | string | Yes | Message text to send \(supports Slack mrkdwn formatting\) | | `thread_ts` | string | No | Thread timestamp to reply to \(creates thread reply\) | | `files` | file[] | No | Files to attach to the message | @@ -111,7 +112,8 @@ Read the latest messages from Slack channels. Retrieve conversation history with | --------- | ---- | -------- | ----------- | | `authMethod` | string | No | Authentication method: oauth or bot_token | | `botToken` | string | No | Bot token for Custom Bot | -| `channel` | string | Yes | Slack channel to read messages from \(e.g., #general\) | +| `channel` | string | No | Slack channel to read messages from \(e.g., #general\) | +| `userId` | string | No | User ID for DM conversation \(e.g., U1234567890\) | | `limit` | number | No | Number of messages to retrieve \(default: 10, max: 100\) | | `oldest` | string | No | Start of time range \(timestamp\) | | `latest` | string | No | End of time range \(timestamp\) | From ab30d370204c9a89dd5e44ee1e57703c32771d41 Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 18:45:19 -0800 Subject: [PATCH 15/17] feat(i18n): update translations (#2395) Co-authored-by: waleedlatif1 --- apps/docs/content/docs/de/tools/jira.mdx | 119 ++++++++++++++-------- apps/docs/content/docs/de/tools/slack.mdx | 16 +-- apps/docs/content/docs/es/tools/jira.mdx | 119 ++++++++++++++-------- apps/docs/content/docs/es/tools/slack.mdx | 10 +- apps/docs/content/docs/fr/tools/jira.mdx | 119 ++++++++++++++-------- apps/docs/content/docs/fr/tools/slack.mdx | 12 ++- apps/docs/content/docs/ja/tools/jira.mdx | 119 ++++++++++++++-------- apps/docs/content/docs/ja/tools/slack.mdx | 8 +- apps/docs/content/docs/zh/tools/jira.mdx | 119 ++++++++++++++-------- apps/docs/content/docs/zh/tools/slack.mdx | 16 +-- apps/docs/i18n.lock | 50 ++++----- 11 files changed, 436 insertions(+), 271 deletions(-) diff --git a/apps/docs/content/docs/de/tools/jira.mdx b/apps/docs/content/docs/de/tools/jira.mdx index 1fe842f31..2d94828c3 100644 --- a/apps/docs/content/docs/de/tools/jira.mdx +++ b/apps/docs/content/docs/de/tools/jira.mdx @@ -48,8 +48,13 @@ Ruft detaillierte Informationen zu einem bestimmten Jira-Issue ab | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Jira-Issue-Details mit Issue-Key, Zusammenfassung, Beschreibung, Erstellungs- und Aktualisierungszeitstempeln | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key \(z.B. PROJ-123\) | +| `summary` | string | Issue-Zusammenfassung | +| `description` | json | Inhalt der Issue-Beschreibung | +| `created` | string | Zeitstempel der Issue-Erstellung | +| `updated` | string | Zeitstempel der letzten Issue-Aktualisierung | +| `issue` | json | Vollständiges Issue-Objekt mit allen Feldern | ### `jira_update` @@ -73,8 +78,9 @@ Ein Jira-Issue aktualisieren | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Aktualisierte Jira-Issue-Details mit Zeitstempel, Issue-Key, Zusammenfassung und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Aktualisierter Issue-Key \(z.B. PROJ-123\) | +| `summary` | string | Issue-Zusammenfassung nach der Aktualisierung | ### `jira_write` @@ -97,8 +103,10 @@ Ein Jira-Issue erstellen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Erstellte Jira-Issue-Details mit Zeitstempel, Issue-Key, Zusammenfassung, Erfolgsstatus und URL | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Erstellter Issue-Key \(z.B. PROJ-123\) | +| `summary` | string | Issue-Zusammenfassung | +| `url` | string | URL zum erstellten Issue | ### `jira_bulk_read` @@ -116,8 +124,7 @@ Mehrere Jira-Issues in Masse abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | array | Array von Jira-Issues mit Zusammenfassung, Beschreibung, Erstellungs- und Aktualisierungszeitstempeln | +| `issues` | array | Array von Jira-Issues mit Zeitstempel, Zusammenfassung, Beschreibung, Erstellungs- und Aktualisierungszeitstempeln | ### `jira_delete_issue` @@ -136,8 +143,8 @@ Ein Jira-Issue löschen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Details zum gelöschten Issue mit Zeitstempel, Issue-Key und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Gelöschter Issue-Key | ### `jira_assign_issue` @@ -156,8 +163,9 @@ Ein Jira-Issue einem Benutzer zuweisen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Zuweisungsdetails mit Zeitstempel, Issue-Key, Bearbeiter-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key, der zugewiesen wurde | +| `assigneeId` | string | Konto-ID des Bearbeiters | ### `jira_transition_issue` @@ -177,8 +185,9 @@ Ein Jira-Issue zwischen Workflow-Status verschieben (z.B. To Do -> In Progress) | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Übergangsdetails mit Zeitstempel, Issue-Key, Übergangs-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key, der übergangen wurde | +| `transitionId` | string | Angewendete Übergangs-ID | ### `jira_search_issues` @@ -199,8 +208,11 @@ Nach Jira-Issues mit JQL (Jira Query Language) suchen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Suchergebnisse mit Zeitstempel, Gesamtanzahl, Paginierungsdetails und Array der übereinstimmenden Issues | +| `ts` | string | Zeitstempel der Operation | +| `total` | number | Gesamtanzahl der übereinstimmenden Issues | +| `startAt` | number | Paginierungsstartindex | +| `maxResults` | number | Maximale Ergebnisse pro Seite | +| `issues` | array | Array übereinstimmender Issues mit Key, Zusammenfassung, Status, Bearbeiter, Erstellungs- und Aktualisierungsdatum | ### `jira_add_comment` @@ -219,8 +231,10 @@ Einen Kommentar zu einem Jira-Issue hinzufügen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Kommentardetails mit Zeitstempel, Issue-Key, Kommentar-ID, Inhalt und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key, zu dem der Kommentar hinzugefügt wurde | +| `commentId` | string | Erstellte Kommentar-ID | +| `body` | string | Kommentartextinhalt | ### `jira_get_comments` @@ -240,8 +254,10 @@ Alle Kommentare eines Jira-Issues abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Kommentardaten mit Zeitstempel, Issue-Key, Gesamtanzahl und Array von Kommentaren | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `total` | number | Gesamtanzahl der Kommentare | +| `comments` | array | Array von Kommentaren mit ID, Autor, Inhalt, Erstellungs- und Aktualisierungsdatum | ### `jira_update_comment` @@ -261,8 +277,10 @@ Einen bestehenden Kommentar zu einem Jira-Issue aktualisieren | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Aktualisierte Kommentardetails mit Zeitstempel, Issue-Key, Kommentar-ID, Textinhalt und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `commentId` | string | Aktualisierte Kommentar-ID | +| `body` | string | Aktualisierter Kommentartext | ### `jira_delete_comment` @@ -281,8 +299,9 @@ Einen Kommentar aus einem Jira-Issue löschen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Löschdetails mit Zeitstempel, Issue-Key, Kommentar-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `commentId` | string | ID des gelöschten Kommentars | ### `jira_get_attachments` @@ -300,8 +319,9 @@ Alle Anhänge eines Jira-Issues abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Anhangsdaten mit Zeitstempel, Issue-Key und Array von Anhängen | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `attachments` | array | Array von Anhängen mit ID, Dateiname, Größe, MIME-Typ, Erstellungsdatum und Autor | ### `jira_delete_attachment` @@ -319,8 +339,8 @@ Einen Anhang von einem Jira-Issue löschen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Löschdetails mit Zeitstempel, Anhangs-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `attachmentId` | string | ID des gelöschten Anhangs | ### `jira_add_worklog` @@ -341,8 +361,10 @@ Einen Zeiterfassungseintrag zu einem Jira-Issue hinzufügen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Worklog-Details mit Zeitstempel, Issue-Key, Worklog-ID, aufgewendeter Zeit in Sekunden und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key, zu dem der Worklog hinzugefügt wurde | +| `worklogId` | string | ID des erstellten Worklogs | +| `timeSpentSeconds` | number | Aufgewendete Zeit in Sekunden | ### `jira_get_worklogs` @@ -362,8 +384,10 @@ Alle Worklog-Einträge eines Jira-Issues abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Worklog-Daten mit Zeitstempel, Issue-Key, Gesamtanzahl und Array von Worklogs | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `total` | number | Gesamtanzahl der Worklogs | +| `worklogs` | array | Array von Worklogs mit ID, Autor, aufgewendeter Zeit in Sekunden, aufgewendeter Zeit, Kommentar, Erstellungs-, Aktualisierungs- und Startdatum | ### `jira_update_worklog` @@ -385,8 +409,9 @@ Aktualisieren eines vorhandenen Worklog-Eintrags in einem Jira-Issue | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Worklog-Aktualisierungsdetails mit Zeitstempel, Issue-Key, Worklog-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `worklogId` | string | ID des aktualisierten Worklogs | ### `jira_delete_worklog` @@ -405,8 +430,9 @@ Löschen eines Worklog-Eintrags aus einem Jira-Issue | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Löschdetails mit Zeitstempel, Issue-Key, Worklog-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `worklogId` | string | ID des gelöschten Worklogs | ### `jira_create_issue_link` @@ -427,8 +453,11 @@ Eine Verknüpfungsbeziehung zwischen zwei Jira-Issues erstellen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Issue-Verknüpfungsdetails mit Zeitstempel, eingehendem Issue-Key, ausgehendem Issue-Key, Verknüpfungstyp und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `inwardIssue` | string | Key des eingehenden Issues | +| `outwardIssue` | string | Key des ausgehenden Issues | +| `linkType` | string | Art der Issue-Verknüpfung | +| `linkId` | string | ID der erstellten Verknüpfung | ### `jira_delete_issue_link` @@ -446,8 +475,8 @@ Eine Verknüpfung zwischen zwei Jira-Issues löschen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Löschdetails mit Zeitstempel, Link-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `linkId` | string | ID der gelöschten Verknüpfung | ### `jira_add_watcher` @@ -466,8 +495,9 @@ Einen Beobachter zu einem Jira-Issue hinzufügen, um Benachrichtigungen über Ak | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Beobachterdetails mit Zeitstempel, Issue-Key, Beobachter-Account-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `watcherAccountId` | string | Account-ID des hinzugefügten Beobachters | ### `jira_remove_watcher` @@ -486,8 +516,9 @@ Einen Beobachter von einem Jira-Issue entfernen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Entfernungsdetails mit Zeitstempel, Issue-Key, Beobachter-Konto-ID und Erfolgsstatus | +| `ts` | string | Zeitstempel der Operation | +| `issueKey` | string | Issue-Key | +| `watcherAccountId` | string | Account-ID des entfernten Beobachters | ## Hinweise diff --git a/apps/docs/content/docs/de/tools/slack.mdx b/apps/docs/content/docs/de/tools/slack.mdx index 5fc876281..9066f13fb 100644 --- a/apps/docs/content/docs/de/tools/slack.mdx +++ b/apps/docs/content/docs/de/tools/slack.mdx @@ -54,7 +54,7 @@ Integriert Slack in den Workflow. Kann Nachrichten senden, aktualisieren und lö ### `slack_message` -Senden Sie Nachrichten an Slack-Kanäle oder Benutzer über die Slack-API. Unterstützt Slack mrkdwn-Formatierung. +Sende Nachrichten an Slack-Kanäle oder Direktnachrichten. Unterstützt Slack mrkdwn-Formatierung. #### Eingabe @@ -62,8 +62,9 @@ Senden Sie Nachrichten an Slack-Kanäle oder Benutzer über die Slack-API. Unter | --------- | ---- | -------- | ----------- | | `authMethod` | string | Nein | Authentifizierungsmethode: oauth oder bot_token | | `botToken` | string | Nein | Bot-Token für benutzerdefinierten Bot | -| `channel` | string | Ja | Ziel-Slack-Kanal \(z.B. #general\) | -| `text` | string | Ja | Nachrichtentext zum Senden \(unterstützt Slack mrkdwn-Formatierung\) | +| `channel` | string | Nein | Ziel-Slack-Kanal \(z.B. #general\) | +| `userId` | string | Nein | Ziel-Slack-Benutzer-ID für Direktnachrichten \(z.B. U1234567890\) | +| `text` | string | Ja | Zu sendender Nachrichtentext \(unterstützt Slack mrkdwn-Formatierung\) | | `thread_ts` | string | Nein | Thread-Zeitstempel für Antworten \(erstellt Thread-Antwort\) | | `files` | file[] | Nein | Dateien, die an die Nachricht angehängt werden sollen | @@ -109,10 +110,11 @@ Lesen Sie die neuesten Nachrichten aus Slack-Kanälen. Rufen Sie den Konversatio | --------- | ---- | -------- | ----------- | | `authMethod` | string | Nein | Authentifizierungsmethode: oauth oder bot_token | | `botToken` | string | Nein | Bot-Token für benutzerdefinierten Bot | -| `channel` | string | Ja | Slack-Kanal, aus dem Nachrichten gelesen werden sollen (z.B. #general) | -| `limit` | number | Nein | Anzahl der abzurufenden Nachrichten (Standard: 10, max: 100) | -| `oldest` | string | Nein | Beginn des Zeitraums (Zeitstempel) | -| `latest` | string | Nein | Ende des Zeitraums (Zeitstempel) | +| `channel` | string | Nein | Slack-Kanal, aus dem Nachrichten gelesen werden sollen \(z.B. #general\) | +| `userId` | string | Nein | Benutzer-ID für DM-Konversation \(z.B. U1234567890\) | +| `limit` | number | Nein | Anzahl der abzurufenden Nachrichten \(Standard: 10, max: 100\) | +| `oldest` | string | Nein | Beginn des Zeitraums \(Zeitstempel\) | +| `latest` | string | Nein | Ende des Zeitraums \(Zeitstempel\) | #### Ausgabe diff --git a/apps/docs/content/docs/es/tools/jira.mdx b/apps/docs/content/docs/es/tools/jira.mdx index 25a1d4b2e..13f7a8015 100644 --- a/apps/docs/content/docs/es/tools/jira.mdx +++ b/apps/docs/content/docs/es/tools/jira.mdx @@ -48,8 +48,13 @@ Recupera información detallada sobre una incidencia específica de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de la incidencia de Jira con clave de incidencia, resumen, descripción, marcas de tiempo de creación y actualización | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de la incidencia (p. ej., PROJ-123) | +| `summary` | string | Resumen de la incidencia | +| `description` | json | Contenido de la descripción de la incidencia | +| `created` | string | Marca de tiempo de creación de la incidencia | +| `updated` | string | Marca de tiempo de última actualización de la incidencia | +| `issue` | json | Objeto completo de la incidencia con todos los campos | ### `jira_update` @@ -73,8 +78,9 @@ Actualizar una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles actualizados de la incidencia de Jira con marca de tiempo, clave de incidencia, resumen y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de la incidencia actualizada (p. ej., PROJ-123) | +| `summary` | string | Resumen de la incidencia después de la actualización | ### `jira_write` @@ -97,8 +103,10 @@ Escribir una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de la incidencia de Jira creada con marca de tiempo, clave de incidencia, resumen, estado de éxito y URL | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de la incidencia creada (p. ej., PROJ-123) | +| `summary` | string | Resumen de la incidencia | +| `url` | string | URL de la incidencia creada | ### `jira_bulk_read` @@ -116,8 +124,7 @@ Recuperar múltiples incidencias de Jira en bloque | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | array | Array de incidencias de Jira con resumen, descripción, marcas de tiempo de creación y actualización | +| `issues` | array | Array de incidencias de Jira con marca de tiempo, resumen, descripción, y marcas de tiempo de creación y actualización | ### `jira_delete_issue` @@ -136,8 +143,8 @@ Eliminar una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de la incidencia eliminada con marca de tiempo, clave de incidencia y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de la incidencia eliminada | ### `jira_assign_issue` @@ -156,8 +163,9 @@ Asignar una incidencia de Jira a un usuario | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de la asignación con marca de tiempo, clave de incidencia, ID del asignado y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de la incidencia que fue asignada | +| `assigneeId` | string | ID de cuenta del asignado | ### `jira_transition_issue` @@ -177,8 +185,9 @@ Mover una incidencia de Jira entre estados de flujo de trabajo (p. ej., Pendient | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de la transición con marca de tiempo, clave de incidencia, ID de transición y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia que fue transicionada | +| `transitionId` | string | ID de transición aplicada | ### `jira_search_issues` @@ -199,8 +208,11 @@ Buscar incidencias de Jira usando JQL (Jira Query Language) | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Resultados de búsqueda con marca de tiempo, recuento total, detalles de paginación y array de incidencias coincidentes | +| `ts` | string | Marca de tiempo de la operación | +| `total` | number | Número total de incidencias coincidentes | +| `startAt` | number | Índice de inicio de paginación | +| `maxResults` | number | Máximo de resultados por página | +| `issues` | array | Array de incidencias coincidentes con clave, resumen, estado, asignado, creado, actualizado | ### `jira_add_comment` @@ -219,8 +231,10 @@ Añadir un comentario a una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles del comentario con marca de tiempo, clave de incidencia, ID del comentario, cuerpo y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia a la que se añadió el comentario | +| `commentId` | string | ID del comentario creado | +| `body` | string | Contenido de texto del comentario | ### `jira_get_comments` @@ -240,8 +254,10 @@ Obtener todos los comentarios de una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos de comentarios con marca de tiempo, clave de incidencia, recuento total y array de comentarios | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `total` | number | Número total de comentarios | +| `comments` | array | Array de comentarios con id, autor, cuerpo, creado, actualizado | ### `jira_update_comment` @@ -261,8 +277,10 @@ Actualizar un comentario existente en una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles del comentario actualizado con marca de tiempo, clave de incidencia, ID de comentario, texto del cuerpo y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `commentId` | string | ID del comentario actualizado | +| `body` | string | Texto actualizado del comentario | ### `jira_delete_comment` @@ -281,8 +299,9 @@ Eliminar un comentario de una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de eliminación con marca de tiempo, clave de incidencia, ID de comentario y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `commentId` | string | ID del comentario eliminado | ### `jira_get_attachments` @@ -300,8 +319,9 @@ Obtener todos los adjuntos de una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos de adjuntos con marca de tiempo, clave de incidencia y array de adjuntos | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `attachments` | array | Array de adjuntos con id, nombre de archivo, tamaño, tipo MIME, fecha de creación y autor | ### `jira_delete_attachment` @@ -319,8 +339,8 @@ Eliminar un adjunto de una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de eliminación con marca de tiempo, ID de adjunto y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `attachmentId` | string | ID del adjunto eliminado | ### `jira_add_worklog` @@ -341,8 +361,10 @@ Añadir una entrada de registro de trabajo de seguimiento de tiempo a una incide | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles del registro de trabajo con marca de tiempo, clave de incidencia, ID del registro de trabajo, tiempo dedicado en segundos y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia a la que se añadió el registro de trabajo | +| `worklogId` | string | ID del registro de trabajo creado | +| `timeSpentSeconds` | number | Tiempo empleado en segundos | ### `jira_get_worklogs` @@ -362,8 +384,10 @@ Obtener todas las entradas de registro de trabajo de una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos de registros de trabajo con marca de tiempo, clave de incidencia, recuento total y array de registros de trabajo | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `total` | number | Número total de registros de trabajo | +| `worklogs` | array | Array de registros de trabajo con id, autor, segundos empleados, tiempo empleado, comentario, fecha de creación, actualización e inicio | ### `jira_update_worklog` @@ -385,8 +409,9 @@ Actualizar una entrada existente de registro de trabajo en una incidencia de Jir | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de actualización del registro de trabajo con marca de tiempo, clave de incidencia, ID de registro de trabajo y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `worklogId` | string | ID del registro de trabajo actualizado | ### `jira_delete_worklog` @@ -405,8 +430,9 @@ Eliminar una entrada de registro de trabajo de una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de eliminación con marca de tiempo, clave de incidencia, ID de registro de trabajo y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `worklogId` | string | ID del registro de trabajo eliminado | ### `jira_create_issue_link` @@ -427,8 +453,11 @@ Crear una relación de enlace entre dos incidencias de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles del enlace de incidencia con marca de tiempo, clave de incidencia de entrada, clave de incidencia de salida, tipo de enlace y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `inwardIssue` | string | Clave de incidencia de entrada | +| `outwardIssue` | string | Clave de incidencia de salida | +| `linkType` | string | Tipo de enlace de incidencia | +| `linkId` | string | ID del enlace creado | ### `jira_delete_issue_link` @@ -446,8 +475,8 @@ Eliminar un enlace entre dos incidencias de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de eliminación con marca de tiempo, ID del enlace y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `linkId` | string | ID del enlace eliminado | ### `jira_add_watcher` @@ -466,8 +495,9 @@ Añadir un observador a una incidencia de Jira para recibir notificaciones sobre | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles del observador con marca de tiempo, clave de incidencia, ID de cuenta del observador y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `watcherAccountId` | string | ID de cuenta del observador añadido | ### `jira_remove_watcher` @@ -486,8 +516,9 @@ Eliminar un observador de una incidencia de Jira | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Detalles de eliminación con marca de tiempo, clave de incidencia, ID de cuenta del observador y estado de éxito | +| `ts` | string | Marca de tiempo de la operación | +| `issueKey` | string | Clave de incidencia | +| `watcherAccountId` | string | ID de cuenta del observador eliminado | ## Notas diff --git a/apps/docs/content/docs/es/tools/slack.mdx b/apps/docs/content/docs/es/tools/slack.mdx index 2d63c4158..bfe60303e 100644 --- a/apps/docs/content/docs/es/tools/slack.mdx +++ b/apps/docs/content/docs/es/tools/slack.mdx @@ -54,7 +54,7 @@ Integra Slack en el flujo de trabajo. Puede enviar, actualizar y eliminar mensaj ### `slack_message` -Envía mensajes a canales o usuarios de Slack a través de la API de Slack. Compatible con el formato mrkdwn de Slack. +Envía mensajes a canales de Slack o mensajes directos. Compatible con el formato mrkdwn de Slack. #### Entrada @@ -62,9 +62,10 @@ Envía mensajes a canales o usuarios de Slack a través de la API de Slack. Comp | --------- | ---- | -------- | ----------- | | `authMethod` | string | No | Método de autenticación: oauth o bot_token | | `botToken` | string | No | Token del bot para Bot personalizado | -| `channel` | string | Sí | Canal de Slack objetivo (p. ej., #general) | +| `channel` | string | No | Canal de Slack objetivo (p. ej., #general) | +| `userId` | string | No | ID de usuario de Slack objetivo para mensajes directos (p. ej., U1234567890) | | `text` | string | Sí | Texto del mensaje a enviar (admite formato mrkdwn de Slack) | -| `thread_ts` | string | No | Marca de tiempo del hilo para responder (crea respuesta en hilo) | +| `thread_ts` | string | No | Marca de tiempo del hilo al que responder (crea respuesta en hilo) | | `files` | file[] | No | Archivos para adjuntar al mensaje | #### Salida @@ -109,7 +110,8 @@ Lee los últimos mensajes de los canales de Slack. Recupera el historial de conv | --------- | ---- | -------- | ----------- | | `authMethod` | string | No | Método de autenticación: oauth o bot_token | | `botToken` | string | No | Token del bot para Bot personalizado | -| `channel` | string | Sí | Canal de Slack del que leer mensajes (p. ej., #general) | +| `channel` | string | No | Canal de Slack del que leer mensajes (p. ej., #general) | +| `userId` | string | No | ID de usuario para conversación por MD (p. ej., U1234567890) | | `limit` | number | No | Número de mensajes a recuperar (predeterminado: 10, máx: 100) | | `oldest` | string | No | Inicio del rango de tiempo (marca de tiempo) | | `latest` | string | No | Fin del rango de tiempo (marca de tiempo) | diff --git a/apps/docs/content/docs/fr/tools/jira.mdx b/apps/docs/content/docs/fr/tools/jira.mdx index ed3e88d10..70ccb82cc 100644 --- a/apps/docs/content/docs/fr/tools/jira.mdx +++ b/apps/docs/content/docs/fr/tools/jira.mdx @@ -48,8 +48,13 @@ Récupérer des informations détaillées sur un ticket Jira spécifique | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Détails du ticket Jira avec la clé du ticket, le résumé, la description, les horodatages de création et de mise à jour | +| `ts` | chaîne | Horodatage de l'opération | +| `issueKey` | chaîne | Clé du ticket \(ex. : PROJ-123\) | +| `summary` | chaîne | Résumé du ticket | +| `description` | json | Contenu de la description du ticket | +| `created` | chaîne | Horodatage de création du ticket | +| `updated` | chaîne | Horodatage de dernière mise à jour du ticket | +| `issue` | json | Objet complet du ticket avec tous les champs | ### `jira_update` @@ -73,8 +78,9 @@ Mettre à jour un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la demande Jira mise à jour avec horodatage, clé de la demande, résumé et statut de réussite | +| `ts` | chaîne | Horodatage de l'opération | +| `issueKey` | chaîne | Clé du ticket mis à jour \(ex. : PROJ-123\) | +| `summary` | chaîne | Résumé du ticket après mise à jour | ### `jira_write` @@ -97,8 +103,10 @@ Rédiger une demande Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la demande Jira créée avec horodatage, clé de la demande, résumé, statut de réussite et URL | +| `ts` | chaîne | Horodatage de l'opération | +| `issueKey` | chaîne | Clé du ticket créé \(ex. : PROJ-123\) | +| `summary` | chaîne | Résumé du ticket | +| `url` | chaîne | URL vers le ticket créé | ### `jira_bulk_read` @@ -116,8 +124,7 @@ Récupérer plusieurs demandes Jira en masse | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | array | Tableau des tickets Jira avec résumé, description, horodatages de création et de mise à jour | +| `issues` | tableau | Tableau des tickets Jira avec horodatages ts, résumé, description, création et mise à jour | ### `jira_delete_issue` @@ -136,8 +143,8 @@ Supprimer un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Détails du ticket supprimé avec horodatage, clé du ticket et statut de réussite | +| `ts` | chaîne | Horodatage de l'opération | +| `issueKey` | chaîne | Clé du ticket supprimé | ### `jira_assign_issue` @@ -156,8 +163,9 @@ Assigner un ticket Jira à un utilisateur | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Détails de l'assignation avec horodatage, clé du ticket, ID de l'assigné et statut de réussite | +| `ts` | chaîne | Horodatage de l'opération | +| `issueKey` | chaîne | Clé du ticket qui a été assigné | +| `assigneeId` | chaîne | ID de compte de l'assigné | ### `jira_transition_issue` @@ -177,8 +185,9 @@ Déplacer un ticket Jira entre les statuts de workflow (par ex., À faire -> En | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Détails de la transition avec horodatage, clé du ticket, ID de transition et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket qui a été transitionné | +| `transitionId` | string | ID de la transition appliquée | ### `jira_search_issues` @@ -199,8 +208,11 @@ Rechercher des tickets Jira à l'aide de JQL (Jira Query Language) | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Résultats de recherche avec horodatage, nombre total, détails de pagination et tableau des tickets correspondants | +| `ts` | string | Horodatage de l'opération | +| `total` | number | Nombre total de tickets correspondants | +| `startAt` | number | Index de début de pagination | +| `maxResults` | number | Nombre maximum de résultats par page | +| `issues` | array | Tableau des tickets correspondants avec clé, résumé, statut, assigné, créé, mis à jour | ### `jira_add_comment` @@ -219,8 +231,10 @@ Ajouter un commentaire à un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Détails du commentaire avec horodatage, clé du ticket, ID du commentaire, corps et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket auquel le commentaire a été ajouté | +| `commentId` | string | ID du commentaire créé | +| `body` | string | Contenu textuel du commentaire | ### `jira_get_comments` @@ -240,8 +254,10 @@ Obtenir tous les commentaires d'un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données des commentaires avec horodatage, clé du ticket, nombre total et tableau de commentaires | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `total` | number | Nombre total de commentaires | +| `comments` | array | Tableau des commentaires avec id, auteur, corps, créé, mis à jour | ### `jira_update_comment` @@ -261,8 +277,10 @@ Mettre à jour un commentaire existant sur un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails du commentaire mis à jour avec horodatage, clé du ticket, ID du commentaire, texte du corps et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `commentId` | string | ID du commentaire mis à jour | +| `body` | string | Texte du commentaire mis à jour | ### `jira_delete_comment` @@ -281,8 +299,9 @@ Supprimer un commentaire d'un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la suppression avec horodatage, clé du ticket, ID du commentaire et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `commentId` | string | ID du commentaire supprimé | ### `jira_get_attachments` @@ -300,8 +319,9 @@ Obtenir toutes les pièces jointes d'un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données des pièces jointes avec horodatage, clé du ticket et tableau des pièces jointes | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `attachments` | array | Tableau des pièces jointes avec id, nom de fichier, taille, type MIME, date de création, auteur | ### `jira_delete_attachment` @@ -319,8 +339,8 @@ Supprimer une pièce jointe d'un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la suppression avec horodatage, ID de la pièce jointe et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `attachmentId` | string | ID de la pièce jointe supprimée | ### `jira_add_worklog` @@ -341,8 +361,10 @@ Ajouter une entrée de journal de travail pour le suivi du temps à un ticket Ji | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Détails du journal de travail avec horodatage, clé du ticket, ID du journal de travail, temps passé en secondes et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket auquel le journal de travail a été ajouté | +| `worklogId` | string | ID du journal de travail créé | +| `timeSpentSeconds` | number | Temps passé en secondes | ### `jira_get_worklogs` @@ -362,8 +384,10 @@ Obtenir toutes les entrées du journal de travail d'un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données des journaux de travail avec horodatage, clé du ticket, nombre total et tableau des journaux de travail | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `total` | number | Nombre total de journaux de travail | +| `worklogs` | array | Tableau des journaux de travail avec id, auteur, temps passé en secondes, temps passé, commentaire, date de création, mise à jour, démarrage | ### `jira_update_worklog` @@ -385,8 +409,9 @@ Mettre à jour une entrée de journal de travail existante sur un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la mise à jour du journal de travail avec horodatage, clé du ticket, ID du journal de travail et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `worklogId` | string | ID du journal de travail mis à jour | ### `jira_delete_worklog` @@ -405,8 +430,9 @@ Supprimer une entrée de journal de travail d'un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la suppression avec horodatage, clé de la demande, ID du journal de travail et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `worklogId` | string | ID du journal de travail supprimé | ### `jira_create_issue_link` @@ -427,8 +453,11 @@ Créer une relation de lien entre deux tickets Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails du lien entre tickets avec horodatage, clé du ticket entrant, clé du ticket sortant, type de lien et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `inwardIssue` | string | Clé du ticket entrant | +| `outwardIssue` | string | Clé du ticket sortant | +| `linkType` | string | Type de lien entre tickets | +| `linkId` | string | ID du lien créé | ### `jira_delete_issue_link` @@ -446,8 +475,8 @@ Supprimer un lien entre deux tickets Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la suppression avec horodatage, ID du lien et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `linkId` | string | ID du lien supprimé | ### `jira_add_watcher` @@ -466,8 +495,9 @@ Ajouter un observateur à un ticket Jira pour recevoir des notifications sur les | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de l'observateur avec horodatage, clé du ticket, ID de compte de l'observateur et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `watcherAccountId` | string | ID du compte observateur ajouté | ### `jira_remove_watcher` @@ -486,8 +516,9 @@ Supprimer un observateur d'un ticket Jira | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Détails de la suppression avec horodatage, clé de la demande, ID du compte observateur et statut de réussite | +| `ts` | string | Horodatage de l'opération | +| `issueKey` | string | Clé du ticket | +| `watcherAccountId` | string | ID du compte observateur supprimé | ## Notes diff --git a/apps/docs/content/docs/fr/tools/slack.mdx b/apps/docs/content/docs/fr/tools/slack.mdx index 45f1ec933..a22cbf5ff 100644 --- a/apps/docs/content/docs/fr/tools/slack.mdx +++ b/apps/docs/content/docs/fr/tools/slack.mdx @@ -54,17 +54,18 @@ Intégrez Slack dans le flux de travail. Peut envoyer, mettre à jour et supprim ### `slack_message` -Envoyez des messages aux canaux ou utilisateurs Slack via l'API Slack. Prend en charge le formatage mrkdwn de Slack. +Envoyez des messages aux canaux Slack ou en messages directs. Prend en charge le formatage mrkdwn de Slack. #### Entrée | Paramètre | Type | Obligatoire | Description | | --------- | ---- | ---------- | ----------- | | `authMethod` | chaîne | Non | Méthode d'authentification : oauth ou bot_token | -| `botToken` | chaîne | Non | Jeton du bot pour le Bot personnalisé | -| `channel` | chaîne | Oui | Canal Slack cible \(par ex., #general\) | +| `botToken` | chaîne | Non | Jeton du bot pour Bot personnalisé | +| `channel` | chaîne | Non | Canal Slack cible \(ex. : #general\) | +| `userId` | chaîne | Non | ID utilisateur Slack cible pour les messages directs \(ex. : U1234567890\) | | `text` | chaîne | Oui | Texte du message à envoyer \(prend en charge le formatage mrkdwn de Slack\) | -| `thread_ts` | chaîne | Non | Horodatage du fil pour répondre \(crée une réponse dans le fil\) | +| `thread_ts` | chaîne | Non | Horodatage du fil de discussion auquel répondre \(crée une réponse dans le fil\) | | `files` | fichier[] | Non | Fichiers à joindre au message | #### Sortie @@ -109,7 +110,8 @@ Lisez les derniers messages des canaux Slack. Récupérez l'historique des conve | --------- | ---- | ---------- | ----------- | | `authMethod` | chaîne | Non | Méthode d'authentification : oauth ou bot_token | | `botToken` | chaîne | Non | Jeton du bot pour Bot personnalisé | -| `channel` | chaîne | Oui | Canal Slack pour lire les messages \(ex. : #general\) | +| `channel` | chaîne | Non | Canal Slack pour lire les messages \(ex. : #general\) | +| `userId` | chaîne | Non | ID utilisateur pour la conversation en MP \(ex. : U1234567890\) | | `limit` | nombre | Non | Nombre de messages à récupérer \(par défaut : 10, max : 100\) | | `oldest` | chaîne | Non | Début de la plage temporelle \(horodatage\) | | `latest` | chaîne | Non | Fin de la plage temporelle \(horodatage\) | diff --git a/apps/docs/content/docs/ja/tools/jira.mdx b/apps/docs/content/docs/ja/tools/jira.mdx index d6d1d9313..7a9cc4e91 100644 --- a/apps/docs/content/docs/ja/tools/jira.mdx +++ b/apps/docs/content/docs/ja/tools/jira.mdx @@ -48,8 +48,13 @@ Jiraをワークフローに統合します。課題の読み取り、書き込 | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 課題キー、要約、説明、作成日時、更新日時を含むJira課題の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー(例:PROJ-123) | +| `summary` | string | 課題の要約 | +| `description` | json | 課題の説明内容 | +| `created` | string | 課題作成タイムスタンプ | +| `updated` | string | 課題最終更新タイムスタンプ | +| `issue` | json | すべてのフィールドを含む完全な課題オブジェクト | ### `jira_update` @@ -73,8 +78,9 @@ Jira課題を更新する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、要約、成功ステータスを含む更新されたJira課題の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 更新された課題キー(例:PROJ-123) | +| `summary` | string | 更新後の課題要約 | ### `jira_write` @@ -97,8 +103,10 @@ Jira課題を作成する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、要約、成功ステータス、URLを含む作成されたJira課題の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 作成された課題キー(例:PROJ-123) | +| `summary` | string | 課題の要約 | +| `url` | string | 作成された課題へのURL | ### `jira_bulk_read` @@ -116,8 +124,7 @@ Jira課題を作成する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | array | 概要、説明、作成日時、更新日時を含むJiraの課題の配列 | +| `issues` | array | タイムスタンプ、要約、説明、作成日時、更新日時を含むJira課題の配列 | ### `jira_delete_issue` @@ -136,8 +143,8 @@ Jira課題を削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、成功ステータスを含む削除された課題の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 削除された課題キー | ### `jira_assign_issue` @@ -156,8 +163,9 @@ Jira課題をユーザーに割り当てる | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、担当者ID、成功ステータスを含む割り当ての詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 割り当てられた課題キー | +| `assigneeId` | string | 担当者のアカウントID | ### `jira_transition_issue` @@ -177,8 +185,9 @@ Jira課題をワークフローステータス間で移動する(例:To Do | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、移行ID、成功ステータスを含む移行の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 遷移した課題キー | +| `transitionId` | string | 適用されたトランジションID | ### `jira_search_issues` @@ -199,8 +208,11 @@ JQL(Jira Query Language)を使用してJira課題を検索する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、合計数、ページネーション詳細、一致する課題の配列を含む検索結果 | +| `ts` | string | 操作のタイムスタンプ | +| `total` | number | 一致する課題の総数 | +| `startAt` | number | ページネーション開始インデックス | +| `maxResults` | number | ページあたりの最大結果数 | +| `issues` | array | キー、要約、ステータス、担当者、作成日時、更新日時を含む一致する課題の配列 | ### `jira_add_comment` @@ -219,8 +231,10 @@ Jira課題にコメントを追加する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、コメントID、本文、成功ステータスを含むコメント詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | コメントが追加された課題キー | +| `commentId` | string | 作成されたコメントID | +| `body` | string | コメントのテキスト内容 | ### `jira_get_comments` @@ -240,8 +254,10 @@ Jira課題からすべてのコメントを取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、合計数、コメントの配列を含むコメントデータ | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `total` | number | コメントの総数 | +| `comments` | array | ID、作成者、本文、作成日時、更新日時を含むコメントの配列 | ### `jira_update_comment` @@ -261,8 +277,10 @@ Jira課題の既存コメントを更新する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、コメントID、本文テキスト、成功ステータスを含む更新されたコメントの詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `commentId` | string | 更新されたコメントID | +| `body` | string | 更新されたコメントテキスト | ### `jira_delete_comment` @@ -281,8 +299,9 @@ Jira課題からコメントを削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、コメントID、成功ステータスを含む削除の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `commentId` | string | 削除されたコメントID | ### `jira_get_attachments` @@ -300,8 +319,9 @@ Jira課題からすべての添付ファイルを取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、添付ファイルの配列を含む添付ファイルデータ | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `attachments` | array | ID、ファイル名、サイズ、MIMEタイプ、作成日時、作成者を含む添付ファイルの配列 | ### `jira_delete_attachment` @@ -319,8 +339,8 @@ Jira課題から添付ファイルを削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、添付ファイルID、成功ステータスを含む削除の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `attachmentId` | string | 削除された添付ファイルID | ### `jira_add_worklog` @@ -341,8 +361,10 @@ Jira課題に作業時間記録エントリを追加する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、作業ログID、秒単位の作業時間、成功ステータスを含む作業ログの詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 作業ログが追加された課題キー | +| `worklogId` | string | 作成された作業ログID | +| `timeSpentSeconds` | number | 秒単位の作業時間 | ### `jira_get_worklogs` @@ -362,8 +384,10 @@ Jira課題からすべての作業ログエントリを取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、合計数、作業ログの配列を含む作業ログデータ | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `total` | number | 作業ログの総数 | +| `worklogs` | array | ID、作成者、秒単位の作業時間、作業時間、コメント、作成日時、更新日時、開始日時を含む作業ログの配列 | ### `jira_update_worklog` @@ -385,8 +409,9 @@ Jira課題の既存の作業ログエントリを更新する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、作業ログID、成功ステータスを含む作業ログ更新の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `worklogId` | string | 更新された作業ログID | ### `jira_delete_worklog` @@ -405,8 +430,9 @@ Jira課題から作業ログエントリを削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、作業ログID、成功ステータスを含む削除の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `worklogId` | string | 削除された作業ログID | ### `jira_create_issue_link` @@ -427,8 +453,11 @@ Jira課題から作業ログエントリを削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、インワード課題キー、アウトワード課題キー、リンクタイプ、成功ステータスを含む課題リンクの詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `inwardIssue` | string | インワード課題キー | +| `outwardIssue` | string | アウトワード課題キー | +| `linkType` | string | 課題リンクのタイプ | +| `linkId` | string | 作成されたリンクID | ### `jira_delete_issue_link` @@ -446,8 +475,8 @@ Jira課題から作業ログエントリを削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、リンクID、成功ステータスを含む削除の詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `linkId` | string | 削除されたリンクID | ### `jira_add_watcher` @@ -466,8 +495,9 @@ Jira課題から作業ログエントリを削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、ウォッチャーアカウントID、成功ステータスを含むウォッチャーの詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `watcherAccountId` | string | 追加されたウォッチャーのアカウントID | ### `jira_remove_watcher` @@ -486,8 +516,9 @@ Jira課題からウォッチャーを削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | タイムスタンプ、課題キー、ウォッチャーアカウントID、成功ステータスを含む削除詳細 | +| `ts` | string | 操作のタイムスタンプ | +| `issueKey` | string | 課題キー | +| `watcherAccountId` | string | 削除されたウォッチャーのアカウントID | ## 注意事項 diff --git a/apps/docs/content/docs/ja/tools/slack.mdx b/apps/docs/content/docs/ja/tools/slack.mdx index 3edd946a3..af0031619 100644 --- a/apps/docs/content/docs/ja/tools/slack.mdx +++ b/apps/docs/content/docs/ja/tools/slack.mdx @@ -53,7 +53,7 @@ Slackをワークフローに統合します。メッセージの送信、更新 ### `slack_message` -Slack APIを通じてSlackチャンネルまたはユーザーにメッセージを送信します。Slack mrkdwnフォーマットをサポートしています。 +Slackチャンネルまたはダイレクトメッセージにメッセージを送信します。Slack mrkdwn形式をサポートしています。 #### 入力 @@ -61,7 +61,8 @@ Slack APIを通じてSlackチャンネルまたはユーザーにメッセージ | --------- | ---- | -------- | ----------- | | `authMethod` | string | いいえ | 認証方法:oauthまたはbot_token | | `botToken` | string | いいえ | カスタムボット用のボットトークン | -| `channel` | string | はい | 対象のSlackチャンネル(例:#general) | +| `channel` | string | いいえ | 対象のSlackチャンネル(例:#general) | +| `userId` | string | いいえ | ダイレクトメッセージ用の対象SlackユーザーID(例:U1234567890) | | `text` | string | はい | 送信するメッセージテキスト(Slack mrkdwn形式をサポート) | | `thread_ts` | string | いいえ | 返信するスレッドのタイムスタンプ(スレッド返信を作成) | | `files` | file[] | いいえ | メッセージに添付するファイル | @@ -108,7 +109,8 @@ Slackチャンネルから最新のメッセージを読み取ります。フィ | --------- | ---- | -------- | ----------- | | `authMethod` | string | いいえ | 認証方法:oauthまたはbot_token | | `botToken` | string | いいえ | カスタムボット用のボットトークン | -| `channel` | string | はい | メッセージを読み取るSlackチャンネル(例:#general) | +| `channel` | string | いいえ | メッセージを読み取るSlackチャンネル(例:#general) | +| `userId` | string | いいえ | DMの会話用のユーザーID(例:U1234567890) | | `limit` | number | いいえ | 取得するメッセージ数(デフォルト:10、最大:100) | | `oldest` | string | いいえ | 時間範囲の開始(タイムスタンプ) | | `latest` | string | いいえ | 時間範囲の終了(タイムスタンプ) | diff --git a/apps/docs/content/docs/zh/tools/jira.mdx b/apps/docs/content/docs/zh/tools/jira.mdx index 0ad44be52..d5013e470 100644 --- a/apps/docs/content/docs/zh/tools/jira.mdx +++ b/apps/docs/content/docs/zh/tools/jira.mdx @@ -48,8 +48,13 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 包含问题键、摘要、描述、创建和更新时间戳的 Jira 问题详细信息 | +| `ts` | 字符串 | 操作的时间戳 | +| `issueKey` | 字符串 | 问题键 \(例如:PROJ-123\) | +| `summary` | 字符串 | 问题摘要 | +| `description` | JSON | 问题描述内容 | +| `created` | 字符串 | 问题创建的时间戳 | +| `updated` | 字符串 | 问题最后更新的时间戳 | +| `issue` | JSON | 包含所有字段的完整问题对象 | ### `jira_update` @@ -73,8 +78,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 更新的 Jira 问题详情,包括时间戳、问题键、摘要和成功状态 | +| `ts` | 字符串 | 操作的时间戳 | +| `issueKey` | 字符串 | 更新后的问题键 \(例如:PROJ-123\) | +| `summary` | 字符串 | 更新后的问题摘要 | ### `jira_write` @@ -97,8 +103,10 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 创建的 Jira 问题详情,包括时间戳、问题键、摘要、成功状态和 URL | +| `ts` | 字符串 | 操作的时间戳 | +| `issueKey` | 字符串 | 创建的问题键 \(例如:PROJ-123\) | +| `summary` | 字符串 | 问题摘要 | +| `url` | 字符串 | 创建的问题的 URL | ### `jira_bulk_read` @@ -116,8 +124,7 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | array | 包含 Jira 问题的数组,包括摘要、描述、创建和更新的时间戳 | +| `issues` | 数组 | 包含时间戳、摘要、描述、创建和更新时间戳的 Jira 问题数组 | ### `jira_delete_issue` @@ -136,8 +143,8 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 删除的问题详情,包括时间戳、问题键和成功状态 | +| `ts` | 字符串 | 操作的时间戳 | +| `issueKey` | 字符串 | 删除的问题键 | ### `jira_assign_issue` @@ -156,8 +163,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 分配详情,包括时间戳、问题键、分配人 ID 和成功状态 | +| `ts` | 字符串 | 操作的时间戳 | +| `issueKey` | 字符串 | 被分配的任务键 | +| `assigneeId` | 字符串 | 分配者的账户 ID | ### `jira_transition_issue` @@ -177,8 +185,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 转换详情,包括时间戳、问题键、转换 ID 和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 已转换的问题键 | +| `transitionId` | string | 应用的转换 ID | ### `jira_search_issues` @@ -199,8 +208,11 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 搜索结果,包括时间戳、总数、分页详情和匹配问题的数组 | +| `ts` | string | 操作的时间戳 | +| `total` | number | 匹配问题的总数 | +| `startAt` | number | 分页起始索引 | +| `maxResults` | number | 每页的最大结果数 | +| `issues` | array | 包含键、摘要、状态、负责人、创建时间和更新时间的匹配问题数组 | ### `jira_add_comment` @@ -219,8 +231,10 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 评论详情,包括时间戳、问题键、评论 ID、正文和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 添加评论的问题键 | +| `commentId` | string | 创建的评论 ID | +| `body` | string | 评论的文本内容 | ### `jira_get_comments` @@ -240,8 +254,10 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 评论数据,包括时间戳、问题键、总数和评论数组 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `total` | number | 评论的总数 | +| `comments` | array | 包含 ID、作者、正文、创建时间和更新时间的评论数组 | ### `jira_update_comment` @@ -261,8 +277,10 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 更新的评论详情,包括时间戳、问题键、评论 ID、正文文本和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `commentId` | string | 更新的评论 ID | +| `body` | string | 更新的评论文本 | ### `jira_delete_comment` @@ -281,8 +299,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 删除详情,包括时间戳、问题键、评论 ID 和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `commentId` | string | 已删除的评论 ID | ### `jira_get_attachments` @@ -300,8 +319,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 附件数据,包括时间戳、问题键和附件数组 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `attachments` | array | 附件数组,包括 id、文件名、大小、mimeType、创建时间、作者 | ### `jira_delete_attachment` @@ -319,8 +339,8 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 删除详情,包括时间戳、附件 ID 和成功状态 | +| `ts` | string | 操作的时间戳 | +| `attachmentId` | string | 已删除的附件 ID | ### `jira_add_worklog` @@ -341,8 +361,10 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 工作日志详情,包括时间戳、问题键、工作日志 ID、花费的时间(以秒为单位)和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 添加工作日志的相关问题键 | +| `worklogId` | string | 创建的工作日志 ID | +| `timeSpentSeconds` | number | 花费的时间(以秒为单位) | ### `jira_get_worklogs` @@ -362,8 +384,10 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 包含时间戳、问题键、总数和工作日志数组的工作日志数据 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `total` | number | 工作日志的总数 | +| `worklogs` | array | 工作日志数组,包括 id、作者、timeSpentSeconds、timeSpent、评论、创建时间、更新时间、开始时间 | ### `jira_update_worklog` @@ -385,8 +409,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 包含时间戳、问题键、工作日志 ID 和成功状态的工作日志更新详情 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `worklogId` | string | 更新的工作日志 ID | ### `jira_delete_worklog` @@ -405,8 +430,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 删除详情,包括时间戳、问题键、工作日志 ID 和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `worklogId` | string | 已删除的工作日志 ID | ### `jira_create_issue_link` @@ -427,8 +453,11 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 问题链接详情,包括时间戳、内部问题键、外部问题键、链接类型和成功状态 | +| `ts` | string | 操作的时间戳 | +| `inwardIssue` | string | 内部问题键 | +| `outwardIssue` | string | 外部问题键 | +| `linkType` | string | 问题链接的类型 | +| `linkId` | string | 创建的链接 ID | ### `jira_delete_issue_link` @@ -446,8 +475,8 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 删除详情,包括时间戳、链接 ID 和成功状态 | +| `ts` | string | 操作的时间戳 | +| `linkId` | string | 已删除的链接 ID | ### `jira_add_watcher` @@ -466,8 +495,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 观察者详情,包括时间戳、问题键、观察者账户 ID 和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `watcherAccountId` | string | 添加的观察者账户 ID | ### `jira_remove_watcher` @@ -486,8 +516,9 @@ Jira 的主要功能包括: | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 移除详情,包括时间戳、问题键、观察者账户 ID 和成功状态 | +| `ts` | string | 操作的时间戳 | +| `issueKey` | string | 问题键 | +| `watcherAccountId` | string | 移除的观察者账户 ID | ## 注意事项 diff --git a/apps/docs/content/docs/zh/tools/slack.mdx b/apps/docs/content/docs/zh/tools/slack.mdx index a22782dae..a4a997321 100644 --- a/apps/docs/content/docs/zh/tools/slack.mdx +++ b/apps/docs/content/docs/zh/tools/slack.mdx @@ -52,7 +52,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" ### `slack_message` -通过 Slack API 向 Slack 频道或用户发送消息。支持 Slack mrkdwn 格式化。 +向 Slack 频道或直接消息发送消息。支持 Slack mrkdwn 格式。 #### 输入 @@ -60,10 +60,11 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | --------- | ---- | -------- | ----------- | | `authMethod` | string | 否 | 认证方法:oauth 或 bot_token | | `botToken` | string | 否 | 自定义 Bot 的令牌 | -| `channel` | string | 是 | 目标 Slack 频道(例如,#general) | +| `channel` | string | 否 | 目标 Slack 频道(例如,#general) | +| `userId` | string | 否 | 目标 Slack 用户 ID,用于直接消息(例如,U1234567890) | | `text` | string | 是 | 要发送的消息文本(支持 Slack mrkdwn 格式) | -| `thread_ts` | string | 否 | 要回复的线程时间戳(创建线程回复) | -| `files` | file[] | 否 | 要附加到消息的文件 | +| `thread_ts` | string | 否 | 回复的线程时间戳(创建线程回复) | +| `files` | file[] | 否 | 附加到消息的文件 | #### 输出 @@ -103,11 +104,12 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" #### 输入 -| 参数 | 类型 | 必需 | 描述 | +| 参数 | 类型 | 是否必需 | 描述 | | --------- | ---- | -------- | ----------- | | `authMethod` | string | 否 | 认证方法:oauth 或 bot_token | -| `botToken` | string | 否 | 自定义 Bot 的 Bot token | -| `channel` | string | 是 | 要读取消息的 Slack 频道(例如:#general) | +| `botToken` | string | 否 | 自定义 Bot 的令牌 | +| `channel` | string | 否 | 要读取消息的 Slack 频道(例如,#general) | +| `userId` | string | 否 | DM 对话的用户 ID(例如,U1234567890) | | `limit` | number | 否 | 要检索的消息数量(默认:10,最大:100) | | `oldest` | string | 否 | 时间范围的开始(时间戳) | | `latest` | string | 否 | 时间范围的结束(时间戳) | diff --git a/apps/docs/i18n.lock b/apps/docs/i18n.lock index 5b5687764..bd3c2e424 100644 --- a/apps/docs/i18n.lock +++ b/apps/docs/i18n.lock @@ -889,9 +889,9 @@ checksums: content/10: 71c6cf129630acff9d8df39d0a5c5407 content/11: 9c8aa3f09c9b2bd50ea4cdff3598ea4e content/12: 8ee83eff32425b2c52929284e8485c20 - content/13: 6cda87dc9837779f4572ed70b87a5654 + content/13: c1ec0b00cb68561551e48616731ea43a content/14: 371d0e46b4bd2c23f559b8bc112f6955 - content/15: 2f696275726cdeefd7d7280b5bb43b21 + content/15: 117e42c934a7f2a76b0399235841260e content/16: bcadfc362b69078beee0088e5936c98b content/17: bb43e4f36fdc1eb6211f46ddeed9e0aa content/18: 05540cb3028d4d781521c14e5f9e3835 @@ -903,7 +903,7 @@ checksums: content/24: 228a8ece96627883153b826a1cbaa06c content/25: 53abe061a259c296c82676b4770ddd1b content/26: 371d0e46b4bd2c23f559b8bc112f6955 - content/27: 170ccdc4ce7ee086e9c6b5073efca582 + content/27: 03e8b10ec08b354de98e360b66b779e3 content/28: bcadfc362b69078beee0088e5936c98b content/29: b82def7d82657f941fbe60df3924eeeb content/30: 1ca7ee3856805fa1718031c5f75b6ffb @@ -2511,133 +2511,133 @@ checksums: content/12: 371d0e46b4bd2c23f559b8bc112f6955 content/13: 8c25606fde43bf4ff519760364fda052 content/14: bcadfc362b69078beee0088e5936c98b - content/15: 9ee0b1e8873ef165299443a76823e7bc + content/15: 4ec31e928a8498d050922adb2f977c98 content/16: be5c68d578443b68c062029104bd6ddb content/17: 3b38aa70e04f841184b7d958b087af8c content/18: 371d0e46b4bd2c23f559b8bc112f6955 content/19: 5269008adfae57a3a8fb5d8bf5922498 content/20: bcadfc362b69078beee0088e5936c98b - content/21: c66b2996f62f0f7150fce59eed9ad7a8 + content/21: 5317f2e9eb34d7297064b381aef6912c content/22: ef92d95455e378abe4d27a1cdc5e1aed content/23: febd6019055f3754953fd93395d0dbf2 content/24: 371d0e46b4bd2c23f559b8bc112f6955 content/25: 7ef3f388e5ee9346bac54c771d825f40 content/26: bcadfc362b69078beee0088e5936c98b - content/27: 7bccc537f32fabcbb4cd0a85bef612de + content/27: e0fa91c45aa780fc03e91df77417f893 content/28: b463f54cd5fe2458b5842549fbb5e1ce content/29: 55f8c724e1a2463bc29a32518a512c73 content/30: 371d0e46b4bd2c23f559b8bc112f6955 content/31: 770b1f7b3105937d452bc2815ebb6e05 content/32: bcadfc362b69078beee0088e5936c98b - content/33: b7be768fe967164e71af56dd5cd13f86 + content/33: 0b92b54ce40dc29bb6faccf82eace18b content/34: f426b59ee38021a4254fe566995c416c content/35: 2455b2f418cc79f4a67558678ae444bc content/36: 371d0e46b4bd2c23f559b8bc112f6955 content/37: 320e2962c7e2d24690c75b2f69e5be9d content/38: bcadfc362b69078beee0088e5936c98b - content/39: 25501290045d87dad2c5819200528091 + content/39: 76f532ccbddb41115e56a7d56d97aa96 content/40: 52233c13208d6e10497340a37b11ef3a content/41: 9c6bf4c4180c96e31668941aa36f2cde content/42: 371d0e46b4bd2c23f559b8bc112f6955 content/43: a792b3be1ab2bc6bc570f5dafca56aaa content/44: bcadfc362b69078beee0088e5936c98b - content/45: 120bc8dacae493c314aed0f4a4094c7f + content/45: 838a4016055b35389dae383f8b4ec2ac content/46: fe4880697d8adcd75c3a2c7e5b0fec86 content/47: fdd9ab6e60b2c42a18a41ef869fb925b content/48: 371d0e46b4bd2c23f559b8bc112f6955 content/49: 0ccf3f3f59955dd78558a0e3589579a6 content/50: bcadfc362b69078beee0088e5936c98b - content/51: 35ce33f78ffa1130c2719885759406a5 + content/51: 8defd6d29c0ddbd9a811caa8f2cf3f39 content/52: f04e8809e7d4f701cf24b339d844bea5 content/53: 811c364b512dd61a2f40fb8418b6b0cd content/54: 371d0e46b4bd2c23f559b8bc112f6955 content/55: 2ce7e48a76065784447b75b6bc0fbff9 content/56: bcadfc362b69078beee0088e5936c98b - content/57: 437fae06c917576e309864634ac006d5 + content/57: 47be5344f0c8a9ede380f37f769b5b3f content/58: 8d41bb08f7d4000b665e6786583aa2b5 content/59: 48adb1980e062be3783331522082edaf content/60: 371d0e46b4bd2c23f559b8bc112f6955 content/61: b89e1b7a15f022c0c13adadb25e3f49d content/62: bcadfc362b69078beee0088e5936c98b - content/63: 74e0d576bddb246c672c98a8e9f4fd32 + content/63: 44b209460093fb955b8f6b4e575dde17 content/64: c02f43d19361be7571e8141a61e83980 content/65: 21a0f57793fb19dd8761b644e22ee731 content/66: 371d0e46b4bd2c23f559b8bc112f6955 content/67: 6aa3e815a37986856a2781ccfdb7b4de content/68: bcadfc362b69078beee0088e5936c98b - content/69: b76190aa5e84cb17cdcf2e061edf706b + content/69: 9032d6a71c23f90a39232d653c3daf36 content/70: 1845561cd920176e2dbfed65eaccca9e content/71: 2960e1e609b8c512f5cf1ef715c2a684 content/72: 371d0e46b4bd2c23f559b8bc112f6955 content/73: e3586b13bcb7c91515437d76a0027201 content/74: bcadfc362b69078beee0088e5936c98b - content/75: 520c03c754968a673ed5def1706f919d + content/75: f14261cdc2105f3c2380a90629edc172 content/76: 14a4d1b2c2f1257eadb4b26b85672fab content/77: 45bd9b1b321f85dc31c720051c12f681 content/78: 371d0e46b4bd2c23f559b8bc112f6955 content/79: 176f866fa8ad12f179eb5466bc968914 content/80: bcadfc362b69078beee0088e5936c98b - content/81: 1fc32b27418b8efe2abba1beb6d31868 + content/81: 8f1d8635d9e542fbce4ee4167d8a2bb1 content/82: 5c0b4adc7825b3ed5831bf6c4d83a6a2 content/83: cace0c917728a3a5bc93d26dd65669f7 content/84: 371d0e46b4bd2c23f559b8bc112f6955 content/85: 490b3a3ef8840f1e01a58c6875af027b content/86: bcadfc362b69078beee0088e5936c98b - content/87: f4c4cbcb48dbbd87b27fdf76105aaa8c + content/87: 501cead9242b6febffe4659a63cef613 content/88: efa34ea34fd3d30088470cf6f4476106 content/89: 47fc6e4fd184baaf72d61223ec944148 content/90: 371d0e46b4bd2c23f559b8bc112f6955 content/91: 43349c22c04743b654d4aee849cc81ff content/92: bcadfc362b69078beee0088e5936c98b - content/93: 82634f835e924e6b2242df6127f0969a + content/93: a53a400b7cddac3a34435d23332db795 content/94: e3a5c53a79de7fe47abd7f7a9f86fb65 content/95: 2053815e47b54488983f0571f49cd11a content/96: 371d0e46b4bd2c23f559b8bc112f6955 content/97: 846be448c3cedb87e4bd923c63e04512 content/98: bcadfc362b69078beee0088e5936c98b - content/99: 11608f282141ee6c31f933f6c2fcaa0d + content/99: d227cd028bf20eab7f826827efa9ea90 content/100: 17d1e59a4290138d979568f39e6fea9b content/101: cf9c3c1b441bde10b35d04d776e9f5ce content/102: 371d0e46b4bd2c23f559b8bc112f6955 content/103: 599eb9d3e6b88cba7a15d007ce1111f4 content/104: bcadfc362b69078beee0088e5936c98b - content/105: ad1bd5c40adabc9f2a97682be1671d67 + content/105: 09d70143b5598699ae1ed593baa4ac61 content/106: afa20ccc5f708cf36a0cb6ede6ec0c4f content/107: 8a64259005d325f6849527186097f390 content/108: 371d0e46b4bd2c23f559b8bc112f6955 content/109: c2b8a2c90d216d94f24850c8124849ef content/110: bcadfc362b69078beee0088e5936c98b - content/111: b5e005f9e95aead5c9596968b21821e1 + content/111: 6bab23a5c82acbd6cd79fbdfc9bbcbde content/112: 05141d844a911fb66fd7bdc2b98e8160 content/113: f04861fc73d9abc76dd8e140703baa14 content/114: 371d0e46b4bd2c23f559b8bc112f6955 content/115: 09a884f6fcddd37ad4968718fb48db9e content/116: bcadfc362b69078beee0088e5936c98b - content/117: 62695bf37dd3fe43470266996e09ef86 + content/117: a1645115447094e7520eb3d45244a3c8 content/118: 96b30990733f35886cb04bc6bae18613 content/119: b22baefc2beca09303baa0778b27a4d6 content/120: 371d0e46b4bd2c23f559b8bc112f6955 content/121: 9b936f3424feac6551b709d0c5d5713c content/122: bcadfc362b69078beee0088e5936c98b - content/123: 1a249d65588fd4a33bb50af768870bbc + content/123: 6cd2f15ea11b6f07e6ac7e170a90a91f content/124: 573661fdf0cd751a2433052dff8dcdfe content/125: 81d70f8bd307578a5814374f31a7d6c2 content/126: 371d0e46b4bd2c23f559b8bc112f6955 content/127: d04dcb9d64bc76606af4f9309eeacb75 content/128: bcadfc362b69078beee0088e5936c98b - content/129: 7a78c9363ed6fba5d7277a24186c7296 + content/129: b6b0fd5e140401e9f4c4c8a0e5ab0da1 content/130: ce10caa6dcfe95e58c32db33157d989a content/131: 49df30ca91d4139a38b591311b7a83a8 content/132: 371d0e46b4bd2c23f559b8bc112f6955 content/133: b61b7e3b5e5b7c05fcb65478d744abb5 content/134: bcadfc362b69078beee0088e5936c98b - content/135: 9ec868a621316d03619fc37582084053 + content/135: 310e65d225fb68cf48f1d44d1047ea12 content/136: 805790ac8b4ae77c30fbfc9f6023bac8 content/137: 1a4e93e8a49abd71333809a3bc0856c9 content/138: 371d0e46b4bd2c23f559b8bc112f6955 content/139: 33fde4c3da4584b51f06183b7b192a78 content/140: bcadfc362b69078beee0088e5936c98b - content/141: a4e11bf0073b1f1e45c07e3c1c7dd969 + content/141: b7451190f100388d999c183958d787a7 content/142: b3f310d5ef115bea5a8b75bf25d7ea9a content/143: 4930918f803340baa861bed9cdf789de 8f76e389f6226f608571622b015ca6a1: From f0dc8e81d9f6a456a3d7e59f1ee3f363f6f6a8a1 Mon Sep 17 00:00:00 2001 From: Waleed Date: Mon, 15 Dec 2025 19:00:52 -0800 Subject: [PATCH 16/17] fix(build): downgrade nextjs from canary to 16.0.9 (#2394) --- bun.lock | 3 ++- package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bun.lock b/bun.lock index fa7ace079..dce8c8e5d 100644 --- a/bun.lock +++ b/bun.lock @@ -18,6 +18,7 @@ "isolated-vm": "6.0.2", "mongodb": "6.19.0", "neo4j-driver": "6.0.1", + "next-runtime-env": "3.3.0", "nodemailer": "7.0.11", "onedollarstats": "0.0.10", "postgres": "^3.4.5", @@ -28,7 +29,7 @@ }, "devDependencies": { "@biomejs/biome": "2.0.0-beta.5", - "@next/env": "16.0.9", + "@next/env": "16.1.0-canary.21", "@octokit/rest": "^21.0.0", "@tailwindcss/typography": "0.5.19", "@types/nodemailer": "7.0.4", diff --git a/package.json b/package.json index 4e0b8b9f4..fed21b437 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ }, "dependencies": { "@linear/sdk": "40.0.0", + "next-runtime-env": "3.3.0", "@modelcontextprotocol/sdk": "1.20.2", "@t3-oss/env-nextjs": "0.13.4", "@tanstack/react-query": "5.90.8", @@ -57,7 +58,7 @@ }, "devDependencies": { "@biomejs/biome": "2.0.0-beta.5", - "@next/env": "16.0.9", + "@next/env": "16.1.0-canary.21", "@octokit/rest": "^21.0.0", "@tailwindcss/typography": "0.5.19", "@types/nodemailer": "7.0.4", From 0e6a1315d0f053a9a235d9bedabc0a600b748c8e Mon Sep 17 00:00:00 2001 From: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:21:21 -0800 Subject: [PATCH 17/17] improvement: workflow loading, sidebar scrolling (#2322) * improvement: workflow loading, sidebar scrolling * further optimizations * remove redundant perms calls * remove redundant api calls * use displayNodes local state to make dragging smooth even in larger workflows * improvement(logs): trace span output styling * fix(s-modal): sidebar overflow scrolling * fix(footer): guardrails link * improvement(loading): spinner * refactor(training-modal): changed file name * improvement(spinner): optimize spinner in background --------- Co-authored-by: Vikhyath Mondreti --- .../app/(landing)/components/footer/consts.ts | 2 +- .../app/workspace/[workspaceId]/layout.tsx | 2 +- .../components/trace-spans/trace-spans.tsx | 25 +- .../training-modal.tsx | 0 .../workflow-edge/workflow-edge.tsx | 109 +-- .../hooks/use-current-workflow.ts | 37 +- .../[workspaceId]/w/[workflowId]/layout.tsx | 2 +- .../[workspaceId]/w/[workflowId]/workflow.tsx | 877 ++++++++---------- .../team-management/team-management.tsx | 5 +- .../settings-modal/settings-modal.tsx | 4 +- .../workspace-header/workspace-header.tsx | 298 +++--- .../w/components/sidebar/sidebar.tsx | 8 +- .../app/workspace/[workspaceId]/w/page.tsx | 16 +- apps/sim/app/workspace/page.tsx | 5 +- .../workspace/providers/socket-provider.tsx | 6 +- .../emcn/components/s-modal/s-modal.tsx | 8 +- apps/sim/hooks/queries/organization.ts | 5 +- apps/sim/stores/workflows/workflow/store.ts | 3 - 18 files changed, 672 insertions(+), 740 deletions(-) rename apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/{training-controls => training-modal}/training-modal.tsx (100%) diff --git a/apps/sim/app/(landing)/components/footer/consts.ts b/apps/sim/app/(landing)/components/footer/consts.ts index f45b6d64d..7e55d03a2 100644 --- a/apps/sim/app/(landing)/components/footer/consts.ts +++ b/apps/sim/app/(landing)/components/footer/consts.ts @@ -4,6 +4,7 @@ export const FOOTER_BLOCKS = [ 'Condition', 'Evaluator', 'Function', + 'Guardrails', 'Human In The Loop', 'Loop', 'Parallel', @@ -30,7 +31,6 @@ export const FOOTER_TOOLS = [ 'GitHub', 'Gmail', 'Google Drive', - 'Guardrails', 'HubSpot', 'HuggingFace', 'Hunter', diff --git a/apps/sim/app/workspace/[workspaceId]/layout.tsx b/apps/sim/app/workspace/[workspaceId]/layout.tsx index 8166860e7..8b5d1093a 100644 --- a/apps/sim/app/workspace/[workspaceId]/layout.tsx +++ b/apps/sim/app/workspace/[workspaceId]/layout.tsx @@ -14,7 +14,7 @@ export default function WorkspaceLayout({ children }: { children: React.ReactNod -
+
diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-spans/trace-spans.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-spans/trace-spans.tsx index db01dff5e..49deb4955 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-spans/trace-spans.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-spans/trace-spans.tsx @@ -398,7 +398,7 @@ function InputOutputSection({ }, [data]) return ( -
+
onToggle(sectionKey)} @@ -436,7 +436,7 @@ function InputOutputSection({ )} @@ -477,7 +477,7 @@ function NestedBlockItem({ const isChildrenExpanded = expandedChildren.has(spanId) return ( -
+
+
{span.children!.map((child, childIndex) => ( -
+
0 && isCardExpanded && ( -
+
{inlineChildren.map((childSpan, index) => ( +
{[...toolCallSpans, ...inlineChildren].map((childSpan, index) => { const childId = childSpan.id || `${spanId}-inline-${index}` const childIsError = childSpan.status === 'error' @@ -677,7 +677,10 @@ function TraceSpanItem({ ) return ( -
+
+
{childSpan.children!.map((nestedChild, nestedIndex) => ( +
Trace Span -
+
{normalizedSpans.map((span, index) => ( state.diffAnalysis) - const isShowingDiff = useWorkflowDiffStore((state) => state.isShowingDiff) - const isDiffReady = useWorkflowDiffStore((state) => state.isDiffReady) + // Combined store subscription to reduce subscription overhead + const { diffAnalysis, isShowingDiff, isDiffReady } = useWorkflowDiffStore( + useShallow((state) => ({ + diffAnalysis: state.diffAnalysis, + isShowingDiff: state.isShowingDiff, + isDiffReady: state.isDiffReady, + })) + ) const lastRunEdges = useExecutionStore((state) => state.lastRunEdges) - const generateEdgeIdentity = ( - sourceId: string, - targetId: string, - sourceHandle?: string | null, - targetHandle?: string | null - ): string => { - const actualSourceHandle = sourceHandle || 'source' - const actualTargetHandle = targetHandle || 'target' - return `${sourceId}-${actualSourceHandle}-${targetId}-${actualTargetHandle}` - } - - const edgeIdentifier = generateEdgeIdentity(source, target, sourceHandle, targetHandle) - - let edgeDiffStatus: EdgeDiffStatus = null - - if (data?.isDeleted) { - edgeDiffStatus = 'deleted' - } else if (diffAnalysis?.edge_diff && edgeIdentifier && isDiffReady) { - if (isShowingDiff) { - if (diffAnalysis.edge_diff.new_edges.includes(edgeIdentifier)) { - edgeDiffStatus = 'new' - } else if (diffAnalysis.edge_diff.unchanged_edges.includes(edgeIdentifier)) { - edgeDiffStatus = 'unchanged' - } - } else { - if (diffAnalysis.edge_diff.deleted_edges.includes(edgeIdentifier)) { - edgeDiffStatus = 'deleted' - } - } - } - const dataSourceHandle = (data as { sourceHandle?: string } | undefined)?.sourceHandle const isErrorEdge = (sourceHandle ?? dataSourceHandle) === 'error' - - // Check if this edge was traversed during last execution const edgeRunStatus = lastRunEdges.get(id) - const getEdgeColor = () => { - if (edgeDiffStatus === 'deleted') return 'var(--text-error)' - if (isErrorEdge) return 'var(--text-error)' - if (edgeDiffStatus === 'new') return 'var(--brand-tertiary)' - // Show run path status if edge was traversed - if (edgeRunStatus === 'success') return 'var(--border-success)' - if (edgeRunStatus === 'error') return 'var(--text-error)' - return 'var(--surface-12)' - } + // Memoize diff status calculation to avoid recomputing on every render + const edgeDiffStatus = useMemo((): EdgeDiffStatus => { + if (data?.isDeleted) return 'deleted' + if (!diffAnalysis?.edge_diff || !isDiffReady) return null - const edgeStyle = { - ...(style ?? {}), - strokeWidth: edgeDiffStatus ? 3 : isSelected ? 2.5 : 2, - stroke: getEdgeColor(), - strokeDasharray: edgeDiffStatus === 'deleted' ? '10,5' : undefined, - opacity: edgeDiffStatus === 'deleted' ? 0.7 : isSelected ? 0.5 : 1, - } + const actualSourceHandle = sourceHandle || 'source' + const actualTargetHandle = targetHandle || 'target' + const edgeIdentifier = `${source}-${actualSourceHandle}-${target}-${actualTargetHandle}` + + if (isShowingDiff) { + if (diffAnalysis.edge_diff.new_edges.includes(edgeIdentifier)) return 'new' + if (diffAnalysis.edge_diff.unchanged_edges.includes(edgeIdentifier)) return 'unchanged' + } else { + if (diffAnalysis.edge_diff.deleted_edges.includes(edgeIdentifier)) return 'deleted' + } + return null + }, [ + data?.isDeleted, + diffAnalysis, + isDiffReady, + isShowingDiff, + source, + target, + sourceHandle, + targetHandle, + ]) + + // Memoize edge style to prevent object recreation + const edgeStyle = useMemo(() => { + let color = 'var(--surface-12)' + if (edgeDiffStatus === 'deleted') color = 'var(--text-error)' + else if (isErrorEdge) color = 'var(--text-error)' + else if (edgeDiffStatus === 'new') color = 'var(--brand-tertiary)' + else if (edgeRunStatus === 'success') color = 'var(--border-success)' + else if (edgeRunStatus === 'error') color = 'var(--text-error)' + + return { + ...(style ?? {}), + strokeWidth: edgeDiffStatus ? 3 : isSelected ? 2.5 : 2, + stroke: color, + strokeDasharray: edgeDiffStatus === 'deleted' ? '10,5' : undefined, + opacity: edgeDiffStatus === 'deleted' ? 0.7 : isSelected ? 0.5 : 1, + } + }, [style, edgeDiffStatus, isSelected, isErrorEdge, edgeRunStatus]) return ( <> @@ -148,3 +149,5 @@ export const WorkflowEdge = ({ ) } + +export const WorkflowEdge = memo(WorkflowEdgeComponent) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts index 6ba5978f2..b73fe534c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow.ts @@ -43,26 +43,29 @@ export interface CurrentWorkflow { */ export function useCurrentWorkflow(): CurrentWorkflow { // Get normal workflow state - optimized with shallow comparison - // This prevents re-renders when only subblock values change (not block structure) const normalWorkflow = useWorkflowStore( - useShallow((state) => { - const workflow = state.getWorkflowState() - return { - blocks: workflow.blocks, - edges: workflow.edges, - loops: workflow.loops, - parallels: workflow.parallels, - lastSaved: workflow.lastSaved, - isDeployed: workflow.isDeployed, - deployedAt: workflow.deployedAt, - deploymentStatuses: workflow.deploymentStatuses, - needsRedeployment: workflow.needsRedeployment, - } - }) + useShallow((state) => ({ + blocks: state.blocks, + edges: state.edges, + loops: state.loops, + parallels: state.parallels, + lastSaved: state.lastSaved, + isDeployed: state.isDeployed, + deployedAt: state.deployedAt, + deploymentStatuses: state.deploymentStatuses, + needsRedeployment: state.needsRedeployment, + })) ) - // Get diff state - now including isDiffReady - const { isShowingDiff, isDiffReady, hasActiveDiff, baselineWorkflow } = useWorkflowDiffStore() + // Get diff state - optimized with shallow comparison + const { isShowingDiff, isDiffReady, hasActiveDiff, baselineWorkflow } = useWorkflowDiffStore( + useShallow((state) => ({ + isShowingDiff: state.isShowingDiff, + isDiffReady: state.isDiffReady, + hasActiveDiff: state.hasActiveDiff, + baselineWorkflow: state.baselineWorkflow, + })) + ) // Create the abstracted interface - optimized to prevent unnecessary re-renders const currentWorkflow = useMemo((): CurrentWorkflow => { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/layout.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/layout.tsx index 3f5daa765..122a26ef9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/layout.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/layout.tsx @@ -2,7 +2,7 @@ import { ErrorBoundary } from '@/app/workspace/[workspaceId]/w/[workflowId]/comp export default function WorkflowLayout({ children }: { children: React.ReactNode }) { return ( -
+
{children}
) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index c28ff8dc2..edb40a9ae 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -1,22 +1,25 @@ 'use client' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useParams, useRouter } from 'next/navigation' import ReactFlow, { + applyNodeChanges, ConnectionLineType, type Edge, type EdgeTypes, + type Node, + type NodeChange, type NodeTypes, ReactFlowProvider, useReactFlow, } from 'reactflow' import 'reactflow/dist/style.css' -import { Loader2 } from 'lucide-react' +import { useShallow } from 'zustand/react/shallow' import type { OAuthConnectEventDetail } from '@/lib/copilot/tools/client/other/oauth-request-access' import { createLogger } from '@/lib/logs/console/logger' import type { OAuthProvider } from '@/lib/oauth' import { TriggerUtils } from '@/lib/workflows/triggers/triggers' -import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' +import { useWorkspacePermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { CommandList, DiffControls, @@ -25,12 +28,10 @@ import { SubflowNodeComponent, Terminal, } from '@/app/workspace/[workspaceId]/w/[workflowId]/components' -import { Chat } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat' import { Cursors } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/cursors/cursors' import { ErrorBoundary } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/error/index' import { NoteBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/note-block/note-block' -import { OAuthRequiredModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' -import { TrainingModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/training-controls/training-modal' +import { TrainingModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/training-modal/training-modal' import { WorkflowBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block' import { WorkflowEdge } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-edge/workflow-edge' import { @@ -44,7 +45,6 @@ import { isAnnotationOnlyBlock } from '@/executor/constants' import { useWorkspaceEnvironment } from '@/hooks/queries/environment' import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow' import { useStreamCleanup } from '@/hooks/use-stream-cleanup' -import { useWorkspacePermissions } from '@/hooks/use-workspace-permissions' import { useCopilotTrainingStore } from '@/stores/copilot-training/store' import { useExecutionStore } from '@/stores/execution/store' import { useNotificationStore } from '@/stores/notifications/store' @@ -56,20 +56,34 @@ import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { getUniqueBlockName } from '@/stores/workflows/utils' import { useWorkflowStore } from '@/stores/workflows/workflow/store' +/** Lazy-loaded components for non-critical UI that can load after initial render */ +const LazyChat = lazy(() => + import('@/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat').then((mod) => ({ + default: mod.Chat, + })) +) +const LazyOAuthRequiredModal = lazy(() => + import( + '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal' + ).then((mod) => ({ default: mod.OAuthRequiredModal })) +) + const logger = createLogger('Workflow') -// Define custom node and edge types - memoized outside component to prevent re-creation +/** Custom node types for ReactFlow. */ const nodeTypes: NodeTypes = { workflowBlock: WorkflowBlock, noteBlock: NoteBlock, subflowNode: SubflowNodeComponent, } + +/** Custom edge types for ReactFlow. */ const edgeTypes: EdgeTypes = { default: WorkflowEdge, - workflowEdge: WorkflowEdge, // Keep for backward compatibility + workflowEdge: WorkflowEdge, } -// Memoized ReactFlow props to prevent unnecessary re-renders +/** ReactFlow configuration constants. */ const defaultEdgeOptions = { type: 'custom' } const snapGrid: [number, number] = [20, 20] const reactFlowFitViewOptions = { padding: 0.6 } as const @@ -78,7 +92,7 @@ const reactFlowProOptions = { hideAttribution: true } as const interface SelectedEdgeInfo { id: string parentLoopId?: string - contextId?: string // Unique identifier combining edge ID and context + contextId?: string } interface BlockData { @@ -88,16 +102,14 @@ interface BlockData { distance: number } +/** + * Main workflow canvas content component. + * Renders the ReactFlow canvas with blocks, edges, and all interactive features. + */ const WorkflowContent = React.memo(() => { - // State - - // State for tracking node dragging - const [draggedNodeId, setDraggedNodeId] = useState(null) + const [isCanvasReady, setIsCanvasReady] = useState(false) const [potentialParentId, setPotentialParentId] = useState(null) - // Enhanced edge selection with parent context and unique identifier const [selectedEdgeInfo, setSelectedEdgeInfo] = useState(null) - - // Track whether the active connection drag started from an error handle const [isErrorConnectionDrag, setIsErrorConnectionDrag] = useState(false) const [oauthModal, setOauthModal] = useState<{ provider: OAuthProvider @@ -107,32 +119,35 @@ const WorkflowContent = React.memo(() => { newScopes?: string[] } | null>(null) - // Hooks const params = useParams() const router = useRouter() const { screenToFlowPosition, getNodes, fitView } = useReactFlow() const { emitCursorUpdate } = useSocket() - // Get workspace ID from the params const workspaceId = params.workspaceId as string const workflowIdParam = params.workflowId as string - // Notification store const addNotification = useNotificationStore((state) => state.addNotification) - const { workflows, activeWorkflowId, hydration, setActiveWorkflow } = useWorkflowRegistry() + const { workflows, activeWorkflowId, hydration, setActiveWorkflow } = useWorkflowRegistry( + useShallow((state) => ({ + workflows: state.workflows, + activeWorkflowId: state.activeWorkflowId, + hydration: state.hydration, + setActiveWorkflow: state.setActiveWorkflow, + })) + ) - // Use the clean abstraction for current workflow state const currentWorkflow = useCurrentWorkflow() - const { - updateNodeDimensions, - updateBlockPosition: storeUpdateBlockPosition, - setDragStartPosition, - getDragStartPosition, - } = useWorkflowStore() + const { updateNodeDimensions, setDragStartPosition, getDragStartPosition } = useWorkflowStore( + useShallow((state) => ({ + updateNodeDimensions: state.updateNodeDimensions, + setDragStartPosition: state.setDragStartPosition, + getDragStartPosition: state.getDragStartPosition, + })) + ) - // Get copilot cleanup function const copilotCleanup = useCopilotStore((state) => state.cleanup) // Training modal state @@ -141,17 +156,18 @@ const WorkflowContent = React.memo(() => { // Handle copilot stream cleanup on page unload and component unmount useStreamCleanup(copilotCleanup) - // Extract workflow data from the abstraction const { blocks, edges, isDiffMode, lastSaved } = currentWorkflow - const isWorkflowReady = - hydration.phase === 'ready' && - hydration.workflowId === workflowIdParam && - activeWorkflowId === workflowIdParam && - Boolean(workflows[workflowIdParam]) && - lastSaved !== undefined + const isWorkflowReady = useMemo( + () => + hydration.phase === 'ready' && + hydration.workflowId === workflowIdParam && + activeWorkflowId === workflowIdParam && + Boolean(workflows[workflowIdParam]) && + lastSaved !== undefined, + [hydration.phase, hydration.workflowId, workflowIdParam, activeWorkflowId, workflows, lastSaved] + ) - // Node utilities hook for position/hierarchy calculations (requires blocks) const { getNodeDepth, getNodeHierarchy, @@ -162,23 +178,16 @@ const WorkflowContent = React.memo(() => { getNodeAnchorPosition, } = useNodeUtilities(blocks) - /** - * Wrapper to call resizeLoopNodes with immediate execution. - * No delays for responsive subflow resizing. - */ + /** Triggers immediate subflow resize without delays. */ const resizeLoopNodesWrapper = useCallback(() => { return resizeLoopNodes(updateNodeDimensions) }, [resizeLoopNodes, updateNodeDimensions]) - // Auto-layout hook const { applyAutoLayoutAndUpdateStore } = useAutoLayout(activeWorkflowId || null) - // Check if workflow is empty (no blocks) - const isWorkflowEmpty = useMemo(() => { - return Object.keys(blocks).length === 0 - }, [blocks]) + const isWorkflowEmpty = useMemo(() => Object.keys(blocks).length === 0, [blocks]) - // Listen for global OAuth connect events (from Copilot tool) + /** Handles OAuth connect events dispatched by Copilot tools. */ useEffect(() => { const handleOpenOAuthConnect = (event: Event) => { const detail = (event as CustomEvent).detail @@ -197,52 +206,45 @@ const WorkflowContent = React.memo(() => { window.removeEventListener('open-oauth-connect', handleOpenOAuthConnect as EventListener) }, []) - // Get diff analysis for edge reconstruction const { diffAnalysis, isShowingDiff, isDiffReady, reapplyDiffMarkers, hasActiveDiff } = - useWorkflowDiffStore() + useWorkflowDiffStore( + useShallow((state) => ({ + diffAnalysis: state.diffAnalysis, + isShowingDiff: state.isShowingDiff, + isDiffReady: state.isDiffReady, + reapplyDiffMarkers: state.reapplyDiffMarkers, + hasActiveDiff: state.hasActiveDiff, + })) + ) - // Re-apply diff markers when blocks change (e.g., after socket rehydration) + /** Re-applies diff markers when blocks change after socket rehydration. */ const blocksRef = useRef(blocks) useEffect(() => { + if (!isWorkflowReady) return if (hasActiveDiff && isDiffReady && blocks !== blocksRef.current) { blocksRef.current = blocks - // Use setTimeout to ensure the store update has settled - setTimeout(() => { - reapplyDiffMarkers() - }, 0) + setTimeout(() => reapplyDiffMarkers(), 0) } - }, [blocks, hasActiveDiff, isDiffReady, reapplyDiffMarkers]) + }, [blocks, hasActiveDiff, isDiffReady, reapplyDiffMarkers, isWorkflowReady]) - // Reconstruct deleted edges when viewing original workflow and filter out invalid edges + /** Reconstructs deleted edges for diff view and filters invalid edges. */ const edgesForDisplay = useMemo(() => { let edgesToFilter = edges - // If we're not in diff mode and we have diff analysis with deleted edges, - // we need to reconstruct those deleted edges and add them to the display - // Only do this if diff is ready to prevent race conditions if (!isShowingDiff && isDiffReady && diffAnalysis?.edge_diff?.deleted_edges) { const reconstructedEdges: Edge[] = [] + const validHandles = ['source', 'target', 'success', 'error', 'default', 'condition'] - // Parse deleted edge identifiers to reconstruct edges diffAnalysis.edge_diff.deleted_edges.forEach((edgeIdentifier) => { - // Edge identifier format: "sourceId-sourceHandle-targetId-targetHandle" - // Split by '-' and extract components const parts = edgeIdentifier.split('-') if (parts.length >= 4) { - // Find the index where targetId starts (after the source handle) - // We need to handle cases where IDs contain hyphens let sourceEndIndex = -1 let targetStartIndex = -1 - // Look for valid handle names to identify boundaries - const validHandles = ['source', 'target', 'success', 'error', 'default', 'condition'] - for (let i = 1; i < parts.length - 1; i++) { if (validHandles.includes(parts[i])) { sourceEndIndex = i - // Find the next part that could be the start of targetId for (let j = i + 1; j < parts.length - 1; j++) { - // Check if this could be a valid target ID start if (parts[j].length > 0) { targetStartIndex = j break @@ -258,52 +260,38 @@ const WorkflowContent = React.memo(() => { const targetHandle = parts[parts.length - 1] const targetId = parts.slice(targetStartIndex, parts.length - 1).join('-') - // Only reconstruct if both blocks still exist if (blocks[sourceId] && blocks[targetId]) { - // Generate a unique edge ID - const edgeId = `deleted-${sourceId}-${sourceHandle}-${targetId}-${targetHandle}` - reconstructedEdges.push({ - id: edgeId, + id: `deleted-${sourceId}-${sourceHandle}-${targetId}-${targetHandle}`, source: sourceId, target: targetId, sourceHandle, targetHandle, type: 'workflowEdge', - data: { isDeleted: true }, // Mark as deleted for styling + data: { isDeleted: true }, }) } } } }) - // Combine existing edges with reconstructed deleted edges edgesToFilter = [...edges, ...reconstructedEdges] } - // Filter out edges that connect to/from annotation-only blocks (note blocks) - // These blocks don't have handles and shouldn't have connections return edgesToFilter.filter((edge) => { const sourceBlock = blocks[edge.source] const targetBlock = blocks[edge.target] - - // Remove edge if either source or target is an annotation-only block if (!sourceBlock || !targetBlock) return false - if (isAnnotationOnlyBlock(sourceBlock.type) || isAnnotationOnlyBlock(targetBlock.type)) { - return false - } - - return true + return !isAnnotationOnlyBlock(sourceBlock.type) && !isAnnotationOnlyBlock(targetBlock.type) }) }, [edges, isShowingDiff, isDiffReady, diffAnalysis, blocks]) - // User permissions - get current user's specific permissions from context - const userPermissions = useUserPermissionsContext() + const { userPermissions, workspacePermissions, permissionsError } = + useWorkspacePermissionsContext() - // Create diff-aware permissions that disable editing when in diff mode + /** Returns read-only permissions when viewing snapshot, otherwise user permissions. */ const effectivePermissions = useMemo(() => { if (currentWorkflow.isSnapshotView) { - // Snapshot view is read-only return { ...userPermissions, canEdit: false, @@ -314,12 +302,6 @@ const WorkflowContent = React.memo(() => { return userPermissions }, [userPermissions, currentWorkflow.isSnapshotView]) - // Workspace permissions - get all users and their permissions for this workspace - const { permissions: workspacePermissions, error: permissionsError } = useWorkspacePermissions( - workspaceId || null - ) - - // Store access const { collaborativeAddBlock: addBlock, collaborativeAddEdge: addEdge, @@ -332,24 +314,33 @@ const WorkflowContent = React.memo(() => { redo, } = useCollaborativeWorkflow() - // Execution and debug mode state - const { activeBlockIds, pendingBlocks, isDebugging } = useExecutionStore() + const { activeBlockIds, pendingBlocks, isDebugging } = useExecutionStore( + useShallow((state) => ({ + activeBlockIds: state.activeBlockIds, + pendingBlocks: state.pendingBlocks, + isDebugging: state.isDebugging, + })) + ) + const [dragStartParentId, setDragStartParentId] = useState(null) - /** - * Dynamic connection line style that changes color based on the source handle - * Error handles render red connection lines to match error edges - */ - const connectionLineStyle = useMemo(() => { - return { + /** Connection line style - red for error handles, default otherwise. */ + const connectionLineStyle = useMemo( + () => ({ stroke: isErrorConnectionDrag ? 'var(--text-error)' : 'var(--surface-12)', strokeWidth: 2, - } - }, [isErrorConnectionDrag]) + }), + [isErrorConnectionDrag] + ) - // Log permissions when they load + /** Logs permission loading results for debugging. */ useEffect(() => { - if (workspacePermissions) { + if (permissionsError) { + logger.error('Failed to load workspace permissions', { + workspaceId, + error: permissionsError, + }) + } else if (workspacePermissions) { logger.info('Workspace permissions loaded in workflow', { workspaceId, userCount: workspacePermissions.total, @@ -359,17 +350,7 @@ const WorkflowContent = React.memo(() => { })), }) } - }, [workspacePermissions, workspaceId]) - - // Log permissions errors - useEffect(() => { - if (permissionsError) { - logger.error('Failed to load workspace permissions', { - workspaceId, - error: permissionsError, - }) - } - }, [permissionsError, workspaceId]) + }, [workspacePermissions, permissionsError, workspaceId]) const updateNodeParent = useCallback( (nodeId: string, newParentId: string | null, affectedEdges: any[] = []) => { @@ -442,7 +423,7 @@ const WorkflowContent = React.memo(() => { ] ) - // Auto-layout handler - uses the hook for immediate frontend updates + /** Applies auto-layout to the workflow canvas. */ const handleAutoLayout = useCallback(async () => { if (Object.keys(blocks).length === 0) return @@ -514,7 +495,6 @@ const WorkflowContent = React.memo(() => { (blockId: string, edgesToRemove: Edge[]): void => { if (edgesToRemove.length === 0) return - // Skip individual edge recording - parent update will record as batch window.dispatchEvent(new CustomEvent('skip-edge-recording', { detail: { skip: true } })) try { @@ -533,7 +513,7 @@ const WorkflowContent = React.memo(() => { [removeEdge] ) - // Listen for explicit remove-from-subflow actions from ActionBar + /** Handles ActionBar remove-from-subflow events. */ useEffect(() => { const handleRemoveFromSubflow = (event: Event) => { const customEvent = event as CustomEvent<{ blockId: string }> @@ -543,18 +523,12 @@ const WorkflowContent = React.memo(() => { try { const currentBlock = blocks[blockId] const parentId = currentBlock?.data?.parentId - if (!parentId) return - // Find ALL edges connected to this block const edgesToRemove = edgesForDisplay.filter( (e) => e.source === blockId || e.target === blockId ) - - // Remove edges using shared helper removeEdgesForNode(blockId, edgesToRemove) - - // Update parent relationship (null = remove from parent) updateNodeParent(blockId, null, edgesToRemove) } catch (err) { logger.error('Failed to remove from subflow', { err }) @@ -566,10 +540,9 @@ const WorkflowContent = React.memo(() => { window.removeEventListener('remove-from-subflow', handleRemoveFromSubflow as EventListener) }, [blocks, edgesForDisplay, removeEdgesForNode, updateNodeParent]) - // Handle drops + /** Finds the closest block to a position for auto-connect. */ const findClosestOutput = useCallback( (newNodePosition: { x: number; y: number }): BlockData | null => { - // Determine if drop is inside a container; if not, exclude child nodes from candidates const containerAtPoint = isPointInLoopNode(newNodePosition) const nodeIndex = new Map(getNodes().map((n) => [n.id, n])) @@ -604,33 +577,22 @@ const WorkflowContent = React.memo(() => { [blocks, getNodes, getNodeAnchorPosition, isPointInLoopNode] ) - // Determine the appropriate source handle based on block type + /** Determines the appropriate source handle based on block type. */ const determineSourceHandle = useCallback((block: { id: string; type: string }) => { - // Default source handle - let sourceHandle = 'source' - - // For condition blocks, use the first condition handle if (block.type === 'condition') { - // Get just the first condition handle from the DOM const conditionHandles = document.querySelectorAll( `[data-nodeid^="${block.id}"][data-handleid^="condition-"]` ) if (conditionHandles.length > 0) { - // Extract the full handle ID from the first condition handle const handleId = conditionHandles[0].getAttribute('data-handleid') - if (handleId) { - sourceHandle = handleId - } + if (handleId) return handleId } - } - // For loop and parallel nodes, use their end source handle - else if (block.type === 'loop') { - sourceHandle = 'loop-end-source' + } else if (block.type === 'loop') { + return 'loop-end-source' } else if (block.type === 'parallel') { - sourceHandle = 'parallel-end-source' + return 'parallel-end-source' } - - return sourceHandle + return 'source' }, []) /** @@ -648,45 +610,35 @@ const WorkflowContent = React.memo(() => { if (!data.type || data.type === 'connectionBlock') return try { - // Check if dropping inside a container node (loop or parallel) const containerInfo = isPointInLoopNode(position) - // Clear any drag-over styling document .querySelectorAll('.loop-node-drag-over, .parallel-node-drag-over') - .forEach((el) => { - el.classList.remove('loop-node-drag-over', 'parallel-node-drag-over') - }) + .forEach((el) => el.classList.remove('loop-node-drag-over', 'parallel-node-drag-over')) document.body.style.cursor = '' - // Ensure any toolbar drag flags are cleared on drop document.body.classList.remove('sim-drag-subflow') - // Special handling for container nodes (loop or parallel) dragged from toolbar if (data.type === 'loop' || data.type === 'parallel') { - // Create a unique ID and name for the container const id = crypto.randomUUID() const baseName = data.type === 'loop' ? 'Loop' : 'Parallel' const name = getUniqueBlockName(baseName, blocks) - // Subflows cannot be dropped inside other subflows - always add to main canvas const isAutoConnectEnabled = useGeneralStore.getState().isAutoConnectEnabled let autoConnectEdge if (isAutoConnectEnabled) { const closestBlock = findClosestOutput(position) if (closestBlock) { - const sourceHandle = determineSourceHandle(closestBlock) autoConnectEdge = { id: crypto.randomUUID(), source: closestBlock.id, target: id, - sourceHandle, + sourceHandle: determineSourceHandle(closestBlock), targetHandle: 'target', type: 'workflowEdge', } } } - // Add the container node with default dimensions and auto-connect edge addBlock( id, data.type, @@ -912,7 +864,7 @@ const WorkflowContent = React.memo(() => { ] ) - // Listen for toolbar block click events + /** Handles toolbar block click events to add blocks to the canvas. */ useEffect(() => { const handleAddBlockFromToolbar = (event: CustomEvent) => { // Check if user has permission to interact with blocks @@ -1065,7 +1017,6 @@ const WorkflowContent = React.memo(() => { screenToFlowPosition, blocks, addBlock, - addEdge, findClosestOutput, determineSourceHandle, effectivePermissions.canEdit, @@ -1138,15 +1089,14 @@ const WorkflowContent = React.memo(() => { // Only recenter when diff transitions from not ready to ready if (isDiffReady && !prevDiffReadyRef.current && diffAnalysis) { logger.info('Diff ready - recentering canvas to show changes') - // Use a small delay to ensure the diff has fully rendered - setTimeout(() => { + requestAnimationFrame(() => { fitView({ padding: 0.3, duration: 600 }) - }, 100) + }) } prevDiffReadyRef.current = isDiffReady }, [isDiffReady, diffAnalysis, fitView]) - // Listen for trigger warning events + /** Displays trigger warning notifications. */ useEffect(() => { const handleShowTriggerWarning = (event: CustomEvent) => { const { type, triggerName } = event.detail @@ -1170,7 +1120,7 @@ const WorkflowContent = React.memo(() => { } }, [addNotification, activeWorkflowId]) - // Update the onDrop handler to delegate to the shared toolbar-drop handler + /** Handles drop events on the ReactFlow canvas. */ const onDrop = useCallback( (event: React.DragEvent) => { event.preventDefault() @@ -1226,7 +1176,7 @@ const WorkflowContent = React.memo(() => { } }, [emitCursorUpdate]) - // Handle drag over for ReactFlow canvas + /** Handles drag over events for container node highlighting. */ const onDragOver = useCallback( (event: React.DragEvent) => { event.preventDefault() @@ -1283,10 +1233,12 @@ const WorkflowContent = React.memo(() => { [screenToFlowPosition, isPointInLoopNode, getNodes] ) - // Initialize workflow when it exists in registry and isn't active or needs hydration + const loadingWorkflowRef = useRef(null) + const currentWorkflowExists = Boolean(workflows[workflowIdParam]) + + /** Initializes workflow when it exists in registry and needs hydration. */ useEffect(() => { - let cancelled = false - const currentId = params.workflowId as string + const currentId = workflowIdParam const currentWorkspaceHydration = hydration.workspaceId const isRegistryReady = hydration.phase !== 'metadata-loading' && hydration.phase !== 'idle' @@ -1294,13 +1246,23 @@ const WorkflowContent = React.memo(() => { // Wait for registry to be ready to prevent race conditions if ( !currentId || - !workflows[currentId] || + !currentWorkflowExists || !isRegistryReady || (currentWorkspaceHydration && currentWorkspaceHydration !== workspaceId) ) { return } + // Prevent duplicate loads - if we're already loading this workflow, skip + if (loadingWorkflowRef.current === currentId) { + return + } + + // If already loading (state-loading phase), skip + if (hydration.phase === 'state-loading' && hydration.workflowId === currentId) { + return + } + // Check if we encountered an error loading this specific workflow to prevent infinite retries const hasLoadError = hydration.phase === 'error' && hydration.workflowId === currentId @@ -1310,90 +1272,95 @@ const WorkflowContent = React.memo(() => { const needsWorkflowLoad = !hasLoadError && (activeWorkflowId !== currentId || - (activeWorkflowId === currentId && - hydration.phase !== 'ready' && - hydration.phase !== 'state-loading')) + (activeWorkflowId === currentId && hydration.phase !== 'ready')) if (needsWorkflowLoad) { + // Mark this workflow as being loaded to prevent duplicate calls + loadingWorkflowRef.current = currentId + const { clearDiff } = useWorkflowDiffStore.getState() clearDiff() - setActiveWorkflow(currentId).catch((error) => { - if (!cancelled) { - logger.error(`Failed to set active workflow ${currentId}:`, error) - } - }) - } + // Reset canvas ready state when loading a new workflow + setIsCanvasReady(false) - return () => { - cancelled = true + setActiveWorkflow(currentId) + .catch((error) => { + logger.error(`Failed to set active workflow ${currentId}:`, error) + }) + .finally(() => { + // Clear the loading ref when done (success or error) + if (loadingWorkflowRef.current === currentId) { + loadingWorkflowRef.current = null + } + }) } }, [ - params.workflowId, - workflows, + workflowIdParam, + currentWorkflowExists, activeWorkflowId, setActiveWorkflow, hydration.phase, + hydration.workflowId, hydration.workspaceId, workspaceId, ]) - // Preload workspace environment - React Query handles caching automatically useWorkspaceEnvironment(workspaceId) - // Handle navigation and validation + const workflowCount = useMemo(() => Object.keys(workflows).length, [workflows]) + + /** Handles navigation validation and redirects for invalid workflow IDs. */ useEffect(() => { - const validateAndNavigate = async () => { - const workflowIds = Object.keys(workflows) - const currentId = params.workflowId as string - - // Wait for metadata to finish loading before making navigation decisions - if (hydration.phase === 'metadata-loading' || hydration.phase === 'idle') { - return - } - - // If no workflows exist after loading, redirect to workspace root - if (workflowIds.length === 0) { - logger.info('No workflows found, redirecting to workspace root') - router.replace(`/workspace/${workspaceId}/w`) - return - } - - // Navigate to existing workflow or first available - if (!workflows[currentId]) { - logger.info(`Workflow ${currentId} not found, redirecting to first available workflow`) - - // Validate that workflows belong to the current workspace before redirecting - const workspaceWorkflows = workflowIds.filter((id) => { - const workflow = workflows[id] - return workflow.workspaceId === workspaceId - }) - - if (workspaceWorkflows.length > 0) { - router.replace(`/workspace/${workspaceId}/w/${workspaceWorkflows[0]}`) - } else { - // No valid workflows for this workspace, redirect to workspace root - router.replace(`/workspace/${workspaceId}/w`) - } - return - } - - // Validate that the current workflow belongs to the current workspace - const currentWorkflow = workflows[currentId] - if (currentWorkflow && currentWorkflow.workspaceId !== workspaceId) { - logger.warn( - `Workflow ${currentId} belongs to workspace ${currentWorkflow.workspaceId}, not ${workspaceId}` - ) - // Redirect to the correct workspace for this workflow - router.replace(`/workspace/${currentWorkflow.workspaceId}/w/${currentId}`) - return - } + // Wait for metadata to finish loading before making navigation decisions + if (hydration.phase === 'metadata-loading' || hydration.phase === 'idle') { + return } - validateAndNavigate() - }, [params.workflowId, workflows, hydration.phase, workspaceId, router]) + // If no workflows exist after loading, redirect to workspace root + if (workflowCount === 0) { + logger.info('No workflows found, redirecting to workspace root') + router.replace(`/workspace/${workspaceId}/w`) + return + } + + // Navigate to existing workflow or first available + if (!currentWorkflowExists) { + logger.info(`Workflow ${workflowIdParam} not found, redirecting to first available workflow`) + + // Validate that workflows belong to the current workspace before redirecting + const workspaceWorkflows = Object.entries(workflows) + .filter(([, workflow]) => workflow.workspaceId === workspaceId) + .map(([id]) => id) + + if (workspaceWorkflows.length > 0) { + router.replace(`/workspace/${workspaceId}/w/${workspaceWorkflows[0]}`) + } else { + // No valid workflows for this workspace, redirect to workspace root + router.replace(`/workspace/${workspaceId}/w`) + } + return + } + + // Validate that the current workflow belongs to the current workspace + const workflowData = workflows[workflowIdParam] + if (workflowData && workflowData.workspaceId !== workspaceId) { + logger.warn( + `Workflow ${workflowIdParam} belongs to workspace ${workflowData.workspaceId}, not ${workspaceId}` + ) + // Redirect to the correct workspace for this workflow + router.replace(`/workspace/${workflowData.workspaceId}/w/${workflowIdParam}`) + } + }, [ + workflowIdParam, + currentWorkflowExists, + workflowCount, + hydration.phase, + workspaceId, + router, + workflows, + ]) - // Cache block configs to prevent unnecessary re-fetches const blockConfigCache = useRef>(new Map()) const getBlockConfig = useCallback((type: string) => { if (!blockConfigCache.current.has(type)) { @@ -1402,24 +1369,24 @@ const WorkflowContent = React.memo(() => { return blockConfigCache.current.get(type) }, []) - // Track previous blocks hash to prevent unnecessary recalculations const prevBlocksHashRef = useRef('') const prevBlocksRef = useRef(blocks) - // Create a stable hash of block properties that affect node rendering - // This prevents nodes from recreating when only subblock values change - const blocksHash = useMemo(() => { + /** Stable hash of block STRUCTURAL properties - excludes position to prevent node recreation during drag. */ + const blocksStructureHash = useMemo(() => { // Only recalculate hash if blocks reference actually changed if (prevBlocksRef.current === blocks) { return prevBlocksHashRef.current } prevBlocksRef.current = blocks + // Hash only structural properties - NOT position (position changes shouldn't recreate nodes) const hash = Object.values(blocks) .map((b) => { const width = typeof b.data?.width === 'number' ? b.data.width : '' const height = typeof b.data?.height === 'number' ? b.data.height : '' - return `${b.id}:${b.type}:${b.name}:${b.position.x.toFixed(0)}:${b.position.y.toFixed(0)}:${b.height}:${b.data?.parentId || ''}:${width}:${height}` + // Exclude position from hash - drag should not recreate nodes + return `${b.id}:${b.type}:${b.name}:${b.height}:${b.data?.parentId || ''}:${width}:${height}` }) .join('|') @@ -1427,9 +1394,9 @@ const WorkflowContent = React.memo(() => { return hash }, [blocks]) - // Transform blocks and loops into ReactFlow nodes - const nodes = useMemo(() => { - const nodeArray: any[] = [] + /** Transforms blocks into ReactFlow nodes - only recreates on structural changes. */ + const derivedNodes = useMemo(() => { + const nodeArray: Node[] = [] // Add block nodes Object.entries(blocks).forEach(([blockId, block]) => { @@ -1515,41 +1482,42 @@ const WorkflowContent = React.memo(() => { }) return nodeArray - }, [blocksHash, blocks, activeBlockIds, pendingBlocks, isDebugging, getBlockConfig]) + }, [blocksStructureHash, blocks, activeBlockIds, pendingBlocks, isDebugging, getBlockConfig]) - // Update nodes - use store version to avoid collaborative feedback loops - const onNodesChange = useCallback( - (changes: any) => { - changes.forEach((change: any) => { - if (change.type === 'position' && change.position) { - const node = nodes.find((n) => n.id === change.id) - if (!node) return - // Use store version to avoid collaborative feedback loop - // React Flow position changes can be triggered by collaborative updates - storeUpdateBlockPosition(change.id, change.position) - } - }) - }, - [nodes, storeUpdateBlockPosition] - ) + // Local state for nodes - allows smooth drag without store updates on every frame + const [displayNodes, setDisplayNodes] = useState([]) + + // Sync derived nodes to display nodes when structure changes + useEffect(() => { + setDisplayNodes(derivedNodes) + }, [derivedNodes]) + + /** Handles node position changes - updates local state for smooth drag, syncs to store only on drag end. */ + const onNodesChange = useCallback((changes: NodeChange[]) => { + // Apply position changes to local state for smooth rendering + setDisplayNodes((nds) => applyNodeChanges(changes, nds)) + + // Don't sync to store during drag - that's handled in onNodeDragStop + // Only sync non-position changes (like selection) to store if needed + }, []) /** * Effect to resize loops when nodes change (add/remove/position change). - * Runs on every node change for immediate responsiveness. + * Runs on structural changes only - not during drag (position-only changes). + * Skips during loading to avoid unnecessary work. */ useEffect(() => { - // Skip during initial render when nodes aren't loaded yet - if (nodes.length === 0) return + // Skip during initial render when nodes aren't loaded yet or workflow not ready + if (derivedNodes.length === 0 || !isWorkflowReady) return // Resize all loops to fit their children resizeLoopNodesWrapper() + }, [derivedNodes, resizeLoopNodesWrapper, isWorkflowReady]) - // No need for cleanup with direct function - return () => {} - }, [nodes, resizeLoopNodesWrapper]) - - // Special effect to handle cleanup after node deletion + /** Cleans up orphaned nodes with invalid parent references after deletion. */ useEffect(() => { + if (!isWorkflowReady) return + // Create a mapping of node IDs to check for missing parent references const nodeIds = new Set(Object.keys(blocks)) @@ -1572,9 +1540,15 @@ const WorkflowContent = React.memo(() => { updateParentId(id, '', 'parent') } }) - }, [blocks, collaborativeUpdateBlockPosition, updateParentId, getNodeAbsolutePosition]) + }, [ + blocks, + collaborativeUpdateBlockPosition, + updateParentId, + getNodeAbsolutePosition, + isWorkflowReady, + ]) - // Update edges + /** Handles edge removal changes. */ const onEdgesChange = useCallback( (changes: any) => { changes.forEach((change: any) => { @@ -1602,7 +1576,7 @@ const WorkflowContent = React.memo(() => { setIsErrorConnectionDrag(false) }, []) - // Handle connections with improved parent tracking + /** Handles new edge connections with container boundary validation. */ const onConnect = useCallback( (connection: any) => { if (connection.source && connection.target) { @@ -1698,12 +1672,9 @@ const WorkflowContent = React.memo(() => { [addEdge, getNodes] ) - // Handle node drag to detect intersections with container nodes + /** Handles node drag to detect container intersections and update highlighting. */ const onNodeDrag = useCallback( (_event: React.MouseEvent, node: any) => { - // Store currently dragged node ID - setDraggedNodeId(node.id) - // Note: We don't emit position updates during drag to avoid flooding socket events. // The final position is sent in onNodeDragStop for collaborative updates. @@ -1844,7 +1815,7 @@ const WorkflowContent = React.memo(() => { [getNodes, potentialParentId, blocks, getNodeAbsolutePosition, getNodeDepth] ) - // Add in a nodeDrag start event to set the dragStartParentId + /** Captures initial parent ID and position when drag starts. */ const onNodeDragStart = useCallback( (_event: React.MouseEvent, node: any) => { // Store the original parent ID when starting to drag @@ -1861,7 +1832,7 @@ const WorkflowContent = React.memo(() => { [blocks, setDragStartPosition] ) - // Handle node drag stop to establish parent-child relationships + /** Handles node drag stop to establish parent-child relationships. */ const onNodeDragStop = useCallback( (_event: React.MouseEvent, node: any) => { // Clear UI effects @@ -1875,27 +1846,25 @@ const WorkflowContent = React.memo(() => { collaborativeUpdateBlockPosition(node.id, node.position, true) // Record single move entry on drag end to avoid micro-moves - try { - const start = getDragStartPosition() - if (start && start.id === node.id) { - const before = { x: start.x, y: start.y, parentId: start.parentId } - const after = { - x: node.position.x, - y: node.position.y, - parentId: node.parentId || blocks[node.id]?.data?.parentId, - } - const moved = - before.x !== after.x || before.y !== after.y || before.parentId !== after.parentId - if (moved) { - window.dispatchEvent( - new CustomEvent('workflow-record-move', { - detail: { blockId: node.id, before, after }, - }) - ) - } - setDragStartPosition(null) + const start = getDragStartPosition() + if (start && start.id === node.id) { + const before = { x: start.x, y: start.y, parentId: start.parentId } + const after = { + x: node.position.x, + y: node.position.y, + parentId: node.parentId || blocks[node.id]?.data?.parentId, } - } catch {} + const moved = + before.x !== after.x || before.y !== after.y || before.parentId !== after.parentId + if (moved) { + window.dispatchEvent( + new CustomEvent('workflow-record-move', { + detail: { blockId: node.id, before, after }, + }) + ) + } + setDragStartPosition(null) + } // Don't process parent changes if the node hasn't actually changed parent or is being moved within same parent if (potentialParentId === dragStartParentId) return @@ -1907,25 +1876,10 @@ const WorkflowContent = React.memo(() => { blockId: node.id, attemptedParentId: potentialParentId, }) - // Reset state without updating parent - setDraggedNodeId(null) setPotentialParentId(null) return // Exit early - don't allow starter blocks to have parents } - // Subflow nodes cannot be placed inside other subflows - // This check is redundant with onNodeDrag but serves as a safety guard - if (node.type === 'subflowNode' && potentialParentId) { - logger.warn('Prevented subflow node from being placed inside a container', { - blockId: node.id, - attemptedParentId: potentialParentId, - }) - // Reset state without updating parent - setDraggedNodeId(null) - setPotentialParentId(null) - return - } - // Trigger blocks cannot be placed inside loop or parallel subflows if (potentialParentId) { const block = blocks[node.id] @@ -1940,8 +1894,6 @@ const WorkflowContent = React.memo(() => { blockType: block.type, attemptedParentId: potentialParentId, }) - // Reset state without updating parent - setDraggedNodeId(null) setPotentialParentId(null) return } @@ -2048,7 +2000,6 @@ const WorkflowContent = React.memo(() => { } // Reset state - setDraggedNodeId(null) setPotentialParentId(null) }, [ @@ -2070,16 +2021,13 @@ const WorkflowContent = React.memo(() => { ] ) - // Update onPaneClick to only handle edge selection + /** Clears edge selection and panel state when clicking empty canvas. */ const onPaneClick = useCallback(() => { setSelectedEdgeInfo(null) - try { - // Clear current design selection when clicking on empty canvas - usePanelEditorStore.getState().clearCurrentBlock() - } catch {} + usePanelEditorStore.getState().clearCurrentBlock() }, []) - // Edge selection + /** Handles edge selection with container context tracking. */ const onEdgeClick = useCallback( (event: React.MouseEvent, edge: any) => { event.stopPropagation() // Prevent bubbling @@ -2104,67 +2052,41 @@ const WorkflowContent = React.memo(() => { [getNodes] ) - // Transform edges to include improved selection state - const edgesWithSelection = edgesForDisplay.map((edge) => { - // Check if this edge connects nodes inside a loop - const sourceNode = getNodes().find((n) => n.id === edge.source) - const targetNode = getNodes().find((n) => n.id === edge.target) - const parentLoopId = sourceNode?.parentId || targetNode?.parentId - const isInsideLoop = Boolean(parentLoopId) + /** Stable delete handler to avoid creating new function references per edge. */ + const handleEdgeDelete = useCallback( + (edgeId: string) => { + removeEdge(edgeId) + setSelectedEdgeInfo((current) => (current?.id === edgeId ? null : current)) + }, + [removeEdge] + ) - // Create a unique context ID for this edge - const edgeContextId = `${edge.id}${parentLoopId ? `-${parentLoopId}` : ''}` + /** Transforms edges to include selection state and delete handlers. Memoized to prevent re-renders. */ + const edgesWithSelection = useMemo(() => { + // Build node lookup map once - O(n) instead of O(n) per edge + const nodeMap = new Map(displayNodes.map((n) => [n.id, n])) - // Determine if this edge is selected using context-aware matching - const isSelected = selectedEdgeInfo?.contextId === edgeContextId + return edgesForDisplay.map((edge) => { + const sourceNode = nodeMap.get(edge.source) + const targetNode = nodeMap.get(edge.target) + const parentLoopId = sourceNode?.parentId || targetNode?.parentId + const edgeContextId = `${edge.id}${parentLoopId ? `-${parentLoopId}` : ''}` - return { - ...edge, - data: { - // Preserve original edge data - ...edge.data, - // Send only necessary data to the edge component - isSelected, - isInsideLoop, - parentLoopId, - sourceHandle: edge.sourceHandle, - onDelete: (edgeId: string) => { - // Log deletion for debugging - - // Only delete this specific edge - removeEdge(edgeId) - - // Only clear selection if this was the selected edge - if (selectedEdgeInfo?.id === edgeId) { - setSelectedEdgeInfo(null) - } + return { + ...edge, + data: { + ...edge.data, + isSelected: selectedEdgeInfo?.contextId === edgeContextId, + isInsideLoop: Boolean(parentLoopId), + parentLoopId, + sourceHandle: edge.sourceHandle, + onDelete: handleEdgeDelete, }, - }, - } - }) - - // Handle keyboard shortcuts with better edge tracking - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if ((event.key === 'Delete' || event.key === 'Backspace') && selectedEdgeInfo) { - // Only delete the specific selected edge - removeEdge(selectedEdgeInfo.id) - setSelectedEdgeInfo(null) } - } + }) + }, [edgesForDisplay, displayNodes, selectedEdgeInfo?.contextId, handleEdgeDelete]) - window.addEventListener('keydown', handleKeyDown) - return () => window.removeEventListener('keydown', handleKeyDown) - }, [selectedEdgeInfo, removeEdge]) - - /** - * Handle Delete / Backspace for removing selected blocks. - * - * This mirrors the behavior of clicking the ActionBar delete button by - * invoking the collaborative remove-block helper. The handler is disabled - * while focus is inside editable elements so it does not interfere with - * text editing. - */ + /** Handles Delete/Backspace to remove selected edges or blocks. */ useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key !== 'Delete' && event.key !== 'Backspace') { @@ -2182,6 +2104,14 @@ const WorkflowContent = React.memo(() => { return } + // Handle edge deletion first (edges take priority if selected) + if (selectedEdgeInfo) { + removeEdge(selectedEdgeInfo.id) + setSelectedEdgeInfo(null) + return + } + + // Handle block deletion if (!effectivePermissions.canEdit) { return } @@ -2191,23 +2121,16 @@ const WorkflowContent = React.memo(() => { return } - // Prevent default browser behavior (e.g., page navigation) when we act event.preventDefault() - - try { - // For now, mirror edge behavior and delete the primary selected block - const primaryNode = selectedNodes[0] - removeBlock(primaryNode.id) - } catch (err) { - logger.error('Failed to delete block via keyboard', { err }) - } + const primaryNode = selectedNodes[0] + removeBlock(primaryNode.id) } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) - }, [getNodes, removeBlock, effectivePermissions.canEdit]) + }, [selectedEdgeInfo, removeEdge, getNodes, removeBlock, effectivePermissions.canEdit]) - // Handle sub-block value updates from custom events + /** Handles sub-block value updates from custom events. */ useEffect(() => { const handleSubBlockValueUpdate = (event: CustomEvent) => { const { blockId, subBlockId, value } = event.detail @@ -2228,101 +2151,89 @@ const WorkflowContent = React.memo(() => { } }, [collaborativeSetSubblockValue]) - // Show skeleton UI while loading until the workflow store is hydrated - const showSkeletonUI = !isWorkflowReady - - if (showSkeletonUI) { - return ( -
-
-
-
- -
-
-
- - -
- ) - } - return ( -
-
- {/* Training Modal - for recording workflow edits */} - {showTrainingModal && } +
+
+ {/* Loading spinner - always mounted, animation paused when hidden to avoid overhead */} +
+
+
- { - requestAnimationFrame(() => { - requestAnimationFrame(() => { - instance.fitView(reactFlowFitViewOptions) - }) - }) - }} - minZoom={0.1} - maxZoom={1.3} - panOnScroll - fitViewOptions={reactFlowFitViewOptions} // Not seen due to onInit - defaultEdgeOptions={defaultEdgeOptions} - proOptions={reactFlowProOptions} - connectionLineStyle={connectionLineStyle} - connectionLineType={ConnectionLineType.SmoothStep} - onNodeClick={(e, _node) => { - e.stopPropagation() - }} - onPaneClick={onPaneClick} - onEdgeClick={onEdgeClick} - onPointerMove={handleCanvasPointerMove} - onPointerLeave={handleCanvasPointerLeave} - elementsSelectable={true} - selectNodesOnDrag={false} - nodesConnectable={effectivePermissions.canEdit} - nodesDraggable={effectivePermissions.canEdit} - draggable={false} - noWheelClassName='allow-scroll' - edgesFocusable={true} - edgesUpdatable={effectivePermissions.canEdit} - className='workflow-container h-full' - onNodeDrag={effectivePermissions.canEdit ? onNodeDrag : undefined} - onNodeDragStop={effectivePermissions.canEdit ? onNodeDragStop : undefined} - onNodeDragStart={effectivePermissions.canEdit ? onNodeDragStart : undefined} - snapToGrid={false} - snapGrid={snapGrid} - elevateEdgesOnSelect={true} - // Performance optimizations - onlyRenderVisibleElements={true} - deleteKeyCode={null} - elevateNodesOnSelect={true} - autoPanOnConnect={effectivePermissions.canEdit} - autoPanOnNodeDrag={effectivePermissions.canEdit} - /> + {isWorkflowReady && ( + <> + {showTrainingModal && } - + { + requestAnimationFrame(() => { + instance.fitView(reactFlowFitViewOptions) + setIsCanvasReady(true) + }) + }} + fitViewOptions={reactFlowFitViewOptions} + minZoom={0.1} + maxZoom={1.3} + panOnScroll + defaultEdgeOptions={defaultEdgeOptions} + proOptions={reactFlowProOptions} + connectionLineStyle={connectionLineStyle} + connectionLineType={ConnectionLineType.SmoothStep} + onNodeClick={(e, _node) => { + e.stopPropagation() + }} + onPaneClick={onPaneClick} + onEdgeClick={onEdgeClick} + onPointerMove={handleCanvasPointerMove} + onPointerLeave={handleCanvasPointerLeave} + elementsSelectable={true} + selectNodesOnDrag={false} + nodesConnectable={effectivePermissions.canEdit} + nodesDraggable={effectivePermissions.canEdit} + draggable={false} + noWheelClassName='allow-scroll' + edgesFocusable={true} + edgesUpdatable={effectivePermissions.canEdit} + className={`workflow-container h-full bg-[var(--bg)] transition-opacity duration-150 ${isCanvasReady ? 'opacity-100' : 'opacity-0'}`} + onNodeDrag={effectivePermissions.canEdit ? onNodeDrag : undefined} + onNodeDragStop={effectivePermissions.canEdit ? onNodeDragStop : undefined} + onNodeDragStart={effectivePermissions.canEdit ? onNodeDragStart : undefined} + snapToGrid={false} + snapGrid={snapGrid} + elevateEdgesOnSelect={true} + onlyRenderVisibleElements={false} + deleteKeyCode={null} + elevateNodesOnSelect={true} + autoPanOnConnect={effectivePermissions.canEdit} + autoPanOnNodeDrag={effectivePermissions.canEdit} + /> - {/* Floating chat modal */} - + - {/* Show DiffControls if diff is available (regardless of current view mode) */} - + + + + + + + )} - {/* Notifications display */} - {/* Trigger list for empty workflows - only show after workflow has loaded and hydrated */} {isWorkflowReady && isWorkflowEmpty && effectivePermissions.canEdit && } @@ -2331,15 +2242,17 @@ const WorkflowContent = React.memo(() => { {oauthModal && ( - setOauthModal(null)} - provider={oauthModal.provider} - toolName={oauthModal.providerName} - serviceId={oauthModal.serviceId} - requiredScopes={oauthModal.requiredScopes} - newScopes={oauthModal.newScopes} - /> + + setOauthModal(null)} + provider={oauthModal.provider} + toolName={oauthModal.providerName} + serviceId={oauthModal.serviceId} + requiredScopes={oauthModal.requiredScopes} + newScopes={oauthModal.newScopes} + /> + )}
) @@ -2347,7 +2260,7 @@ const WorkflowContent = React.memo(() => { WorkflowContent.displayName = 'WorkflowContent' -// Workflow wrapper +/** Workflow page with ReactFlowProvider and error boundary wrapper. */ const Workflow = React.memo(() => { return ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx index 195acbb77..75982d706 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx @@ -38,11 +38,10 @@ export function TeamManagement() { const { data: organizationsData } = useOrganizations() const activeOrganization = organizationsData?.activeOrganization - const billingData = organizationsData?.billingData?.data - const hasTeamPlan = billingData?.isTeam ?? false - const hasEnterprisePlan = billingData?.isEnterprise ?? false const { data: userSubscriptionData } = useSubscriptionData() + const hasTeamPlan = userSubscriptionData?.data?.isTeam ?? false + const hasEnterprisePlan = userSubscriptionData?.data?.isEnterprise ?? false const { data: organization, diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx index d1debd8d2..f0634c7f4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx @@ -316,16 +316,14 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) { queryKey: organizationKeys.lists(), queryFn: async () => { const { client } = await import('@/lib/auth/auth-client') - const [orgsResponse, activeOrgResponse, billingResponse] = await Promise.all([ + const [orgsResponse, activeOrgResponse] = await Promise.all([ client.organization.list(), client.organization.getFullOrganization(), - fetch('/api/billing?context=user').then((r) => r.json()), ]) return { organizations: orgsResponse.data || [], activeOrganization: activeOrgResponse.data, - billingData: billingResponse, } }, staleTime: 30 * 1000, diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx index bafcb8b21..95a0cd17e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx @@ -144,6 +144,12 @@ export function WorkspaceHeader({ const contextMenuRef = useRef(null) const capturedWorkspaceRef = useRef<{ id: string; name: string } | null>(null) + // Client-only rendering for Popover to prevent Radix ID hydration mismatch + const [isMounted, setIsMounted] = useState(false) + useEffect(() => { + setIsMounted(true) + }, []) + /** * Focus the inline list rename input when it becomes active */ @@ -269,104 +275,121 @@ export function WorkspaceHeader({ setIsInviteModalOpen(true)}> Invite - {/* Workspace Switcher Popover */} - { - // Don't close if context menu is opening - if (!open && isContextMenuOpen) { - return - } - setIsWorkspaceMenuOpen(open) - }} - > - - - - e.preventDefault()} + {/* Workspace Switcher Popover - only render after mount to avoid Radix ID hydration mismatch */} + {isMounted ? ( + { + // Don't close if context menu is opening + if (!open && isContextMenuOpen) { + return + } + setIsWorkspaceMenuOpen(open) + }} > - {isWorkspacesLoading ? ( - - Loading workspaces... - - ) : ( - <> -
- Workspaces -
- - - - - -

- {isImportingWorkspace ? 'Importing workspace...' : 'Import workspace'} -

-
-
- - - - - -

{isCreatingWorkspace ? 'Creating workspace...' : 'Create workspace'}

-
-
+ + + + e.preventDefault()} + > + {isWorkspacesLoading ? ( + + Loading workspaces... + + ) : ( + <> +
+ Workspaces +
+ + + + + +

+ {isImportingWorkspace ? 'Importing workspace...' : 'Import workspace'} +

+
+
+ + + + + +

+ {isCreatingWorkspace ? 'Creating workspace...' : 'Create workspace'} +

+
+
+
-
-
- {workspaces.map((workspace, index) => ( -
0 ? 'mt-[2px]' : ''}> - {editingWorkspaceId === workspace.id ? ( -
- setEditingName(e.target.value)} - onKeyDown={async (e) => { - if (e.key === 'Enter') { - e.preventDefault() +
+ {workspaces.map((workspace, index) => ( +
0 ? 'mt-[2px]' : ''}> + {editingWorkspaceId === workspace.id ? ( +
+ setEditingName(e.target.value)} + onKeyDown={async (e) => { + if (e.key === 'Enter') { + e.preventDefault() + setIsListRenaming(true) + try { + await onRenameWorkspace(workspace.id, editingName.trim()) + setEditingWorkspaceId(null) + } finally { + setIsListRenaming(false) + } + } else if (e.key === 'Escape') { + e.preventDefault() + setEditingWorkspaceId(null) + } + }} + onBlur={async () => { + if (!editingWorkspaceId) return setIsListRenaming(true) try { await onRenameWorkspace(workspace.id, editingName.trim()) @@ -374,50 +397,47 @@ export function WorkspaceHeader({ } finally { setIsListRenaming(false) } - } else if (e.key === 'Escape') { + }} + className='w-full border-0 bg-transparent p-0 font-base text-[12px] text-[var(--text-primary)] outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0' + maxLength={100} + autoComplete='off' + autoCorrect='off' + autoCapitalize='off' + spellCheck='false' + disabled={isListRenaming} + onClick={(e) => { e.preventDefault() - setEditingWorkspaceId(null) - } - }} - onBlur={async () => { - if (!editingWorkspaceId) return - setIsListRenaming(true) - try { - await onRenameWorkspace(workspace.id, editingName.trim()) - setEditingWorkspaceId(null) - } finally { - setIsListRenaming(false) - } - }} - className='w-full border-0 bg-transparent p-0 font-base text-[12px] text-[var(--text-primary)] outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0' - maxLength={100} - autoComplete='off' - autoCorrect='off' - autoCapitalize='off' - spellCheck='false' - disabled={isListRenaming} - onClick={(e) => { - e.preventDefault() - e.stopPropagation() - }} - /> -
- ) : ( - onWorkspaceSwitch(workspace)} - onContextMenu={(e) => handleContextMenu(e, workspace)} - > - {workspace.name} - - )} -
- ))} -
- - )} - - + e.stopPropagation() + }} + /> +
+ ) : ( + onWorkspaceSwitch(workspace)} + onContextMenu={(e) => handleContextMenu(e, workspace)} + > + {workspace.name} + + )} +
+ ))} +
+ + )} + + + ) : ( + + )} {/* Sidebar Collapse Toggle */} {showCollapseButton && (
) } diff --git a/apps/sim/app/workspace/page.tsx b/apps/sim/app/workspace/page.tsx index e37919599..9f94a865d 100644 --- a/apps/sim/app/workspace/page.tsx +++ b/apps/sim/app/workspace/page.tsx @@ -1,7 +1,6 @@ 'use client' import { useEffect } from 'react' -import { Loader2 } from 'lucide-react' import { useRouter } from 'next/navigation' import { useSession } from '@/lib/auth/auth-client' import { createLogger } from '@/lib/logs/console/logger' @@ -118,9 +117,7 @@ export default function WorkspacePage() { if (isPending) { return (
-
- -
+
) } diff --git a/apps/sim/app/workspace/providers/socket-provider.tsx b/apps/sim/app/workspace/providers/socket-provider.tsx index df89bee5c..10d9fffe3 100644 --- a/apps/sim/app/workspace/providers/socket-provider.tsx +++ b/apps/sim/app/workspace/providers/socket-provider.tsx @@ -160,17 +160,13 @@ export function SocketProvider({ children, user }: SocketProviderProps) { initializedRef.current = true setIsConnecting(true) - const initializeSocket = async () => { + const initializeSocket = () => { try { - // Generate initial token for socket authentication - const token = await generateSocketToken() - const socketUrl = getEnv('NEXT_PUBLIC_SOCKET_URL') || 'http://localhost:3002' logger.info('Attempting to connect to Socket.IO server', { url: socketUrl, userId: user?.id || 'no-user', - hasToken: !!token, timestamp: new Date().toISOString(), }) diff --git a/apps/sim/components/emcn/components/s-modal/s-modal.tsx b/apps/sim/components/emcn/components/s-modal/s-modal.tsx index a96e98fd4..a1560ab84 100644 --- a/apps/sim/components/emcn/components/s-modal/s-modal.tsx +++ b/apps/sim/components/emcn/components/s-modal/s-modal.tsx @@ -102,11 +102,15 @@ const SModalContent = React.forwardRef< SModalContent.displayName = 'SModalContent' /** - * Sidebar container. + * Sidebar container with scrollable content. */ const SModalSidebar = React.forwardRef>( ({ className, ...props }, ref) => ( -
+
) ) diff --git a/apps/sim/hooks/queries/organization.ts b/apps/sim/hooks/queries/organization.ts index 8ccdabc38..ebb8ba06e 100644 --- a/apps/sim/hooks/queries/organization.ts +++ b/apps/sim/hooks/queries/organization.ts @@ -21,18 +21,17 @@ export const organizationKeys = { /** * Fetch all organizations for the current user + * Note: Billing data is fetched separately via useSubscriptionData() to avoid duplicate calls */ async function fetchOrganizations() { - const [orgsResponse, activeOrgResponse, billingResponse] = await Promise.all([ + const [orgsResponse, activeOrgResponse] = await Promise.all([ client.organization.list(), client.organization.getFullOrganization(), - fetch('/api/billing?context=user').then((r) => r.json()), ]) return { organizations: orgsResponse.data || [], activeOrganization: activeOrgResponse.data, - billingData: billingResponse, } } diff --git a/apps/sim/stores/workflows/workflow/store.ts b/apps/sim/stores/workflows/workflow/store.ts index 02195ebf4..8b3bd73df 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -251,10 +251,7 @@ export const useWorkflowStore = create()( position, }, }, - edges: [...state.edges], })) - get().updateLastSaved() - // No sync for position updates to avoid excessive syncing during drag }, updateNodeDimensions: (id: string, dimensions: { width: number; height: number }) => {