mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
feat(blogs): added blogs scaffolding (#1857)
This commit is contained in:
166
apps/sim/app/(landing)/blog/[slug]/page.tsx
Normal file
166
apps/sim/app/(landing)/blog/[slug]/page.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import type { Metadata } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
import { FAQ } from '@/lib/blog/faq'
|
||||
import { getAllPostMeta, getPostBySlug, getRelatedPosts } from '@/lib/blog/registry'
|
||||
import { buildArticleJsonLd, buildBreadcrumbJsonLd, buildPostMetadata } from '@/lib/blog/seo'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const posts = await getAllPostMeta()
|
||||
return posts.map((p) => ({ slug: p.slug }))
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params
|
||||
const post = await getPostBySlug(slug)
|
||||
return buildPostMetadata(post)
|
||||
}
|
||||
|
||||
export const revalidate = 86400
|
||||
|
||||
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params
|
||||
const post = await getPostBySlug(slug)
|
||||
const Article = post.Content
|
||||
const jsonLd = buildArticleJsonLd(post)
|
||||
const breadcrumbLd = buildBreadcrumbJsonLd(post)
|
||||
const related = await getRelatedPosts(slug, 3)
|
||||
|
||||
return (
|
||||
<article
|
||||
className={`${soehne.className} w-full`}
|
||||
itemScope
|
||||
itemType='https://schema.org/BlogPosting'
|
||||
>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbLd) }}
|
||||
/>
|
||||
<header className='mx-auto max-w-[1450px] px-6 pt-8 sm:px-8 sm:pt-12 md:px-12 md:pt-16'>
|
||||
<div className='mb-6'>
|
||||
<Link href='/blog' className='text-gray-600 text-sm hover:text-gray-900'>
|
||||
← Back to Blog
|
||||
</Link>
|
||||
</div>
|
||||
<div className='flex flex-col gap-8 md:flex-row md:gap-12'>
|
||||
<div className='h-[180px] w-full flex-shrink-0 sm:h-[200px] md:h-auto md:w-[300px]'>
|
||||
<div className='relative h-full w-full overflow-hidden rounded-lg md:aspect-[5/4]'>
|
||||
<Image
|
||||
src={post.ogImage}
|
||||
alt={post.title}
|
||||
width={300}
|
||||
height={240}
|
||||
className='h-full w-full object-cover'
|
||||
priority
|
||||
itemProp='image'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-1 flex-col justify-between'>
|
||||
<h1
|
||||
className='font-medium text-[36px] leading-tight tracking-tight sm:text-[48px] md:text-[56px] lg:text-[64px]'
|
||||
itemProp='headline'
|
||||
>
|
||||
{post.title}
|
||||
</h1>
|
||||
<div className='mt-4 flex items-center gap-3'>
|
||||
{(post.authors || [post.author]).map((a, idx) => (
|
||||
<div key={idx} className='flex items-center gap-2'>
|
||||
{a?.avatarUrl ? (
|
||||
<Avatar className='size-6'>
|
||||
<AvatarImage src={a.avatarUrl} alt={a.name} />
|
||||
<AvatarFallback>{a.name.slice(0, 2)}</AvatarFallback>
|
||||
</Avatar>
|
||||
) : null}
|
||||
<Link
|
||||
href={a?.url || '#'}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer author'
|
||||
className='text-[14px] text-gray-600 leading-[1.5] hover:text-gray-900 sm:text-[16px]'
|
||||
itemProp='author'
|
||||
itemScope
|
||||
itemType='https://schema.org/Person'
|
||||
>
|
||||
<span itemProp='name'>{a?.name}</span>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr className='mt-8 border-gray-200 border-t sm:mt-12' />
|
||||
<div className='flex flex-col gap-6 py-8 sm:flex-row sm:items-start sm:justify-between sm:gap-8 sm:py-10'>
|
||||
<div className='flex flex-shrink-0 items-center gap-4'>
|
||||
<time
|
||||
className='block text-[14px] text-gray-600 leading-[1.5] sm:text-[16px]'
|
||||
dateTime={post.date}
|
||||
itemProp='datePublished'
|
||||
>
|
||||
{new Date(post.date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</time>
|
||||
<meta itemProp='dateModified' content={post.updated ?? post.date} />
|
||||
</div>
|
||||
<div className='flex-1'>
|
||||
<p className='m-0 block translate-y-[-4px] font-[400] text-[18px] leading-[1.5] sm:text-[20px] md:text-[26px]'>
|
||||
{post.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className='mx-auto max-w-[900px] px-6 pb-20 sm:px-8 md:px-12' itemProp='articleBody'>
|
||||
<div className='prose prose-lg max-w-none'>
|
||||
<Article />
|
||||
{post.faq && post.faq.length > 0 ? <FAQ items={post.faq} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
{related.length > 0 && (
|
||||
<div className='mx-auto max-w-[900px] px-6 pb-24 sm:px-8 md:px-12'>
|
||||
<h2 className='mb-4 font-medium text-[24px]'>Related posts</h2>
|
||||
<div className='grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3'>
|
||||
{related.map((p) => (
|
||||
<Link key={p.slug} href={`/blog/${p.slug}`} className='group'>
|
||||
<div className='overflow-hidden rounded-lg border border-gray-200'>
|
||||
<Image
|
||||
src={p.ogImage}
|
||||
alt={p.title}
|
||||
width={600}
|
||||
height={315}
|
||||
className='h-[160px] w-full object-cover'
|
||||
/>
|
||||
<div className='p-3'>
|
||||
<div className='mb-1 text-gray-600 text-xs'>
|
||||
{new Date(p.date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<div className='font-medium text-sm leading-tight'>{p.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<meta itemProp='publisher' content='Sim' />
|
||||
<meta itemProp='inLanguage' content='en-US' />
|
||||
<meta itemProp='keywords' content={post.tags.join(', ')} />
|
||||
</article>
|
||||
)
|
||||
}
|
||||
72
apps/sim/app/(landing)/blog/authors/[id]/page.tsx
Normal file
72
apps/sim/app/(landing)/blog/authors/[id]/page.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { getAllPostMeta } from '@/lib/blog/registry'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
|
||||
export const revalidate = 3600
|
||||
|
||||
export default async function AuthorPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params
|
||||
const posts = (await getAllPostMeta()).filter((p) => p.author.id === id)
|
||||
const author = posts[0]?.author
|
||||
if (!author) {
|
||||
return (
|
||||
<main className={`${soehne.className} mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12`}>
|
||||
<h1 className='font-medium text-[32px]'>Author not found</h1>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
const personJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Person',
|
||||
name: author.name,
|
||||
url: `https://sim.ai/blog/authors/${author.id}`,
|
||||
sameAs: author.url ? [author.url] : [],
|
||||
image: author.avatarUrl,
|
||||
}
|
||||
return (
|
||||
<main className={`${soehne.className} mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12`}>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(personJsonLd) }}
|
||||
/>
|
||||
<div className='mb-6 flex items-center gap-3'>
|
||||
{author.avatarUrl ? (
|
||||
<Image
|
||||
src={author.avatarUrl}
|
||||
alt={author.name}
|
||||
width={40}
|
||||
height={40}
|
||||
className='rounded-full'
|
||||
/>
|
||||
) : null}
|
||||
<h1 className='font-medium text-[32px] leading-tight'>{author.name}</h1>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 gap-8 sm:grid-cols-2'>
|
||||
{posts.map((p) => (
|
||||
<Link key={p.slug} href={`/blog/${p.slug}`} className='group'>
|
||||
<div className='overflow-hidden rounded-lg border border-gray-200'>
|
||||
<Image
|
||||
src={p.ogImage}
|
||||
alt={p.title}
|
||||
width={600}
|
||||
height={315}
|
||||
className='h-[160px] w-full object-cover transition-transform group-hover:scale-[1.02]'
|
||||
/>
|
||||
<div className='p-3'>
|
||||
<div className='mb-1 text-gray-600 text-xs'>
|
||||
{new Date(p.date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<div className='font-medium text-sm leading-tight'>{p.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
13
apps/sim/app/(landing)/blog/head.tsx
Normal file
13
apps/sim/app/(landing)/blog/head.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
export default function Head() {
|
||||
return (
|
||||
<>
|
||||
<link rel='canonical' href='https://sim.ai/blog' />
|
||||
<link
|
||||
rel='alternate'
|
||||
type='application/rss+xml'
|
||||
title='Sim Blog'
|
||||
href='https://sim.ai/blog/rss.xml'
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,40 @@
|
||||
import { Footer, Nav } from '@/app/(landing)/components'
|
||||
|
||||
export default function BlogLayout({ children }: { children: React.ReactNode }) {
|
||||
const orgJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
name: 'Sim',
|
||||
url: 'https://sim.ai',
|
||||
logo: 'https://sim.ai/logo/primary/small.png',
|
||||
sameAs: ['https://x.com/simdotai'],
|
||||
}
|
||||
|
||||
const websiteJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
name: 'Sim',
|
||||
url: 'https://sim.ai',
|
||||
potentialAction: {
|
||||
'@type': 'SearchAction',
|
||||
target: 'https://sim.ai/search?q={search_term_string}',
|
||||
'query-input': 'required name=search_term_string',
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}
|
||||
/>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteJsonLd) }}
|
||||
/>
|
||||
<Nav hideAuthButtons={false} variant='landing' />
|
||||
<main className='relative'>{children}</main>
|
||||
<main className='relative flex-1'>{children}</main>
|
||||
<Footer fullWidth={true} />
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,795 +0,0 @@
|
||||
import Image from 'next/image'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
|
||||
export default function OpenAiN8nSim() {
|
||||
const baseUrl = 'https://sim.ai'
|
||||
const articleUrl = `${baseUrl}/blog/openai-vs-n8n-vs-sim`
|
||||
|
||||
const articleStructuredData = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'TechArticle',
|
||||
headline: 'OpenAI AgentKit vs n8n vs Sim: AI Agent Workflow Builder Comparison',
|
||||
description:
|
||||
'Compare OpenAI AgentKit, n8n, and Sim for building AI agent workflows. Explore key differences in capabilities, integrations, collaboration, and which platform best fits your production AI agent needs.',
|
||||
image: `${baseUrl}/blog/openai-vs-n8n-vs-sim/workflow.png`,
|
||||
datePublished: '2025-10-06T00:00:00.000Z',
|
||||
dateModified: '2025-10-06T00:00:00.000Z',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Emir Karabeg',
|
||||
url: 'https://x.com/karabegemir',
|
||||
sameAs: ['https://x.com/karabegemir'],
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'Sim',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: `${baseUrl}/logo/sim-logo.png`,
|
||||
},
|
||||
url: baseUrl,
|
||||
},
|
||||
mainEntityOfPage: {
|
||||
'@type': 'WebPage',
|
||||
'@id': articleUrl,
|
||||
},
|
||||
keywords:
|
||||
'AI agents, OpenAI AgentKit, n8n, Sim, workflow automation, AI agent development, RAG, MCP protocol, agentic workflows, ChatKit, AI Copilot',
|
||||
articleSection: 'Technology',
|
||||
inLanguage: 'en-US',
|
||||
about: [
|
||||
{
|
||||
'@type': 'Thing',
|
||||
name: 'Artificial Intelligence',
|
||||
},
|
||||
{
|
||||
'@type': 'Thing',
|
||||
name: 'Workflow Automation',
|
||||
},
|
||||
{
|
||||
'@type': 'SoftwareApplication',
|
||||
name: 'OpenAI AgentKit',
|
||||
},
|
||||
{
|
||||
'@type': 'SoftwareApplication',
|
||||
name: 'n8n',
|
||||
},
|
||||
{
|
||||
'@type': 'SoftwareApplication',
|
||||
name: 'Sim',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const breadcrumbStructuredData = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 1,
|
||||
name: 'Home',
|
||||
item: baseUrl,
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 2,
|
||||
name: 'Blog',
|
||||
item: `${baseUrl}/blog`,
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 3,
|
||||
name: 'OpenAI AgentKit vs n8n vs Sim',
|
||||
item: articleUrl,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Structured Data for SEO */}
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify(articleStructuredData),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify(breadcrumbStructuredData),
|
||||
}}
|
||||
/>
|
||||
|
||||
<article
|
||||
className={`${soehne.className} w-full`}
|
||||
itemScope
|
||||
itemType='https://schema.org/TechArticle'
|
||||
>
|
||||
{/* Header Section with Image and Title */}
|
||||
<header className='mx-auto max-w-[1450px] px-6 pt-8 sm:px-8 sm:pt-12 md:px-12 md:pt-16'>
|
||||
<div className='flex flex-col gap-8 md:flex-row md:gap-12'>
|
||||
{/* Large Image on Left */}
|
||||
<div className='h-[180px] w-full flex-shrink-0 sm:h-[200px] md:h-auto md:w-[300px]'>
|
||||
<div className='relative h-full w-full overflow-hidden rounded-lg md:aspect-[5/4]'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/workflow.png'
|
||||
alt='Sim AI agent workflow builder interface'
|
||||
width={300}
|
||||
height={240}
|
||||
className='h-full w-full object-cover'
|
||||
priority
|
||||
itemProp='image'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Title - Taking up 80% */}
|
||||
<div className='flex flex-1 flex-col justify-between'>
|
||||
<h1
|
||||
className='font-medium text-[36px] leading-tight tracking-tight sm:text-[48px] md:text-[56px] lg:text-[64px]'
|
||||
itemProp='headline'
|
||||
>
|
||||
OpenAI AgentKit vs n8n vs Sim: AI Agent Workflow Builder Comparison
|
||||
</h1>
|
||||
<div className='mt-4 hidden items-center justify-end gap-2 sm:flex'>
|
||||
<a
|
||||
href='https://x.com/karabegemir'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer author'
|
||||
aria-label='@karabegemir on X'
|
||||
className='block'
|
||||
>
|
||||
<Avatar className='size-6'>
|
||||
<AvatarImage
|
||||
src='/blog/openai-vs-n8n-vs-sim/emir-karabeg.png'
|
||||
alt='Emir Karabeg'
|
||||
/>
|
||||
<AvatarFallback>EK</AvatarFallback>
|
||||
</Avatar>
|
||||
</a>
|
||||
<a
|
||||
href='https://x.com/karabegemir'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer author'
|
||||
className='text-[14px] text-gray-600 leading-[1.5] hover:text-gray-900 sm:text-[16px]'
|
||||
itemProp='author'
|
||||
itemScope
|
||||
itemType='https://schema.org/Person'
|
||||
>
|
||||
<span itemProp='name'>Emir Karabeg</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Horizontal Line Separator */}
|
||||
<hr className='mt-8 border-gray-200 border-t sm:mt-12' />
|
||||
|
||||
{/* Publish Date and Subtitle */}
|
||||
<div className='flex flex-col gap-6 py-8 sm:flex-row sm:items-start sm:justify-between sm:gap-8 sm:py-10'>
|
||||
{/* Publish Date and Author */}
|
||||
<div className='flex flex-shrink-0 items-center justify-between gap-4 sm:gap-0'>
|
||||
<time
|
||||
className='block text-[14px] text-gray-600 leading-[1.5] sm:text-[16px]'
|
||||
dateTime='2025-10-06T00:00:00.000Z'
|
||||
itemProp='datePublished'
|
||||
>
|
||||
Published Oct 6, 2025
|
||||
</time>
|
||||
<meta itemProp='dateModified' content='2025-10-06T00:00:00.000Z' />
|
||||
<div className='flex items-center gap-2 sm:hidden'>
|
||||
<a
|
||||
href='https://x.com/karabegemir'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer author'
|
||||
aria-label='@karabegemir on X'
|
||||
className='block'
|
||||
>
|
||||
<Avatar className='size-6'>
|
||||
<AvatarImage
|
||||
src='/blog/openai-vs-n8n-vs-sim/emir-karabeg.png'
|
||||
alt='Emir Karabeg'
|
||||
/>
|
||||
<AvatarFallback>EK</AvatarFallback>
|
||||
</Avatar>
|
||||
</a>
|
||||
<a
|
||||
href='https://x.com/karabegemir'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer author'
|
||||
className='text-[14px] text-gray-600 leading-[1.5] hover:text-gray-900'
|
||||
>
|
||||
Emir Karabeg
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Subtitle on Right */}
|
||||
<div className='flex-1'>
|
||||
<p
|
||||
className='m-0 block translate-y-[-4px] font-[400] text-[18px] leading-[1.5] sm:text-[20px] md:text-[26px]'
|
||||
itemProp='description'
|
||||
>
|
||||
OpenAI just released AgentKit for building AI agents. How does it compare to
|
||||
workflow automation platforms like n8n and purpose-built AI agent builders like Sim?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content Area - Medium-style centered with padding */}
|
||||
<div className='mx-auto max-w-[800px] px-6 pb-20 sm:px-8 md:px-12' itemProp='articleBody'>
|
||||
<div className='prose prose-lg max-w-none'>
|
||||
{/* Introduction */}
|
||||
<section className='mb-12'>
|
||||
<p className='text-[20px] text-gray-800 leading-relaxed'>
|
||||
When building AI agent workflows, developers often evaluate multiple platforms to
|
||||
find the right fit for their needs. Three platforms frequently come up in these
|
||||
discussions: OpenAI's new AgentKit, the established workflow automation tool n8n,
|
||||
and Sim, a purpose-built AI agent workflow builder. While all three enable AI agent
|
||||
development, they take fundamentally different approaches, each with distinct
|
||||
strengths and ideal use cases.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Section 1: OpenAI AgentKit */}
|
||||
<section className='mb-12'>
|
||||
<h2 className='mb-4 font-medium text-[28px] leading-tight sm:text-[32px]'>
|
||||
What is OpenAI AgentKit?
|
||||
</h2>
|
||||
<p className='mb-6 text-[19px] text-gray-800 leading-relaxed'>
|
||||
OpenAI AgentKit is a set of building blocks designed to help developers take AI
|
||||
agents from prototype to production. Built on top of the OpenAI Responses API, it
|
||||
provides a structured approach to building and deploying intelligent agents.
|
||||
</p>
|
||||
|
||||
<figure className='my-8 overflow-hidden rounded-lg'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/openai.png'
|
||||
alt='OpenAI AgentKit workflow interface'
|
||||
width={800}
|
||||
height={450}
|
||||
className='w-full'
|
||||
/>
|
||||
<figcaption className='sr-only'>
|
||||
OpenAI AgentKit visual workflow builder interface
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>Core Features</h3>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Agent Builder Canvas
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
AgentKit provides a visual canvas where developers can design and build agents. This
|
||||
interface allows you to model complex workflows visually, making it easier to
|
||||
understand and iterate on agent behavior. The builder is powered by OpenAI's
|
||||
Responses API.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
ChatKit for Embedded Interfaces
|
||||
</h4>
|
||||
<p className='mb-6 text-[19px] text-gray-800 leading-relaxed'>
|
||||
ChatKit enables developers to embed chat interfaces to run workflows directly within
|
||||
their applications. It includes custom widgets that you can create and integrate,
|
||||
with the ability to preview interfaces right in the workflow builder before
|
||||
deployment.
|
||||
</p>
|
||||
|
||||
<figure className='my-8 overflow-hidden rounded-lg'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/widgets.png'
|
||||
alt='OpenAI AgentKit custom widgets interface'
|
||||
width={800}
|
||||
height={450}
|
||||
className='w-full'
|
||||
/>
|
||||
<figcaption className='sr-only'>
|
||||
OpenAI AgentKit ChatKit custom widgets preview
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Comprehensive Evaluation System
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
AgentKit includes out-of-the-box evaluation capabilities to measure agent
|
||||
performance. Features include datasets to assess agent nodes, prompt optimization
|
||||
tools, and the ability to run evaluations on external models beyond OpenAI's
|
||||
ecosystem.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Connectors and Integrations
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
The platform provides connectors to integrate with both internal tools and external
|
||||
services, enabling agents to interact with your existing tech stack.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>API Publishing</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Once your agent is ready, the publish feature allows you to integrate it as an API
|
||||
inside your codebase, making deployment straightforward.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Built-in Guardrails
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
AgentKit comes with guardrails out of the box, helping ensure agent behavior stays
|
||||
within defined boundaries and safety parameters.
|
||||
</p>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>
|
||||
What AgentKit Doesn't Offer
|
||||
</h3>
|
||||
<p className='mb-2 text-[19px] text-gray-800 leading-relaxed'>
|
||||
While AgentKit is powerful for building agents, it has some limitations:
|
||||
</p>
|
||||
<ul className='mb-4 ml-6 list-disc text-[19px] text-gray-800 leading-relaxed'>
|
||||
<li className='mb-2'>
|
||||
Only able to run OpenAI models—no support for other AI providers
|
||||
</li>
|
||||
<li className='mb-2'>
|
||||
Cannot make generic API requests in workflows—limited to MCP (Model Context
|
||||
Protocol) integrations only
|
||||
</li>
|
||||
<li className='mb-2'>Not an open-source platform</li>
|
||||
<li className='mb-2'>No workflow templates to accelerate development</li>
|
||||
<li className='mb-2'>
|
||||
No execution logs or detailed monitoring for debugging and observability
|
||||
</li>
|
||||
<li className='mb-2'>No ability to trigger workflows via external integrations</li>
|
||||
<li className='mb-2'>
|
||||
Limited out-of-the-box integration options compared to dedicated workflow
|
||||
automation platforms
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* Section 2: n8n */}
|
||||
<section className='mb-12'>
|
||||
<h2 className='mb-4 font-medium text-[28px] leading-tight sm:text-[32px]'>
|
||||
What is n8n?
|
||||
</h2>
|
||||
<p className='mb-6 text-[19px] text-gray-800 leading-relaxed'>
|
||||
n8n is a workflow automation platform that excels at connecting various services and
|
||||
APIs together. While it started as a general automation tool, n8n has evolved to
|
||||
support AI agent workflows alongside its traditional integration capabilities.
|
||||
</p>
|
||||
|
||||
<figure className='my-8 overflow-hidden rounded-lg'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/n8n.png'
|
||||
alt='n8n workflow automation interface'
|
||||
width={800}
|
||||
height={450}
|
||||
className='w-full'
|
||||
/>
|
||||
<figcaption className='sr-only'>
|
||||
n8n node-based visual workflow automation platform
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>Core Capabilities</h3>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Extensive Integration Library
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
n8n's primary strength lies in its vast library of pre-built integrations. With
|
||||
hundreds of connectors for popular services, it makes it easy to connect disparate
|
||||
systems without writing custom code.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Visual Workflow Builder
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
The platform provides a node-based visual interface for building workflows. Users
|
||||
can drag and drop nodes representing different services and configure how data flows
|
||||
between them.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Flexible Triggering Options
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
n8n supports various ways to trigger workflows, including webhooks, scheduled
|
||||
executions, and manual triggers, making it versatile for different automation
|
||||
scenarios.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
AI and LLM Integration
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
More recently, n8n has added support for AI models and agent-like capabilities,
|
||||
allowing users to incorporate language models into their automation workflows.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Self-Hosting Options
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
n8n offers both cloud-hosted and self-hosted deployment options, giving
|
||||
organizations control over their data and infrastructure.
|
||||
</p>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>Primary Use Cases</h3>
|
||||
<p className='mb-2 text-[19px] text-gray-800 leading-relaxed'>
|
||||
n8n is best suited for:
|
||||
</p>
|
||||
<ul className='mb-4 ml-6 list-disc text-[19px] text-gray-800 leading-relaxed'>
|
||||
<li className='mb-2'>Traditional workflow automation and service integration</li>
|
||||
<li className='mb-2'>Data synchronization between business tools</li>
|
||||
<li className='mb-2'>Event-driven automation workflows</li>
|
||||
<li className='mb-2'>Simple AI-enhanced automations</li>
|
||||
</ul>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>
|
||||
What n8n Doesn't Offer
|
||||
</h3>
|
||||
<p className='mb-2 text-[19px] text-gray-800 leading-relaxed'>
|
||||
While n8n is excellent for traditional automation, it has some limitations for AI
|
||||
agent development:
|
||||
</p>
|
||||
<ul className='mb-4 ml-6 list-disc text-[19px] text-gray-800 leading-relaxed'>
|
||||
<li className='mb-2'>
|
||||
No SDK to build workflows programmatically—limited to visual builder only
|
||||
</li>
|
||||
<li className='mb-2'>
|
||||
Not fully open source but fair-use licensed, with some restrictions
|
||||
</li>
|
||||
<li className='mb-2'>
|
||||
Free trial limited to 14 days, after which paid plans are required
|
||||
</li>
|
||||
<li className='mb-2'>Limited/complex parallel execution handling</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* Section 3: Sim */}
|
||||
<section className='mb-12'>
|
||||
<h2 className='mb-4 font-medium text-[28px] leading-tight sm:text-[32px]'>
|
||||
What is Sim?
|
||||
</h2>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Sim is a fully open-source platform (Apache 2.0 license) specifically designed for
|
||||
AI agent development. Unlike platforms that added AI capabilities as an
|
||||
afterthought, Sim was created from the ground up to address the unique challenges of
|
||||
building, testing, and deploying production-ready AI agents.
|
||||
</p>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>
|
||||
Comprehensive AI Agent Platform
|
||||
</h3>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Visual AI Workflow Builder
|
||||
</h4>
|
||||
<p className='mb-6 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Sim provides an intuitive drag-and-drop canvas where developers can build complex AI
|
||||
agent workflows visually. The platform supports sophisticated agent architectures,
|
||||
including multi-agent systems, conditional logic, loops, and parallel execution
|
||||
paths. Additionally, Sim's built-in AI Copilot can assist you directly in the
|
||||
editor, helping you build and modify workflows faster with intelligent suggestions
|
||||
and explanations.
|
||||
</p>
|
||||
|
||||
<figure className='my-8 overflow-hidden rounded-lg'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/sim.png'
|
||||
alt='Sim visual workflow builder with AI agent blocks'
|
||||
width={800}
|
||||
height={450}
|
||||
className='w-full'
|
||||
/>
|
||||
<figcaption className='sr-only'>
|
||||
Sim drag-and-drop AI agent workflow builder canvas
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
AI Copilot for Workflow Building
|
||||
</h4>
|
||||
<p className='mb-6 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Sim includes an intelligent in-editor AI assistant that helps you build and edit
|
||||
workflows faster. Copilot can explain complex concepts, suggest best practices, and
|
||||
even make changes to your workflow when you approve them. Using the @ context menu,
|
||||
you can reference workflows, blocks, knowledge bases, documentation, templates, and
|
||||
execution logs—giving Copilot the full context it needs to provide accurate,
|
||||
relevant assistance. This dramatically accelerates workflow development compared to
|
||||
building from scratch.
|
||||
</p>
|
||||
|
||||
<figure className='my-8 overflow-hidden rounded-lg'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/copilot.png'
|
||||
alt='Sim AI Copilot assisting with workflow development'
|
||||
width={800}
|
||||
height={450}
|
||||
className='w-full'
|
||||
/>
|
||||
<figcaption className='sr-only'>
|
||||
Sim AI Copilot in-editor assistant for workflow building
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Pre-Built Workflow Templates
|
||||
</h4>
|
||||
<p className='mb-6 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Get started quickly with Sim's extensive library of pre-built workflow templates.
|
||||
Browse templates across categories like Marketing, Sales, Finance, Support, and
|
||||
Artificial Intelligence. Each template is a production-ready workflow you can
|
||||
customize for your needs, saving hours of development time. Templates are created by
|
||||
the Sim team and community members, with popularity ratings and integration counts
|
||||
to help you find the right starting point.
|
||||
</p>
|
||||
|
||||
<figure className='my-8 overflow-hidden rounded-lg'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/templates.png'
|
||||
alt='Sim workflow templates gallery'
|
||||
width={800}
|
||||
height={450}
|
||||
className='w-full'
|
||||
/>
|
||||
<figcaption className='sr-only'>
|
||||
Sim pre-built workflow templates library
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
80+ Built-in Integrations
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Out of the box, Sim connects with 80+ services including multiple AI providers
|
||||
(OpenAI, Anthropic, Google, Groq, Cerebras, local Ollama models), communication
|
||||
tools (Gmail, Slack, Teams, Telegram, WhatsApp), productivity apps (Notion, Google
|
||||
Sheets, Airtable, Monday.com), and developer tools (GitHub, GitLab).
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Multiple Trigger Options
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Unlike AgentKit, Sim workflows can be triggered in multiple ways: chat interfaces,
|
||||
REST APIs, webhooks, scheduled jobs, or external events from integrated services
|
||||
like Slack and GitHub. This flexibility ensures your agents can be activated however
|
||||
your use case demands.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Real-Time Team Collaboration
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Sim enables multiple team members to work simultaneously on the same workflow with
|
||||
real-time editing, commenting, and comprehensive permissions management. This makes
|
||||
it ideal for teams building complex agent systems together.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Advanced Agent Capabilities
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
The platform includes specialized blocks for AI agents, RAG (Retrieval-Augmented
|
||||
Generation) systems, function calling, code execution, data processing, and
|
||||
evaluation. These purpose-built components enable developers to create sophisticated
|
||||
agentic workflows without custom coding.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Intelligent Knowledge Base with Vector Search
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Sim's native knowledge base goes far beyond simple document storage. Powered by
|
||||
pgvector, it provides semantic search that understands meaning and context, not just
|
||||
keywords. Upload documents in multiple formats (PDF, Word, Excel, Markdown, and
|
||||
more), and Sim automatically processes them with intelligent chunking, generates
|
||||
vector embeddings, and makes them instantly searchable. The knowledge base supports
|
||||
natural language queries, concept-based retrieval, multi-language understanding, and
|
||||
configurable chunk sizes (100-4,000 characters). This makes building RAG agents
|
||||
straightforward—your AI can search through your organization's knowledge with
|
||||
context-aware precision.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Comprehensive Execution Logging and Monitoring
|
||||
</h4>
|
||||
<p className='mb-6 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Sim provides enterprise-grade logging that captures every detail of workflow
|
||||
execution. Track workflow runs with execution IDs, view block-level logs with
|
||||
precise timing and duration metrics, monitor token usage and costs per execution,
|
||||
and debug failures with detailed error traces and trace spans. The logging system
|
||||
integrates with Copilot—you can reference execution logs directly in your Copilot
|
||||
conversations to understand what happened and troubleshoot issues. This level of
|
||||
observability is essential for production AI agents where understanding behavior and
|
||||
debugging issues quickly is critical.
|
||||
</p>
|
||||
|
||||
<figure className='my-8 overflow-hidden rounded-lg'>
|
||||
<Image
|
||||
src='/blog/openai-vs-n8n-vs-sim/logs.png'
|
||||
alt='Sim execution logs and monitoring dashboard'
|
||||
width={800}
|
||||
height={450}
|
||||
className='w-full'
|
||||
/>
|
||||
<figcaption className='sr-only'>
|
||||
Sim execution logs dashboard with detailed workflow monitoring
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Custom Integrations via MCP Protocol
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Beyond the 80+ built-in integrations, Sim supports the Model Context Protocol (MCP),
|
||||
allowing developers to create custom integrations for proprietary systems or
|
||||
specialized tools.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Flexible Deployment Options
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Sim offers both cloud-hosted and self-hosted deployment options. Organizations can
|
||||
run Sim on their own infrastructure for complete control, or use the managed cloud
|
||||
service for simplicity. The platform is SOC2 and HIPAA compliant, ensuring
|
||||
enterprise-level security.
|
||||
</p>
|
||||
|
||||
<h4 className='mt-4 mb-2 font-medium text-[19px] leading-tight'>
|
||||
Production-Ready Infrastructure
|
||||
</h4>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
The platform includes everything needed for production deployments: background job
|
||||
processing, webhook handling, monitoring, and API management. Workflows can be
|
||||
published as REST API endpoints, embedded via SDKs, or run through chat interfaces.
|
||||
</p>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>
|
||||
What You Can Build with Sim
|
||||
</h3>
|
||||
<ul className='mb-4 ml-6 list-disc text-[19px] text-gray-800 leading-relaxed'>
|
||||
<li className='mb-2'>
|
||||
<strong>AI Assistants & Chatbots:</strong> Intelligent agents that search the web,
|
||||
access calendars, send emails, and interact with business tools
|
||||
</li>
|
||||
<li className='mb-2'>
|
||||
<strong>Business Process Automation:</strong> Automate repetitive tasks like data
|
||||
entry, report generation, customer support, and content creation
|
||||
</li>
|
||||
<li className='mb-2'>
|
||||
<strong>Data Processing & Analysis:</strong> Extract insights from documents,
|
||||
analyze datasets, generate reports, and sync data between systems
|
||||
</li>
|
||||
<li className='mb-2'>
|
||||
<strong>API Integration Workflows:</strong> Connect multiple services into unified
|
||||
endpoints and orchestrate complex business logic
|
||||
</li>
|
||||
<li className='mb-2'>
|
||||
<strong>RAG Systems:</strong> Build sophisticated retrieval-augmented generation
|
||||
pipelines with custom knowledge bases
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>
|
||||
Drawbacks to Consider
|
||||
</h3>
|
||||
<p className='mb-2 text-[19px] text-gray-800 leading-relaxed'>
|
||||
While Sim excels at AI agent workflows, there are some tradeoffs:
|
||||
</p>
|
||||
<ul className='mb-4 ml-6 list-disc text-[19px] text-gray-800 leading-relaxed'>
|
||||
<li className='mb-2'>
|
||||
Fewer pre-built integrations compared to n8n's extensive library—though Sim's 80+
|
||||
integrations cover most AI agent use cases and MCP protocol enables custom
|
||||
integrations
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* Comparison Section */}
|
||||
<section className='mb-12'>
|
||||
<h2 className='mb-4 font-medium text-[28px] leading-tight sm:text-[32px]'>
|
||||
Key Differences
|
||||
</h2>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
While all three platforms enable AI agent development, they excel in different
|
||||
areas:
|
||||
</p>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>OpenAI AgentKit</h3>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
<strong>Best for:</strong> Teams deeply invested in the OpenAI ecosystem who
|
||||
prioritize evaluation and testing capabilities. Ideal when you need tight
|
||||
integration with OpenAI's latest models and want built-in prompt optimization and
|
||||
evaluation tools.
|
||||
</p>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
<strong>Limitations:</strong> Locked into OpenAI models only, not open-source, no
|
||||
workflow templates or execution logs, limited triggering options, and fewer
|
||||
out-of-the-box integrations.
|
||||
</p>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>n8n</h3>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
<strong>Best for:</strong> Traditional workflow automation with some AI enhancement.
|
||||
Excellent when your primary need is connecting business tools and services, with AI
|
||||
as a complementary feature rather than the core focus.
|
||||
</p>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
<strong>Limitations:</strong> No SDK for programmatic workflow building, fair-use
|
||||
licensing (not fully open source), 14-day trial limitation, and AI agent
|
||||
capabilities are newer and less mature compared to its traditional automation
|
||||
features.
|
||||
</p>
|
||||
|
||||
<h3 className='mt-6 mb-3 font-medium text-[22px] leading-tight'>Sim</h3>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
<strong>Best for:</strong> Building production-ready AI agent workflows that require
|
||||
flexibility, collaboration, and comprehensive tooling. Ideal for teams that need AI
|
||||
Copilot assistance, advanced knowledge base features, detailed logging, and the
|
||||
ability to work across multiple AI providers with purpose-built agentic workflow
|
||||
tools.
|
||||
</p>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
<strong>Limitations:</strong> Fewer integrations than n8n's extensive library,
|
||||
though the 80+ built-in integrations and MCP protocol support cover most AI agent
|
||||
needs.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Conclusion */}
|
||||
<section className='mb-12'>
|
||||
<h2 className='mb-4 font-medium text-[28px] leading-tight sm:text-[32px]'>
|
||||
Which Should You Choose?
|
||||
</h2>
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
The right platform depends on your specific needs and context:
|
||||
</p>
|
||||
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Choose <strong>OpenAI AgentKit</strong> if you're exclusively using OpenAI models
|
||||
and want to build chat interfaces with the ChatKit. It's a solid choice for teams
|
||||
that want to stay within OpenAI's ecosystem and prioritize testing capabilities.
|
||||
</p>
|
||||
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Choose <strong>n8n</strong> if your primary use case is traditional workflow
|
||||
automation between business tools, with occasional AI enhancement. It's ideal for
|
||||
organizations already familiar with n8n who want to add some AI capabilities to
|
||||
existing automations.
|
||||
</p>
|
||||
|
||||
<p className='mb-4 text-[19px] text-gray-800 leading-relaxed'>
|
||||
Choose <strong>Sim</strong> if you're building AI agents as your primary objective
|
||||
and need a platform purpose-built for that use case. Sim provides the most
|
||||
comprehensive feature set for agentic workflows, with AI Copilot to accelerate
|
||||
development, parallel execution handling, intelligent knowledge base for RAG
|
||||
applications, detailed execution logging for production monitoring, flexibility
|
||||
across AI providers, extensive integrations, team collaboration, and deployment
|
||||
options that scale from prototype to production.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Publisher information for schema */}
|
||||
<meta itemProp='publisher' content='Sim' />
|
||||
<meta itemProp='inLanguage' content='en-US' />
|
||||
<meta
|
||||
itemProp='keywords'
|
||||
content='AI agents, OpenAI AgentKit, n8n, Sim, workflow automation'
|
||||
/>
|
||||
</article>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
import type { Metadata } from 'next'
|
||||
import OpenAiN8nSim from './openai-n8n-sim'
|
||||
|
||||
const baseUrl = 'https://sim.ai'
|
||||
const canonicalUrl = `${baseUrl}/blog/openai-vs-n8n-vs-sim`
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'OpenAI AgentKit vs n8n vs Sim: AI Agent Workflow Builder Comparison | Sim',
|
||||
description:
|
||||
'Compare OpenAI AgentKit, n8n, and Sim for building AI agent workflows. Explore key differences in capabilities, integrations, collaboration, and which platform best fits your production AI agent needs.',
|
||||
keywords: [
|
||||
'AgentKit',
|
||||
'AI agents',
|
||||
'AI agent development',
|
||||
'agents',
|
||||
'workflow builder',
|
||||
'visual workflow builder',
|
||||
'workflows',
|
||||
'OpenAI AgentKit',
|
||||
'OpenAI',
|
||||
'OpenAI Responses API',
|
||||
'n8n',
|
||||
'n8n workflow automation',
|
||||
'AI workflow automation',
|
||||
'workflow automation platform',
|
||||
'Sim',
|
||||
'agent builder comparison',
|
||||
'RAG agents',
|
||||
'RAG systems',
|
||||
'retrieval augmented generation',
|
||||
'ChatKit',
|
||||
'agent evaluation',
|
||||
'prompt optimization',
|
||||
'multi-agent systems',
|
||||
'team collaboration workflows',
|
||||
'production AI agents',
|
||||
'AI guardrails',
|
||||
'workflow integrations',
|
||||
'self-hosted AI agents',
|
||||
'cloud AI agent platform',
|
||||
'MCP protocol',
|
||||
'Model Context Protocol',
|
||||
'knowledge base integration',
|
||||
'vector embeddings',
|
||||
'AI agent canvas',
|
||||
'agentic workflows',
|
||||
'AI agent API',
|
||||
'AI chatbot workflows',
|
||||
'business process automation',
|
||||
'AI Copilot',
|
||||
'workflow copilot',
|
||||
'AI assistant for workflows',
|
||||
'vector search',
|
||||
'semantic search',
|
||||
'pgvector',
|
||||
'knowledge base AI',
|
||||
'document embeddings',
|
||||
'execution logging',
|
||||
'workflow monitoring',
|
||||
'AI agent observability',
|
||||
'workflow debugging',
|
||||
'execution traces',
|
||||
'AI workflow logs',
|
||||
'intelligent chunking',
|
||||
'context-aware search',
|
||||
],
|
||||
authors: [{ name: 'Emir Karabeg', url: 'https://x.com/karabegemir' }],
|
||||
creator: 'Emir Karabeg',
|
||||
publisher: 'Sim',
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
googleBot: {
|
||||
index: true,
|
||||
follow: true,
|
||||
'max-video-preview': -1,
|
||||
'max-image-preview': 'large',
|
||||
'max-snippet': -1,
|
||||
},
|
||||
},
|
||||
alternates: {
|
||||
canonical: canonicalUrl,
|
||||
},
|
||||
openGraph: {
|
||||
title: 'OpenAI AgentKit vs n8n vs Sim: AI Agent Workflow Builder Comparison',
|
||||
description:
|
||||
'Compare OpenAI AgentKit, n8n, and Sim for building AI agent workflows. Explore key differences in capabilities, integrations, and which platform fits your production needs.',
|
||||
url: canonicalUrl,
|
||||
siteName: 'Sim',
|
||||
locale: 'en_US',
|
||||
type: 'article',
|
||||
publishedTime: '2025-10-06T00:00:00.000Z',
|
||||
modifiedTime: '2025-10-06T00:00:00.000Z',
|
||||
authors: ['Emir Karabeg'],
|
||||
section: 'Technology',
|
||||
tags: [
|
||||
'AI Agents',
|
||||
'Workflow Automation',
|
||||
'OpenAI AgentKit',
|
||||
'n8n',
|
||||
'Sim',
|
||||
'AgentKit',
|
||||
'AI Development',
|
||||
'RAG',
|
||||
'MCP Protocol',
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: `${baseUrl}/blog/openai-vs-n8n-vs-sim/workflow.png`,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'Sim AI agent workflow builder interface comparison',
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'OpenAI AgentKit vs n8n vs Sim: AI Agent Workflow Builder Comparison',
|
||||
description:
|
||||
'Compare OpenAI AgentKit, n8n, and Sim for building AI agent workflows. Explore key differences in capabilities, integrations, and which platform fits your production needs.',
|
||||
images: ['/blog/openai-vs-n8n-vs-sim/workflow.png'],
|
||||
creator: '@karabegemir',
|
||||
site: '@simai',
|
||||
},
|
||||
other: {
|
||||
'article:published_time': '2025-10-06T00:00:00.000Z',
|
||||
'article:modified_time': '2025-10-06T00:00:00.000Z',
|
||||
'article:author': 'Emir Karabeg',
|
||||
'article:section': 'Technology',
|
||||
},
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return <OpenAiN8nSim />
|
||||
}
|
||||
@@ -1,5 +1,223 @@
|
||||
import { redirect } from 'next/navigation'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
import { getAllPostMeta } from '@/lib/blog/registry'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
|
||||
export default function BlogPage() {
|
||||
redirect('/blog/openai-vs-n8n-vs-sim')
|
||||
export const revalidate = 3600
|
||||
|
||||
export default async function BlogIndex({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ page?: string; tag?: string }>
|
||||
}) {
|
||||
const { page, tag } = await searchParams
|
||||
const pageNum = Math.max(1, Number(page || 1))
|
||||
const perPage = 20
|
||||
|
||||
const all = await getAllPostMeta()
|
||||
const filtered = tag ? all.filter((p) => p.tags.includes(tag)) : all
|
||||
const totalPages = Math.max(1, Math.ceil(filtered.length / perPage))
|
||||
const start = (pageNum - 1) * perPage
|
||||
const posts = filtered.slice(start, start + perPage)
|
||||
// Tag filter chips are intentionally disabled for now.
|
||||
// const tags = await getAllTags()
|
||||
const blogJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Blog',
|
||||
name: 'Sim Blog',
|
||||
url: 'https://sim.ai/blog',
|
||||
description: 'Announcements, insights, and guides for building AI agent workflows.',
|
||||
}
|
||||
|
||||
const [featured, ...rest] = posts
|
||||
|
||||
return (
|
||||
<main className={`${soehne.className} mx-auto max-w-[1200px] px-6 py-12 sm:px-8 md:px-12`}>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(blogJsonLd) }}
|
||||
/>
|
||||
<h1 className='mb-3 font-medium text-[40px] leading-tight sm:text-[56px]'>The Sim Times</h1>
|
||||
<p className='mb-10 text-[18px] text-gray-700'>
|
||||
Announcements, insights, and guides for building AI agent workflows.
|
||||
</p>
|
||||
|
||||
{/* Tag filter chips hidden until we have more posts */}
|
||||
{/* <div className='mb-10 flex flex-wrap gap-3'>
|
||||
<Link href='/blog' className={`rounded-full border px-3 py-1 text-sm ${!tag ? 'border-black bg-black text-white' : 'border-gray-300'}`}>All</Link>
|
||||
{tags.map((t) => (
|
||||
<Link key={t.tag} href={`/blog?tag=${encodeURIComponent(t.tag)}`} className={`rounded-full border px-3 py-1 text-sm ${tag === t.tag ? 'border-black bg-black text-white' : 'border-gray-300'}`}>
|
||||
{t.tag} ({t.count})
|
||||
</Link>
|
||||
))}
|
||||
</div> */}
|
||||
|
||||
{featured && (
|
||||
<Link href={`/blog/${featured.slug}`} className='group mb-10 block'>
|
||||
<div className='overflow-hidden rounded-2xl border border-gray-200'>
|
||||
<Image
|
||||
src={featured.ogImage}
|
||||
alt={featured.title}
|
||||
width={1200}
|
||||
height={630}
|
||||
className='h-[320px] w-full object-cover sm:h-[420px]'
|
||||
/>
|
||||
<div className='p-6 sm:p-8'>
|
||||
<div className='mb-2 text-gray-600 text-xs sm:text-sm'>
|
||||
{new Date(featured.date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<h2 className='shine-text mb-2 font-medium text-2xl leading-tight sm:text-3xl'>
|
||||
{featured.title}
|
||||
</h2>
|
||||
<p className='mb-4 text-gray-700 sm:text-base'>{featured.description}</p>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='-space-x-2 flex'>
|
||||
{(featured.authors && featured.authors.length > 0
|
||||
? featured.authors
|
||||
: [featured.author]
|
||||
)
|
||||
.slice(0, 3)
|
||||
.map((author, idx) => (
|
||||
<Avatar key={idx} className='size-5 border-2 border-white'>
|
||||
<AvatarImage src={author?.avatarUrl} alt={author?.name} />
|
||||
<AvatarFallback className='border-2 border-white bg-gray-100 text-gray-600 text-xs'>
|
||||
{author?.name.slice(0, 2)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
</div>
|
||||
<span className='text-gray-600 text-xs sm:text-sm'>
|
||||
{(featured.authors && featured.authors.length > 0
|
||||
? featured.authors
|
||||
: [featured.author]
|
||||
)
|
||||
.slice(0, 2)
|
||||
.map((a, i) => a?.name)
|
||||
.join(', ')}
|
||||
{(featured.authors && featured.authors.length > 0
|
||||
? featured.authors
|
||||
: [featured.author]
|
||||
).length > 2 && (
|
||||
<>
|
||||
{' '}
|
||||
and{' '}
|
||||
{(featured.authors && featured.authors.length > 0
|
||||
? featured.authors
|
||||
: [featured.author]
|
||||
).length - 2}{' '}
|
||||
other
|
||||
{(featured.authors && featured.authors.length > 0
|
||||
? featured.authors
|
||||
: [featured.author]
|
||||
).length -
|
||||
2 >
|
||||
1
|
||||
? 's'
|
||||
: ''}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* Masonry-like list using CSS columns for varied heights */}
|
||||
<div className='gap-6 [column-fill:_balance] md:columns-2 lg:columns-3'>
|
||||
{rest.map((p, i) => {
|
||||
const size = i % 3 === 0 ? 'h-64' : i % 3 === 1 ? 'h-56' : 'h-48'
|
||||
return (
|
||||
<Link
|
||||
key={p.slug}
|
||||
href={`/blog/${p.slug}`}
|
||||
className='group mb-6 inline-block w-full break-inside-avoid'
|
||||
>
|
||||
<div className='overflow-hidden rounded-xl border border-gray-200 transition-colors duration-300 hover:border-gray-300'>
|
||||
<Image
|
||||
src={p.ogImage}
|
||||
alt={p.title}
|
||||
width={800}
|
||||
height={450}
|
||||
className={`${size} w-full object-cover`}
|
||||
/>
|
||||
<div className='p-4'>
|
||||
<div className='mb-2 text-gray-600 text-xs'>
|
||||
{new Date(p.date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<h3 className='shine-text mb-1 font-medium text-lg leading-tight'>{p.title}</h3>
|
||||
<p className='mb-3 line-clamp-3 text-gray-700 text-sm'>{p.description}</p>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='-space-x-1.5 flex'>
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
|
||||
.slice(0, 3)
|
||||
.map((author, idx) => (
|
||||
<Avatar key={idx} className='size-4 border border-white'>
|
||||
<AvatarImage src={author?.avatarUrl} alt={author?.name} />
|
||||
<AvatarFallback className='border border-white bg-gray-100 text-[10px] text-gray-600'>
|
||||
{author?.name.slice(0, 2)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
</div>
|
||||
<span className='text-gray-600 text-xs'>
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
|
||||
.slice(0, 2)
|
||||
.map((a) => a?.name)
|
||||
.join(', ')}
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length > 2 && (
|
||||
<>
|
||||
{' '}
|
||||
and{' '}
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2}{' '}
|
||||
other
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2 >
|
||||
1
|
||||
? 's'
|
||||
: ''}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<div className='mt-10 flex items-center justify-center gap-3'>
|
||||
{pageNum > 1 && (
|
||||
<Link
|
||||
href={`/blog?page=${pageNum - 1}${tag ? `&tag=${encodeURIComponent(tag)}` : ''}`}
|
||||
className='rounded border px-3 py-1 text-sm'
|
||||
>
|
||||
Previous
|
||||
</Link>
|
||||
)}
|
||||
<span className='text-gray-600 text-sm'>
|
||||
Page {pageNum} of {totalPages}
|
||||
</span>
|
||||
{pageNum < totalPages && (
|
||||
<Link
|
||||
href={`/blog?page=${pageNum + 1}${tag ? `&tag=${encodeURIComponent(tag)}` : ''}`}
|
||||
className='rounded border px-3 py-1 text-sm'
|
||||
>
|
||||
Next
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
41
apps/sim/app/(landing)/blog/rss.xml/route.ts
Normal file
41
apps/sim/app/(landing)/blog/rss.xml/route.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getAllPostMeta } from '@/lib/blog/registry'
|
||||
|
||||
export const revalidate = 3600
|
||||
|
||||
export async function GET() {
|
||||
const posts = await getAllPostMeta()
|
||||
const items = posts.slice(0, 50)
|
||||
const site = 'https://sim.ai'
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>Sim Blog</title>
|
||||
<link>${site}</link>
|
||||
<description>Announcements, insights, and guides for AI agent workflows.</description>
|
||||
${items
|
||||
.map(
|
||||
(p) => `
|
||||
<item>
|
||||
<title><![CDATA[${p.title}]]></title>
|
||||
<link>${p.canonical}</link>
|
||||
<guid>${p.canonical}</guid>
|
||||
<pubDate>${new Date(p.date).toUTCString()}</pubDate>
|
||||
<description><![CDATA[${p.description}]]></description>
|
||||
${(p.authors || [p.author])
|
||||
.map((a) => `<author><![CDATA[${a.name}${a.url ? ` (${a.url})` : ''}]]></author>`)
|
||||
.join('\n')}
|
||||
</item>`
|
||||
)
|
||||
.join('')}
|
||||
</channel>
|
||||
</rss>`
|
||||
|
||||
return new NextResponse(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
|
||||
},
|
||||
})
|
||||
}
|
||||
30
apps/sim/app/(landing)/blog/sitemap-images.xml/route.ts
Normal file
30
apps/sim/app/(landing)/blog/sitemap-images.xml/route.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getAllPostMeta } from '@/lib/blog/registry'
|
||||
|
||||
export const revalidate = 3600
|
||||
|
||||
export async function GET() {
|
||||
const posts = await getAllPostMeta()
|
||||
const base = 'https://sim.ai'
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
|
||||
${posts
|
||||
.map(
|
||||
(p) => `<url>
|
||||
<loc>${p.canonical}</loc>
|
||||
<image:image>
|
||||
<image:loc>${p.ogImage.startsWith('http') ? p.ogImage : `${base}${p.ogImage}`}</image:loc>
|
||||
<image:title><![CDATA[${p.title}]]></image:title>
|
||||
<image:caption><![CDATA[${p.description}]]></image:caption>
|
||||
</image:image>
|
||||
</url>`
|
||||
)
|
||||
.join('\n')}
|
||||
</urlset>`
|
||||
return new NextResponse(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
|
||||
},
|
||||
})
|
||||
}
|
||||
25
apps/sim/app/(landing)/blog/tags/page.tsx
Normal file
25
apps/sim/app/(landing)/blog/tags/page.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import Link from 'next/link'
|
||||
import { getAllTags } from '@/lib/blog/registry'
|
||||
|
||||
export default async function TagsIndex() {
|
||||
const tags = await getAllTags()
|
||||
return (
|
||||
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
|
||||
<h1 className='mb-6 font-medium text-[32px] leading-tight'>Browse by tag</h1>
|
||||
<div className='flex flex-wrap gap-3'>
|
||||
<Link href='/blog' className='rounded-full border border-gray-300 px-3 py-1 text-sm'>
|
||||
All
|
||||
</Link>
|
||||
{tags.map((t) => (
|
||||
<Link
|
||||
key={t.tag}
|
||||
href={`/blog?tag=${encodeURIComponent(t.tag)}`}
|
||||
className='rounded-full border border-gray-300 px-3 py-1 text-sm'
|
||||
>
|
||||
{t.tag} ({t.count})
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -20,7 +20,7 @@ interface NavProps {
|
||||
}
|
||||
|
||||
export default function Nav({ hideAuthButtons = false, variant = 'landing' }: NavProps = {}) {
|
||||
const [githubStars, setGithubStars] = useState('17.4k')
|
||||
const [githubStars, setGithubStars] = useState('17.7k')
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
const [isLoginHovered, setIsLoginHovered] = useState(false)
|
||||
const router = useRouter()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { MetadataRoute } from 'next'
|
||||
import { getAllPostMeta } from '@/lib/blog/registry'
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const baseUrl = 'https://sim.ai'
|
||||
|
||||
// Static pages
|
||||
const staticPages = [
|
||||
{
|
||||
url: baseUrl,
|
||||
@@ -37,15 +37,13 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
||||
},
|
||||
]
|
||||
|
||||
// Blog posts and content pages
|
||||
const blogPages = [
|
||||
{
|
||||
url: `${baseUrl}/blog/openai-vs-n8n-vs-sim`,
|
||||
lastModified: new Date('2025-10-11'),
|
||||
changeFrequency: 'monthly' as const,
|
||||
priority: 0.9,
|
||||
},
|
||||
]
|
||||
const posts = await getAllPostMeta()
|
||||
const blogPages = posts.map((p) => ({
|
||||
url: p.canonical,
|
||||
lastModified: new Date(p.updated ?? p.date),
|
||||
changeFrequency: 'monthly' as const,
|
||||
priority: 0.9 as const,
|
||||
}))
|
||||
|
||||
return [...staticPages, ...blogPages]
|
||||
}
|
||||
|
||||
7
apps/sim/content/authors/emir.json
Normal file
7
apps/sim/content/authors/emir.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": "emir",
|
||||
"name": "Emir Karabeg",
|
||||
"url": "https://x.com/karabegemir",
|
||||
"xHandle": "karabegemir",
|
||||
"avatarUrl": "/blog/authors/emir.png"
|
||||
}
|
||||
7
apps/sim/content/authors/sim.json
Normal file
7
apps/sim/content/authors/sim.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": "sim",
|
||||
"name": "The Sim Team",
|
||||
"url": "https://x.com/simdotai",
|
||||
"xHandle": "simdotai",
|
||||
"avatarUrl": "/logo/primary/small.png"
|
||||
}
|
||||
7
apps/sim/content/authors/waleed.json
Normal file
7
apps/sim/content/authors/waleed.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": "waleed",
|
||||
"name": "Waleed Latif",
|
||||
"url": "https://x.com/typingwala",
|
||||
"xHandle": "typingwala",
|
||||
"avatarUrl": "/blog/authors/waleed.png"
|
||||
}
|
||||
216
apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx
Normal file
216
apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
slug: openai-vs-n8n-vs-sim
|
||||
title: 'OpenAI AgentKit vs n8n vs Sim: AI Agent Workflow Builder Comparison'
|
||||
description: OpenAI just released AgentKit for building AI agents. How does it compare to workflow automation platforms like n8n and purpose-built AI agent builders like Sim?
|
||||
date: 2025-10-06
|
||||
updated: 2025-10-06
|
||||
authors:
|
||||
- emir
|
||||
readingTime: 9
|
||||
tags: [AI Agents, Workflow Automation, OpenAI AgentKit, n8n, Sim, MCP]
|
||||
ogImage: /blog/openai-vs-n8n-vs-sim/workflow.png
|
||||
canonical: https://sim.ai/blog/openai-vs-n8n-vs-sim
|
||||
draft: false
|
||||
---
|
||||
|
||||
When building AI agent workflows, developers often evaluate multiple platforms to find the right fit for their needs. Three platforms frequently come up in these discussions: OpenAI's new AgentKit, the established workflow automation tool n8n, and Sim, a purpose-built AI agent workflow builder. While all three enable AI agent development, they take fundamentally different approaches, each with distinct strengths and ideal use cases.
|
||||
|
||||
## What is OpenAI AgentKit?
|
||||
|
||||
OpenAI AgentKit is a set of building blocks designed to help developers take AI agents from prototype to production. Built on top of the OpenAI Responses API, it provides a structured approach to building and deploying intelligent agents.
|
||||
|
||||

|
||||
|
||||
### Core Features
|
||||
|
||||
#### Agent Builder Canvas
|
||||
|
||||
AgentKit provides a visual canvas where developers can design and build agents. This interface allows you to model complex workflows visually, making it easier to understand and iterate on agent behavior. The builder is powered by OpenAI's Responses API.
|
||||
|
||||
#### ChatKit for Embedded Interfaces
|
||||
|
||||
ChatKit enables developers to embed chat interfaces to run workflows directly within their applications. It includes custom widgets that you can create and integrate, with the ability to preview interfaces right in the workflow builder before deployment.
|
||||
|
||||

|
||||
|
||||
#### Comprehensive Evaluation System
|
||||
|
||||
AgentKit includes out-of-the-box evaluation capabilities to measure agent performance. Features include datasets to assess agent nodes, prompt optimization tools, and the ability to run evaluations on external models beyond OpenAI's ecosystem.
|
||||
|
||||
#### Connectors and Integrations
|
||||
|
||||
The platform provides connectors to integrate with both internal tools and external services, enabling agents to interact with your existing tech stack.
|
||||
|
||||
#### API Publishing
|
||||
|
||||
Once your agent is ready, the publish feature allows you to integrate it as an API inside your codebase, making deployment straightforward.
|
||||
|
||||
#### Built-in Guardrails
|
||||
|
||||
AgentKit comes with guardrails out of the box, helping ensure agent behavior stays within defined boundaries and safety parameters.
|
||||
|
||||
### What AgentKit Doesn't Offer
|
||||
|
||||
While AgentKit is powerful for building agents, it has some limitations:
|
||||
|
||||
- Only able to run OpenAI models—no support for other AI providers
|
||||
- Cannot make generic API requests in workflows—limited to MCP (Model Context Protocol) integrations only
|
||||
- Not an open-source platform
|
||||
- No workflow templates to accelerate development
|
||||
- No execution logs or detailed monitoring for debugging and observability
|
||||
- No ability to trigger workflows via external integrations
|
||||
- Limited out-of-the-box integration options compared to dedicated workflow automation platforms
|
||||
|
||||
## What is n8n?
|
||||
|
||||
n8n is a workflow automation platform that excels at connecting various services and APIs together. While it started as a general automation tool, n8n has evolved to support AI agent workflows alongside its traditional integration capabilities.
|
||||
|
||||

|
||||
|
||||
### Core Capabilities
|
||||
|
||||
#### Extensive Integration Library
|
||||
|
||||
n8n's primary strength lies in its vast library of pre-built integrations. With hundreds of connectors for popular services, it makes it easy to connect disparate systems without writing custom code.
|
||||
|
||||
#### Visual Workflow Builder
|
||||
|
||||
The platform provides a node-based visual interface for building workflows. Users can drag and drop nodes representing different services and configure how data flows between them.
|
||||
|
||||
#### Flexible Triggering Options
|
||||
|
||||
n8n supports various ways to trigger workflows, including webhooks, scheduled executions, and manual triggers, making it versatile for different automation scenarios.
|
||||
|
||||
#### AI and LLM Integration
|
||||
|
||||
More recently, n8n has added support for AI models and agent-like capabilities, allowing users to incorporate language models into their automation workflows.
|
||||
|
||||
#### Self-Hosting Options
|
||||
|
||||
n8n offers both cloud-hosted and self-hosted deployment options, giving organizations control over their data and infrastructure.
|
||||
|
||||
### Primary Use Cases
|
||||
|
||||
n8n is best suited for:
|
||||
|
||||
- Traditional workflow automation and service integration
|
||||
- Data synchronization between business tools
|
||||
- Event-driven automation workflows
|
||||
- Simple AI-enhanced automations
|
||||
|
||||
### What n8n Doesn't Offer
|
||||
|
||||
While n8n is excellent for traditional automation, it has some limitations for AI agent development:
|
||||
|
||||
- No SDK to build workflows programmatically—limited to visual builder only
|
||||
- Not fully open source but fair-use licensed, with some restrictions
|
||||
- Free trial limited to 14 days, after which paid plans are required
|
||||
- Limited/complex parallel execution handling
|
||||
|
||||
## What is Sim?
|
||||
|
||||
Sim is a fully open-source platform (Apache 2.0 license) specifically designed for AI agent development. Unlike platforms that added AI capabilities as an afterthought, Sim was created from the ground up to address the unique challenges of building, testing, and deploying production-ready AI agents.
|
||||
|
||||
### Comprehensive AI Agent Platform
|
||||
|
||||
#### Visual AI Workflow Builder
|
||||
|
||||
Sim provides an intuitive drag-and-drop canvas where developers can build complex AI agent workflows visually. The platform supports sophisticated agent architectures, including multi-agent systems, conditional logic, loops, and parallel execution paths. Additionally, Sim's built-in AI Copilot can assist you directly in the editor, helping you build and modify workflows faster with intelligent suggestions and explanations.
|
||||
|
||||

|
||||
|
||||
#### AI Copilot for Workflow Building
|
||||
|
||||
Sim includes an intelligent in-editor AI assistant that helps you build and edit workflows faster. Copilot can explain complex concepts, suggest best practices, and even make changes to your workflow when you approve them. Using the @ context menu, you can reference workflows, blocks, knowledge bases, documentation, templates, and execution logs—giving Copilot the full context it needs to provide accurate, relevant assistance. This dramatically accelerates workflow development compared to building from scratch.
|
||||
|
||||

|
||||
|
||||
#### Pre-Built Workflow Templates
|
||||
|
||||
Get started quickly with Sim's extensive library of pre-built workflow templates. Browse templates across categories like Marketing, Sales, Finance, Support, and Artificial Intelligence. Each template is a production-ready workflow you can customize for your needs, saving hours of development time. Templates are created by the Sim team and community members, with popularity ratings and integration counts to help you find the right starting point.
|
||||
|
||||

|
||||
|
||||
#### 80+ Built-in Integrations
|
||||
|
||||
Out of the box, Sim connects with 80+ services including multiple AI providers (OpenAI, Anthropic, Google, Groq, Cerebras, local Ollama models), communication tools (Gmail, Slack, Teams, Telegram, WhatsApp), productivity apps (Notion, Google Sheets, Airtable, Monday.com), and developer tools (GitHub, GitLab).
|
||||
|
||||
#### Multiple Trigger Options
|
||||
|
||||
Unlike AgentKit, Sim workflows can be triggered in multiple ways: chat interfaces, REST APIs, webhooks, scheduled jobs, or external events from integrated services like Slack and GitHub. This flexibility ensures your agents can be activated however your use case demands.
|
||||
|
||||
#### Real-Time Team Collaboration
|
||||
|
||||
Sim enables multiple team members to work simultaneously on the same workflow with real-time editing, commenting, and comprehensive permissions management. This makes it ideal for teams building complex agent systems together.
|
||||
|
||||
#### Advanced Agent Capabilities
|
||||
|
||||
The platform includes specialized blocks for AI agents, RAG (Retrieval-Augmented Generation) systems, function calling, code execution, data processing, and evaluation. These purpose-built components enable developers to create sophisticated agentic workflows without custom coding.
|
||||
|
||||
#### Intelligent Knowledge Base with Vector Search
|
||||
|
||||
Sim's native knowledge base goes far beyond simple document storage. Powered by pgvector, it provides semantic search that understands meaning and context, not just keywords. Upload documents in multiple formats (PDF, Word, Excel, Markdown, and more), and Sim automatically processes them with intelligent chunking, generates vector embeddings, and makes them instantly searchable. The knowledge base supports natural language queries, concept-based retrieval, multi-language understanding, and configurable chunk sizes (100-4,000 characters). This makes building RAG agents straightforward—your AI can search through your organization's knowledge with context-aware precision.
|
||||
|
||||
#### Comprehensive Execution Logging and Monitoring
|
||||
|
||||
Sim provides enterprise-grade logging that captures every detail of workflow execution. Track workflow runs with execution IDs, view block-level logs with precise timing and duration metrics, monitor token usage and costs per execution, and debug failures with detailed error traces and trace spans. The logging system integrates with Copilot—you can reference execution logs directly in your Copilot conversations to understand what happened and troubleshoot issues. This level of observability is essential for production AI agents where understanding behavior and debugging issues quickly is critical.
|
||||
|
||||

|
||||
|
||||
#### Custom Integrations via MCP Protocol
|
||||
|
||||
Beyond the 80+ built-in integrations, Sim supports the Model Context Protocol (MCP), allowing developers to create custom integrations for proprietary systems or specialized tools.
|
||||
|
||||
#### Flexible Deployment Options
|
||||
|
||||
Sim offers both cloud-hosted and self-hosted deployment options. Organizations can run Sim on their own infrastructure for complete control, or use the managed cloud service for simplicity. The platform is SOC2 and HIPAA compliant, ensuring enterprise-level security.
|
||||
|
||||
#### Production-Ready Infrastructure
|
||||
|
||||
The platform includes everything needed for production deployments: background job processing, webhook handling, monitoring, and API management. Workflows can be published as REST API endpoints, embedded via SDKs, or run through chat interfaces.
|
||||
|
||||
### What You Can Build with Sim
|
||||
|
||||
- **AI Assistants & Chatbots:** Intelligent agents that search the web, access calendars, send emails, and interact with business tools
|
||||
- **Business Process Automation:** Automate repetitive tasks like data entry, report generation, customer support, and content creation
|
||||
- **Data Processing & Analysis:** Extract insights from documents, analyze datasets, generate reports, and sync data between systems
|
||||
- **API Integration Workflows:** Connect multiple services into unified endpoints and orchestrate complex business logic
|
||||
- **RAG Systems:** Build sophisticated retrieval-augmented generation pipelines with custom knowledge bases
|
||||
|
||||
### Drawbacks to Consider
|
||||
|
||||
While Sim excels at AI agent workflows, there are some tradeoffs:
|
||||
|
||||
- Fewer pre-built integrations compared to n8n's extensive library—though Sim's 80+ integrations cover most AI agent use cases and MCP protocol enables custom integrations
|
||||
|
||||
## Key Differences
|
||||
|
||||
While all three platforms enable AI agent development, they excel in different areas:
|
||||
|
||||
### OpenAI AgentKit
|
||||
|
||||
**Best for:** Teams deeply invested in the OpenAI ecosystem who prioritize evaluation and testing capabilities. Ideal when you need tight integration with OpenAI's latest models and want built-in prompt optimization and evaluation tools.
|
||||
|
||||
**Limitations:** Locked into OpenAI models only, not open-source, no workflow templates or execution logs, limited triggering options, and fewer out-of-the-box integrations.
|
||||
|
||||
### n8n
|
||||
|
||||
**Best for:** Traditional workflow automation with some AI enhancement. Excellent when your primary need is connecting business tools and services, with AI as a complementary feature rather than the core focus.
|
||||
|
||||
**Limitations:** No SDK for programmatic workflow building, fair-use licensing (not fully open source), 14-day trial limitation, and AI agent capabilities are newer and less mature compared to its traditional automation features.
|
||||
|
||||
### Sim
|
||||
|
||||
**Best for:** Building production-ready AI agent workflows that require flexibility, collaboration, and comprehensive tooling. Ideal for teams that need AI Copilot assistance, advanced knowledge base features, detailed logging, and the ability to work across multiple AI providers with purpose-built agentic workflow tools.
|
||||
|
||||
**Limitations:** Fewer integrations than n8n's extensive library, though the 80+ built-in integrations and MCP protocol support cover most AI agent needs.
|
||||
|
||||
## Which Should You Choose?
|
||||
|
||||
The right platform depends on your specific needs and context:
|
||||
|
||||
Choose **OpenAI AgentKit** if you're exclusively using OpenAI models and want to build chat interfaces with the ChatKit. It's a solid choice for teams that want to stay within OpenAI's ecosystem and prioritize testing capabilities.
|
||||
|
||||
Choose **n8n** if your primary use case is traditional workflow automation between business tools, with occasional AI enhancement. It's ideal for organizations already familiar with n8n who want to add some AI capabilities to existing automations.
|
||||
|
||||
Choose **Sim** if you're building AI agents as your primary objective and need a platform purpose-built for that use case. Sim provides the most comprehensive feature set for agentic workflows, with AI Copilot to accelerate development, parallel execution handling, intelligent knowledge base for RAG applications, detailed execution logging for production monitoring, flexibility across AI providers, extensive integrations, team collaboration, and deployment options that scale from prototype to production.
|
||||
16
apps/sim/lib/blog/faq.tsx
Normal file
16
apps/sim/lib/blog/faq.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
export function FAQ({ items }: { items: { q: string; a: string }[] }) {
|
||||
if (!items || items.length === 0) return null
|
||||
return (
|
||||
<section className='mt-12'>
|
||||
<h2 className='mb-4 font-medium text-[24px]'>FAQ</h2>
|
||||
<div className='space-y-6'>
|
||||
{items.map((it, i) => (
|
||||
<div key={i}>
|
||||
<h3 className='mb-2 font-medium text-[20px]'>{it.q}</h3>
|
||||
<p className='text-[19px] text-gray-800 leading-relaxed'>{it.a}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
65
apps/sim/lib/blog/mdx.tsx
Normal file
65
apps/sim/lib/blog/mdx.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import clsx from 'clsx'
|
||||
import Image from 'next/image'
|
||||
import type { MDXRemoteProps } from 'next-mdx-remote/rsc'
|
||||
|
||||
export const mdxComponents: MDXRemoteProps['components'] = {
|
||||
img: (props: any) => (
|
||||
<Image
|
||||
src={props.src}
|
||||
alt={props.alt || ''}
|
||||
width={props.width ? Number(props.width) : 800}
|
||||
height={props.height ? Number(props.height) : 450}
|
||||
className={clsx('w-full rounded-lg', props.className)}
|
||||
/>
|
||||
),
|
||||
h2: (props: any) => (
|
||||
<h2
|
||||
{...props}
|
||||
style={{ fontSize: '30px', marginTop: '3rem', marginBottom: '1.5rem' }}
|
||||
className={clsx('font-medium text-black leading-tight', props.className)}
|
||||
/>
|
||||
),
|
||||
h3: (props: any) => (
|
||||
<h3
|
||||
{...props}
|
||||
style={{ fontSize: '24px', marginTop: '1.5rem', marginBottom: '0.75rem' }}
|
||||
className={clsx('font-medium leading-tight', props.className)}
|
||||
/>
|
||||
),
|
||||
h4: (props: any) => (
|
||||
<h4
|
||||
{...props}
|
||||
style={{ fontSize: '19px', marginTop: '1.5rem', marginBottom: '0.75rem' }}
|
||||
className={clsx('font-medium leading-tight', props.className)}
|
||||
/>
|
||||
),
|
||||
p: (props: any) => (
|
||||
<p
|
||||
{...props}
|
||||
style={{ fontSize: '19px', marginBottom: '1.5rem', fontWeight: '400' }}
|
||||
className={clsx('text-gray-800 leading-relaxed', props.className)}
|
||||
/>
|
||||
),
|
||||
ul: (props: any) => (
|
||||
<ul
|
||||
{...props}
|
||||
style={{ fontSize: '19px', marginBottom: '1rem', fontWeight: '400' }}
|
||||
className={clsx('list-outside list-disc pl-6 text-gray-800 leading-relaxed', props.className)}
|
||||
/>
|
||||
),
|
||||
ol: (props: any) => (
|
||||
<ol
|
||||
{...props}
|
||||
style={{ fontSize: '19px', marginBottom: '1rem', fontWeight: '400' }}
|
||||
className={clsx(
|
||||
'list-outside list-decimal pl-6 text-gray-800 leading-relaxed',
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
),
|
||||
li: (props: any) => <li {...props} className={clsx('mb-2', props.className)} />,
|
||||
strong: (props: any) => <strong {...props} className={clsx('font-semibold', props.className)} />,
|
||||
figure: (props: any) => (
|
||||
<figure {...props} className={clsx('my-8 overflow-hidden rounded-lg', props.className)} />
|
||||
),
|
||||
}
|
||||
158
apps/sim/lib/blog/registry.ts
Normal file
158
apps/sim/lib/blog/registry.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import matter from 'gray-matter'
|
||||
import { compileMDX } from 'next-mdx-remote/rsc'
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
|
||||
import rehypeSlug from 'rehype-slug'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { mdxComponents } from './mdx'
|
||||
import type { BlogMeta, BlogPost, TagWithCount } from './schema'
|
||||
import { AuthorSchema, BlogFrontmatterSchema } from './schema'
|
||||
import { AUTHORS_DIR, BLOG_DIR, byDateDesc, ensureContentDirs, toIsoDate } from './utils'
|
||||
|
||||
let cachedMeta: BlogMeta[] | null = null
|
||||
let cachedAuthors: Record<string, any> | null = null
|
||||
|
||||
async function loadAuthors(): Promise<Record<string, any>> {
|
||||
if (cachedAuthors) return cachedAuthors
|
||||
await ensureContentDirs()
|
||||
const files = await fs.readdir(AUTHORS_DIR).catch(() => [])
|
||||
const authors: Record<string, any> = {}
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.json')) continue
|
||||
const raw = await fs.readFile(path.join(AUTHORS_DIR, file), 'utf-8')
|
||||
const json = JSON.parse(raw)
|
||||
const author = AuthorSchema.parse(json)
|
||||
authors[author.id] = author
|
||||
}
|
||||
cachedAuthors = authors
|
||||
return authors
|
||||
}
|
||||
|
||||
function slugify(text: string): string {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9\s-]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
}
|
||||
|
||||
async function scanFrontmatters(): Promise<BlogMeta[]> {
|
||||
if (cachedMeta) return cachedMeta
|
||||
await ensureContentDirs()
|
||||
const entries = await fs.readdir(BLOG_DIR).catch(() => [])
|
||||
const authorsMap = await loadAuthors()
|
||||
const results: BlogMeta[] = []
|
||||
for (const slug of entries) {
|
||||
const postDir = path.join(BLOG_DIR, slug)
|
||||
const stat = await fs.stat(postDir).catch(() => null)
|
||||
if (!stat || !stat.isDirectory()) continue
|
||||
const mdxPath = path.join(postDir, 'index.mdx')
|
||||
const hasMdx = await fs
|
||||
.stat(mdxPath)
|
||||
.then((s) => s.isFile())
|
||||
.catch(() => false)
|
||||
if (!hasMdx) continue
|
||||
const raw = await fs.readFile(mdxPath, 'utf-8')
|
||||
const { data } = matter(raw)
|
||||
const fm = BlogFrontmatterSchema.parse(data)
|
||||
const authors = fm.authors.map((id) => authorsMap[id]).filter(Boolean)
|
||||
if (authors.length === 0) throw new Error(`Authors not found for "${slug}"`)
|
||||
results.push({
|
||||
slug: fm.slug,
|
||||
title: fm.title,
|
||||
description: fm.description,
|
||||
date: toIsoDate(fm.date),
|
||||
updated: fm.updated ? toIsoDate(fm.updated) : undefined,
|
||||
author: authors[0],
|
||||
authors,
|
||||
readingTime: fm.readingTime,
|
||||
tags: fm.tags,
|
||||
ogImage: fm.ogImage,
|
||||
canonical: fm.canonical,
|
||||
ogAlt: fm.ogAlt,
|
||||
about: fm.about,
|
||||
timeRequired: fm.timeRequired,
|
||||
faq: fm.faq,
|
||||
draft: fm.draft,
|
||||
})
|
||||
}
|
||||
cachedMeta = results.sort(byDateDesc)
|
||||
return cachedMeta
|
||||
}
|
||||
|
||||
export async function getAllPostMeta(): Promise<BlogMeta[]> {
|
||||
return (await scanFrontmatters()).filter((p) => !p.draft)
|
||||
}
|
||||
|
||||
export async function getAllTags(): Promise<TagWithCount[]> {
|
||||
const posts = await getAllPostMeta()
|
||||
const counts: Record<string, number> = {}
|
||||
for (const p of posts) {
|
||||
for (const t of p.tags) counts[t] = (counts[t] || 0) + 1
|
||||
}
|
||||
return Object.entries(counts)
|
||||
.map(([tag, count]) => ({ tag, count }))
|
||||
.sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag))
|
||||
}
|
||||
|
||||
export async function getPostBySlug(slug: string): Promise<BlogPost> {
|
||||
const meta = await scanFrontmatters()
|
||||
const found = meta.find((m) => m.slug === slug)
|
||||
if (!found) throw new Error(`Post not found: ${slug}`)
|
||||
const mdxPath = path.join(BLOG_DIR, slug, 'index.mdx')
|
||||
const raw = await fs.readFile(mdxPath, 'utf-8')
|
||||
const { content, data } = matter(raw)
|
||||
const fm = BlogFrontmatterSchema.parse(data)
|
||||
const compiled = await compileMDX({
|
||||
source: content,
|
||||
components: mdxComponents as any,
|
||||
options: {
|
||||
parseFrontmatter: false,
|
||||
mdxOptions: {
|
||||
remarkPlugins: [remarkGfm],
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[rehypeAutolinkHeadings, { behavior: 'wrap', properties: { className: 'anchor' } }],
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
const headings: { text: string; id: string }[] = []
|
||||
const lines = content.split('\n')
|
||||
for (const line of lines) {
|
||||
const match = /^##\s+(.+)$/.exec(line.trim())
|
||||
if (match) {
|
||||
const text = match[1].trim()
|
||||
headings.push({ text, id: slugify(text) })
|
||||
}
|
||||
}
|
||||
return {
|
||||
...found,
|
||||
Content: () => (compiled as any).content,
|
||||
updated: fm.updated ? toIsoDate(fm.updated) : found.updated,
|
||||
headings,
|
||||
}
|
||||
}
|
||||
|
||||
export function invalidateBlogCaches() {
|
||||
cachedMeta = null
|
||||
cachedAuthors = null
|
||||
}
|
||||
|
||||
export async function getRelatedPosts(slug: string, limit = 3): Promise<BlogMeta[]> {
|
||||
const posts = await getAllPostMeta()
|
||||
const current = posts.find((p) => p.slug === slug)
|
||||
if (!current) return []
|
||||
const scored = posts
|
||||
.filter((p) => p.slug !== slug)
|
||||
.map((p) => ({
|
||||
post: p,
|
||||
score: p.tags.filter((t) => current.tags.includes(t)).length,
|
||||
}))
|
||||
.filter((x) => x.score > 0)
|
||||
.sort((a, b) => b.score - a.score || byDateDesc(a.post, b.post))
|
||||
.slice(0, limit)
|
||||
.map((x) => x.post)
|
||||
return scored
|
||||
}
|
||||
72
apps/sim/lib/blog/schema.ts
Normal file
72
apps/sim/lib/blog/schema.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const AuthorSchema = z
|
||||
.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
url: z.string().url().optional(),
|
||||
xHandle: z.string().optional(),
|
||||
avatarUrl: z.string().optional(), // allow relative or absolute
|
||||
})
|
||||
.strict()
|
||||
|
||||
export type Author = z.infer<typeof AuthorSchema>
|
||||
|
||||
export const BlogFrontmatterSchema = z
|
||||
.object({
|
||||
slug: z.string().min(1),
|
||||
title: z.string().min(5),
|
||||
description: z.string().min(20),
|
||||
date: z.coerce.date(),
|
||||
updated: z.coerce.date().optional(),
|
||||
authors: z.array(z.string()).min(1),
|
||||
readingTime: z.number().int().positive().optional(),
|
||||
tags: z.array(z.string()).default([]),
|
||||
ogImage: z.string().min(1),
|
||||
ogAlt: z.string().optional(),
|
||||
about: z.array(z.string()).optional(),
|
||||
timeRequired: z.string().optional(),
|
||||
faq: z
|
||||
.array(
|
||||
z.object({
|
||||
q: z.string().min(1),
|
||||
a: z.string().min(1),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
canonical: z.string().url(),
|
||||
draft: z.boolean().default(false),
|
||||
})
|
||||
.strict()
|
||||
|
||||
export type BlogFrontmatter = z.infer<typeof BlogFrontmatterSchema>
|
||||
|
||||
export interface BlogMeta {
|
||||
slug: string
|
||||
title: string
|
||||
description: string
|
||||
date: string // ISO
|
||||
updated?: string // ISO
|
||||
author: Author
|
||||
authors: Author[]
|
||||
readingTime?: number
|
||||
tags: string[]
|
||||
ogImage: string
|
||||
ogAlt?: string
|
||||
about?: string[]
|
||||
timeRequired?: string
|
||||
faq?: { q: string; a: string }[]
|
||||
canonical: string
|
||||
draft: boolean
|
||||
sourcePath?: string
|
||||
}
|
||||
|
||||
export interface BlogPost extends BlogMeta {
|
||||
Content: React.ComponentType
|
||||
headings?: { text: string; id: string }[]
|
||||
}
|
||||
|
||||
export interface TagWithCount {
|
||||
tag: string
|
||||
count: number
|
||||
}
|
||||
134
apps/sim/lib/blog/seo.ts
Normal file
134
apps/sim/lib/blog/seo.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import type { Metadata } from 'next'
|
||||
import type { BlogMeta } from './schema'
|
||||
|
||||
export function buildPostMetadata(post: BlogMeta): Metadata {
|
||||
const base = new URL(post.canonical)
|
||||
const baseUrl = `${base.protocol}//${base.host}`
|
||||
return {
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
keywords: post.tags,
|
||||
authors: (post.authors && post.authors.length > 0 ? post.authors : [post.author]).map((a) => ({
|
||||
name: a.name,
|
||||
url: a.url,
|
||||
})),
|
||||
creator: post.author.name,
|
||||
publisher: 'Sim',
|
||||
robots: post.draft
|
||||
? { index: false, follow: false, googleBot: { index: false, follow: false } }
|
||||
: { index: true, follow: true, googleBot: { index: true, follow: true } },
|
||||
alternates: { canonical: post.canonical },
|
||||
openGraph: {
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
url: post.canonical,
|
||||
siteName: 'Sim',
|
||||
locale: 'en_US',
|
||||
type: 'article',
|
||||
publishedTime: post.date,
|
||||
modifiedTime: post.updated ?? post.date,
|
||||
authors: (post.authors && post.authors.length > 0 ? post.authors : [post.author]).map(
|
||||
(a) => a.name
|
||||
),
|
||||
tags: post.tags,
|
||||
images: [
|
||||
{
|
||||
url: post.ogImage.startsWith('http') ? post.ogImage : `${baseUrl}${post.ogImage}`,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: post.ogAlt || post.title,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
images: [post.ogImage],
|
||||
creator: post.author.url?.includes('x.com') ? `@${post.author.xHandle || ''}` : undefined,
|
||||
site: '@simdotai',
|
||||
},
|
||||
other: {
|
||||
'article:published_time': post.date,
|
||||
'article:modified_time': post.updated ?? post.date,
|
||||
'article:author': post.author.name,
|
||||
'article:section': 'Technology',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function buildArticleJsonLd(post: BlogMeta) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
headline: post.title,
|
||||
description: post.description,
|
||||
image: [
|
||||
{
|
||||
'@type': 'ImageObject',
|
||||
url: post.ogImage,
|
||||
caption: post.ogAlt || post.title,
|
||||
},
|
||||
],
|
||||
datePublished: post.date,
|
||||
dateModified: post.updated ?? post.date,
|
||||
author: (post.authors && post.authors.length > 0 ? post.authors : [post.author]).map((a) => ({
|
||||
'@type': 'Person',
|
||||
name: a.name,
|
||||
url: a.url,
|
||||
})),
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'Sim',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://sim.ai/logo/primary/medium.png',
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: {
|
||||
'@type': 'WebPage',
|
||||
'@id': post.canonical,
|
||||
},
|
||||
keywords: post.tags.join(', '),
|
||||
about: (post.about || []).map((a) => ({ '@type': 'Thing', name: a })),
|
||||
isAccessibleForFree: true,
|
||||
timeRequired: post.timeRequired,
|
||||
articleSection: 'Technology',
|
||||
inLanguage: 'en-US',
|
||||
}
|
||||
}
|
||||
|
||||
export function buildBreadcrumbJsonLd(post: BlogMeta) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://sim.ai' },
|
||||
{ '@type': 'ListItem', position: 2, name: 'Blog', item: 'https://sim.ai/blog' },
|
||||
{ '@type': 'ListItem', position: 3, name: post.title, item: post.canonical },
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
export function buildFaqJsonLd(items: { q: string; a: string }[] | undefined) {
|
||||
if (!items || items.length === 0) return null
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: items.map((it) => ({
|
||||
'@type': 'Question',
|
||||
name: it.q,
|
||||
acceptedAnswer: { '@type': 'Answer', text: it.a },
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
export function buildBlogJsonLd() {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Blog',
|
||||
name: 'Sim Blog',
|
||||
url: 'https://sim.ai/blog',
|
||||
description: 'Announcements, insights, and guides for building AI agent workflows.',
|
||||
}
|
||||
}
|
||||
27
apps/sim/lib/blog/utils.ts
Normal file
27
apps/sim/lib/blog/utils.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
|
||||
export const BLOG_DIR = path.join(process.cwd(), 'content', 'blog')
|
||||
export const AUTHORS_DIR = path.join(process.cwd(), 'content', 'authors')
|
||||
|
||||
export async function ensureContentDirs() {
|
||||
await fs.mkdir(BLOG_DIR, { recursive: true })
|
||||
await fs.mkdir(AUTHORS_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
export function toIsoDate(value: Date | string | number): string {
|
||||
if (value instanceof Date) return value.toISOString()
|
||||
return new Date(value).toISOString()
|
||||
}
|
||||
|
||||
export function byDateDesc<T extends { date: string }>(a: T, b: T) {
|
||||
return new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||
}
|
||||
|
||||
export function stripMdxExtension(file: string) {
|
||||
return file.replace(/\.mdx?$/i, '')
|
||||
}
|
||||
|
||||
export function isRelativeUrl(url: string) {
|
||||
return url.startsWith('/')
|
||||
}
|
||||
@@ -216,6 +216,20 @@ const nextConfig: NextConfig = {
|
||||
permanent: true,
|
||||
})
|
||||
|
||||
// Move root feeds to blog namespace
|
||||
redirects.push(
|
||||
{
|
||||
source: '/rss.xml',
|
||||
destination: '/blog/rss.xml',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/sitemap-images.xml',
|
||||
destination: '/blog/sitemap-images.xml',
|
||||
permanent: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Only enable domain redirects for the hosted version
|
||||
if (isHosted) {
|
||||
redirects.push(
|
||||
|
||||
@@ -90,12 +90,14 @@
|
||||
"mammoth": "^1.9.0",
|
||||
"mysql2": "3.14.3",
|
||||
"next": "^15.4.1",
|
||||
"next-mdx-remote": "^5.0.0",
|
||||
"next-runtime-env": "3.3.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"officeparser": "^5.2.0",
|
||||
"openai": "^4.91.1",
|
||||
"papaparse": "5.5.3",
|
||||
"pdf-parse": "2.4.5",
|
||||
"gray-matter": "^4.0.3",
|
||||
"posthog-js": "1.268.9",
|
||||
"posthog-node": "5.9.2",
|
||||
"prismjs": "^1.30.0",
|
||||
@@ -108,6 +110,8 @@
|
||||
"react-simple-code-editor": "^0.14.1",
|
||||
"reactflow": "^11.11.4",
|
||||
"remark-gfm": "4.0.1",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"resend": "^4.1.2",
|
||||
"sharp": "0.34.3",
|
||||
"socket.io": "^4.8.1",
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
44
bun.lock
44
bun.lock
@@ -113,6 +113,7 @@
|
||||
"framer-motion": "^12.5.0",
|
||||
"fuse.js": "7.1.0",
|
||||
"geist": "1.4.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"groq-sdk": "^0.15.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"input-otp": "^1.4.2",
|
||||
@@ -126,6 +127,7 @@
|
||||
"mammoth": "^1.9.0",
|
||||
"mysql2": "3.14.3",
|
||||
"next": "^15.4.1",
|
||||
"next-mdx-remote": "^5.0.0",
|
||||
"next-runtime-env": "3.3.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"officeparser": "^5.2.0",
|
||||
@@ -143,6 +145,8 @@
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-simple-code-editor": "^0.14.1",
|
||||
"reactflow": "^11.11.4",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-gfm": "4.0.1",
|
||||
"resend": "^4.1.2",
|
||||
"sharp": "0.34.3",
|
||||
@@ -656,6 +660,8 @@
|
||||
|
||||
"@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="],
|
||||
|
||||
"@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="],
|
||||
|
||||
"@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.3.1", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-6nZrq5kfAz0POWyhljnbWQQJQ5uT8oE2ddX303q1uY0tWsivWKgBDXBBvuFPwOqRRalXJuVO9EjOdVtuhLX0zg=="],
|
||||
@@ -1830,6 +1836,8 @@
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||
|
||||
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
|
||||
|
||||
"estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="],
|
||||
|
||||
"estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="],
|
||||
@@ -1872,6 +1880,8 @@
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
|
||||
|
||||
"fast-content-type-parse": ["fast-content-type-parse@2.0.1", "", {}, "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="],
|
||||
|
||||
"fast-copy": ["fast-copy@3.0.2", "", {}, "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ=="],
|
||||
@@ -1980,6 +1990,8 @@
|
||||
|
||||
"graphql": ["graphql@15.10.1", "", {}, "sha512-BL/Xd/T9baO6NFzoMpiMD7YUZ62R6viR5tp/MULVEnbYJXZA//kRNW7J0j1w/wXArgL0sCxhDfK5dczSKn3+cg=="],
|
||||
|
||||
"gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
|
||||
|
||||
"groq-sdk": ["groq-sdk@0.15.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-aYDEdr4qczx3cLCRRe+Beb37I7g/9bD5kHF+EEDxcrREWw1vKoRcfP3vHEkJB7Ud/8oOuF0scRwDpwWostTWuQ=="],
|
||||
|
||||
"gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="],
|
||||
@@ -1992,6 +2004,10 @@
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hast-util-heading-rank": ["hast-util-heading-rank@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA=="],
|
||||
|
||||
"hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="],
|
||||
|
||||
"hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="],
|
||||
|
||||
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
|
||||
@@ -2070,6 +2086,8 @@
|
||||
|
||||
"is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
|
||||
|
||||
"is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
@@ -2152,6 +2170,8 @@
|
||||
|
||||
"jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="],
|
||||
|
||||
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
|
||||
|
||||
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||
|
||||
"kysely": ["kysely@0.28.7", "", {}, "sha512-u/cAuTL4DRIiO2/g4vNGRgklEKNIj5Q3CG7RoUB5DV5SfEC2hMvPxKi0GWPmnzwL2ryIeud2VTcEEmqzTzEPNw=="],
|
||||
@@ -2424,6 +2444,8 @@
|
||||
|
||||
"next": ["next@15.4.1", "", { "dependencies": { "@next/env": "15.4.1", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.1", "@next/swc-darwin-x64": "15.4.1", "@next/swc-linux-arm64-gnu": "15.4.1", "@next/swc-linux-arm64-musl": "15.4.1", "@next/swc-linux-x64-gnu": "15.4.1", "@next/swc-linux-x64-musl": "15.4.1", "@next/swc-win32-arm64-msvc": "15.4.1", "@next/swc-win32-x64-msvc": "15.4.1", "sharp": "^0.34.3" }, "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-eNKB1q8C7o9zXF8+jgJs2CzSLIU3T6bQtX6DcTnCq1sIR1CJ0GlSyRs1BubQi3/JgCnr9Vr+rS5mOMI38FFyQw=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"next-runtime-env": ["next-runtime-env@3.3.0", "", { "dependencies": { "next": "^14", "react": "^18" } }, "sha512-JgKVnog9mNbjbjH9csVpMnz2tB2cT5sLF+7O47i6Ze/s/GoiKdV7dHhJHk1gwXpo6h5qPj5PTzryldtSjvrHuQ=="],
|
||||
|
||||
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
|
||||
@@ -2692,8 +2714,12 @@
|
||||
|
||||
"regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
|
||||
|
||||
"rehype-autolink-headings": ["rehype-autolink-headings@7.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-is-element": "^3.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw=="],
|
||||
|
||||
"rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="],
|
||||
|
||||
"rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="],
|
||||
|
||||
"remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="],
|
||||
|
||||
"remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
|
||||
@@ -2756,6 +2782,8 @@
|
||||
|
||||
"scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="],
|
||||
|
||||
"section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
|
||||
|
||||
"secure-json-parse": ["secure-json-parse@4.0.0", "", {}, "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA=="],
|
||||
|
||||
"selderee": ["selderee@0.11.0", "", { "dependencies": { "parseley": "^0.12.0" } }, "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA=="],
|
||||
@@ -2872,6 +2900,8 @@
|
||||
|
||||
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
|
||||
|
||||
"strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="],
|
||||
|
||||
"strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
|
||||
|
||||
"strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="],
|
||||
@@ -3026,6 +3056,8 @@
|
||||
|
||||
"unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="],
|
||||
|
||||
"unist-util-remove": ["unist-util-remove@3.1.1", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.0.0" } }, "sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw=="],
|
||||
|
||||
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
|
||||
|
||||
"unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
@@ -3058,6 +3090,8 @@
|
||||
|
||||
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
|
||||
"vfile-matter": ["vfile-matter@5.0.1", "", { "dependencies": { "vfile": "^6.0.0", "yaml": "^2.0.0" } }, "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw=="],
|
||||
|
||||
"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.1.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-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
|
||||
@@ -3440,6 +3474,8 @@
|
||||
|
||||
"google-auth-library/jws": ["jws@4.0.0", "", { "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg=="],
|
||||
|
||||
"gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
||||
|
||||
"groq-sdk/@types/node": ["@types/node@18.19.128", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-m7wxXGpPpqxp2QDi/rpih5O772APRuBIa/6XiGqLNoM1txkjI8Sz1V4oSXJxQLTz/yP5mgy9z6UXEO6/lP70Gg=="],
|
||||
|
||||
"groq-sdk/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||
@@ -3594,6 +3630,12 @@
|
||||
|
||||
"unicode-trie/pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="],
|
||||
|
||||
"unist-util-remove/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||
|
||||
"unist-util-remove/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
|
||||
|
||||
"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-tsconfig-paths/tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
||||
|
||||
"vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
||||
@@ -3732,6 +3774,8 @@
|
||||
|
||||
"google-auth-library/jws/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
|
||||
|
||||
"groq-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"groq-sdk/node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||
|
||||
Reference in New Issue
Block a user