Compare commits

..

2 Commits

Author SHA1 Message Date
Emir Karabeg
e68ec1065c feat(landing): structure 2026-02-09 20:15:01 -08:00
Waleed
622d0cad22 Merge pull request #3172 from simstudioai/fix/notifs
fix(notifications): throw notification on runtime errors, move predeploy checks to update in deploy modal
2026-02-09 11:49:58 -08:00
19 changed files with 631 additions and 106 deletions

View File

@@ -1,38 +0,0 @@
# Sim Cloud Agent Guide
## Project Overview
Sim is an AI agent workflow builder. Turborepo monorepo with Bun workspaces.
### Services
| Service | Port | Command |
|---------|------|---------|
| Next.js App | 3000 | `bun run dev` (from root) |
| Realtime Socket Server | 3002 | `cd apps/sim && bun run dev:sockets` |
| Both together | 3000+3002 | `bun run dev:full` (from root) |
| Docs site | 3001 | `cd apps/docs && bun run dev` |
| PostgreSQL (pgvector) | 5432 | Docker container `simstudio-db` |
## Common Commands
- **Lint**: `bun run lint:check` (read-only) or `bun run lint` (auto-fix)
- **Format**: `bun run format:check` (read-only) or `bun run format` (auto-fix)
- **Test**: `bun run test` (all packages via turborepo)
- **Test single app**: `cd apps/sim && bunx vitest run`
- **Type check**: `bun run type-check`
- **Dev**: `bun run dev:full` (Next.js app + realtime socket server)
- **DB migrations**: `cd packages/db && bunx drizzle-kit migrate --config=./drizzle.config.ts`
## Architecture Notes
- Package manager is **bun** (not npm/npx). Use `bun` and `bunx`.
- Linter/formatter is **Biome** (not ESLint/Prettier).
- Testing framework is **Vitest** with `@sim/testing` for shared mocks/factories.
- Database uses **Drizzle ORM** with PostgreSQL + pgvector.
- Auth is **Better Auth** (session cookies).
- Pre-commit hook runs `bunx lint-staged` which applies `biome check --write`.
- `.npmrc` has `ignore-scripts=true`.
- Docs app requires `fumadocs-mdx` generation before type-check (`bunx fumadocs-mdx` in `apps/docs/`).
- Coding guidelines are in `CLAUDE.md` (root) and `.cursor/rules/*.mdc`.
- See `.github/CONTRIBUTING.md` for contribution workflow details.

View File

@@ -0,0 +1,16 @@
/**
* Collaboration section — team workflows and real-time collaboration.
*
* SEO:
* - `<section id="collaboration" aria-labelledby="collaboration-heading">`.
* - `<h2 id="collaboration-heading">` for the section title.
* - Product visuals use `<figure>` with `<figcaption>` and descriptive `alt` text.
*
* GEO:
* - Name specific capabilities (version control, shared workspaces, RBAC, audit logs).
* - Lead with a summary so AI can answer "Does Sim support team collaboration?".
* - Reference "Sim" by name per capability ("Sim's real-time collaboration").
*/
export default function Collaboration() {
return null
}

View File

@@ -0,0 +1,17 @@
/**
* Enterprise section — compliance, scale, and security messaging.
*
* SEO:
* - `<section id="enterprise" aria-labelledby="enterprise-heading">`.
* - `<h2 id="enterprise-heading">` for the section title.
* - Compliance certs (SOC2, HIPAA) as visible `<strong>` text.
* - Enterprise CTA links to contact form via `<a>` with `rel="noopener noreferrer"`.
*
* GEO:
* - Entity-rich: "Sim is SOC2 and HIPAA compliant" — not "We are compliant."
* - `<ul>` checklist of features (SSO, RBAC, audit logs, SLA, on-premise deployment)
* as an atomic answer block for "What enterprise features does Sim offer?".
*/
export default function Enterprise() {
return null
}

View File

@@ -0,0 +1,17 @@
/**
* Features section — Sim's core product capabilities.
*
* SEO:
* - `<section id="features" aria-labelledby="features-heading">`.
* - `<h2 id="features-heading">` for the section title.
* - Each feature: `<h3>` + `<p>` + `<ul>` capability list. Strict H2 -> H3 -> H4 hierarchy.
* - Feature lists map to `WebApplication.featureList` in structured-data.tsx — keep in sync.
*
* GEO:
* - Each feature block is independently extractable (atomic answer block pattern).
* - Include specific numbers ("100+ integrations", "15+ AI providers", "SOC2 compliant").
* - Always use "Sim" by name — never "the platform" or "our tool" (entity consistency).
*/
export default function Features() {
return null
}

View File

@@ -0,0 +1,18 @@
/**
* Landing page footer — navigation, legal links, and entity reinforcement.
*
* SEO:
* - `<footer role="contentinfo">` with `<nav aria-label="Footer navigation">`.
* - Link groups under semantic headings (`<h3>`). All links are `<Link>` or `<a>` with `href`.
* - External links include `rel="noopener noreferrer"`.
* - Legal links (Privacy, Terms) must be crawlable (trust signals).
*
* GEO:
* - Include "Sim — Open-source AI agent workflow builder" as visible text (entity reinforcement).
* - Social links (X, GitHub, LinkedIn, Discord) must match `sameAs` in structured-data.tsx.
* - Link to all major pages: Docs, Pricing, Enterprise, Careers, Changelog (internal link graph).
* - Display compliance badges (SOC2, HIPAA) and status page link as visible trust signals.
*/
export default function Footer() {
return null
}

View File

@@ -0,0 +1,18 @@
/**
* Hero section — above-the-fold value proposition.
*
* SEO:
* - `<section id="hero" aria-labelledby="hero-heading">`.
* - Contains the page's only `<h1>`. Text aligns with the `<title>` tag keyword.
* - Subtitle `<p>` expands the H1 into a full sentence with the primary keyword.
* - Primary CTA is a `<Link href="/signup">` (crawlable), not a `<button>`.
* - Canvas/animations wrapped in `aria-hidden="true"` with a text alternative.
*
* GEO:
* - H1 + subtitle answer "What is Sim?" in two sentences (answer-first pattern).
* - First 150 chars of visible text explicitly name "Sim", "AI agent", "workflow builder".
* - Include a `<p className="sr-only">` product summary (60-80 words) rich in entities.
*/
export default function Hero() {
return null
}

View File

@@ -0,0 +1,23 @@
import Collaboration from '@/app/(home)/components/collaboration/collaboration'
import Enterprise from '@/app/(home)/components/enterprise/enterprise'
import Features from '@/app/(home)/components/features/features'
import Footer from '@/app/(home)/components/footer/footer'
import Hero from '@/app/(home)/components/hero/hero'
import Navbar from '@/app/(home)/components/navbar/navbar'
import Pricing from '@/app/(home)/components/pricing/pricing'
import StructuredData from '@/app/(home)/components/structured-data'
import Templates from '@/app/(home)/components/templates/templates'
import Testimonials from '@/app/(home)/components/testimonials/testimonials'
export {
Collaboration,
Enterprise,
Features,
Footer,
Hero,
Navbar,
Pricing,
StructuredData,
Templates,
Testimonials,
}

View File

@@ -0,0 +1,17 @@
/**
* Landing page navigation bar.
*
* SEO:
* - `<nav aria-label="Primary navigation">` with `itemScope itemType="https://schema.org/SiteNavigationElement"`.
* - All routes use `<Link>` (crawlable). External links include `rel="noopener noreferrer"`.
* - Logo link contains `<span className="sr-only">` with the brand name.
* - Logo `<Image>` uses `priority` and `loading="eager"` (LCP element).
*
* GEO:
* - "Sim" as the canonical entity name everywhere.
* - Navigation items in semantic `<ul>/<li>` with descriptive anchor text.
* - Descriptive `aria-label` on interactive elements for AI intent extraction.
*/
export default function Navbar() {
return null
}

View File

@@ -0,0 +1,17 @@
/**
* Pricing section — tiered pricing plans with feature comparison.
*
* SEO:
* - `<section id="pricing" aria-labelledby="pricing-heading">`.
* - `<h2 id="pricing-heading">` for the section title.
* - Each tier: `<h3>` plan name + semantic `<ul>` feature list.
* - Free tier CTA uses `<Link href="/signup">` (crawlable). Enterprise CTA uses `<a>`.
*
* GEO:
* - Each plan has consistent structure: name, price, billing period, feature list.
* - Lead with a summary: "Sim offers a free Community plan, $20/mo Pro, $40/mo Team, custom Enterprise."
* - Prices must match the `Offer` items in structured-data.tsx exactly.
*/
export default function Pricing() {
return null
}

View File

@@ -0,0 +1,222 @@
/**
* JSON-LD structured data for the landing page.
*
* Renders a `<script type="application/ld+json">` with Schema.org markup.
* Single source of truth for machine-readable page metadata.
*
* Schemas: Organization, WebSite, WebPage, BreadcrumbList, WebApplication, FAQPage.
*
* AI crawler behavior (2025-2026):
* - Google AI Overviews / Bing Copilot parse JSON-LD from their search indexes.
* - GPTBot indexes JSON-LD during crawling (92% of LLM crawlers parse JSON-LD first).
* - Perplexity / Claude prioritize visible HTML over JSON-LD during direct fetch.
* - All claims here must also appear as visible text on the page.
*
* Maintenance:
* - Offer prices must match the Pricing component exactly.
* - `sameAs` links must match the Footer social links.
* - Do not add `aggregateRating` without real, verifiable review data.
*/
export default function StructuredData() {
const structuredData = {
'@context': 'https://schema.org',
'@graph': [
{
'@type': 'Organization',
'@id': 'https://sim.ai/#organization',
name: 'Sim',
alternateName: 'Sim Studio',
description:
'Open-source AI agent workflow builder used by developers at trail-blazing startups to Fortune 500 companies',
url: 'https://sim.ai',
logo: {
'@type': 'ImageObject',
'@id': 'https://sim.ai/#logo',
url: 'https://sim.ai/logo/b&w/text/b&w.svg',
contentUrl: 'https://sim.ai/logo/b&w/text/b&w.svg',
width: 49.78314,
height: 24.276,
caption: 'Sim Logo',
},
image: { '@id': 'https://sim.ai/#logo' },
sameAs: [
'https://x.com/simdotai',
'https://github.com/simstudioai/sim',
'https://www.linkedin.com/company/simstudioai/',
'https://discord.gg/Hr4UWYEcTT',
],
contactPoint: {
'@type': 'ContactPoint',
contactType: 'customer support',
availableLanguage: ['en'],
},
},
{
'@type': 'WebSite',
'@id': 'https://sim.ai/#website',
url: 'https://sim.ai',
name: 'Sim — AI Agent Workflow Builder',
description:
'Open-source AI agent workflow builder. 60,000+ developers build and deploy agentic workflows. SOC2 and HIPAA compliant.',
publisher: { '@id': 'https://sim.ai/#organization' },
inLanguage: 'en-US',
},
{
'@type': 'WebPage',
'@id': 'https://sim.ai/#webpage',
url: 'https://sim.ai',
name: 'Sim — Workflows for LLMs | Build AI Agent Workflows',
isPartOf: { '@id': 'https://sim.ai/#website' },
about: { '@id': 'https://sim.ai/#software' },
datePublished: '2024-01-01T00:00:00+00:00',
dateModified: new Date().toISOString(),
description:
'Build and deploy AI agent workflows with Sim. Visual drag-and-drop interface for creating powerful LLM-powered automations.',
breadcrumb: { '@id': 'https://sim.ai/#breadcrumb' },
inLanguage: 'en-US',
potentialAction: [{ '@type': 'ReadAction', target: ['https://sim.ai'] }],
},
{
'@type': 'BreadcrumbList',
'@id': 'https://sim.ai/#breadcrumb',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://sim.ai' },
],
},
{
'@type': 'WebApplication',
'@id': 'https://sim.ai/#software',
name: 'Sim — AI Agent Workflow Builder',
description:
'Open-source AI agent workflow builder used by 60,000+ developers. Build agentic workflows with visual drag-and-drop interface. SOC2 and HIPAA compliant. Integrate with 100+ apps.',
applicationCategory: 'DeveloperApplication',
operatingSystem: 'Web',
browserRequirements: 'Requires a modern browser with JavaScript enabled',
offers: [
{
'@type': 'Offer',
name: 'Community Plan',
price: '0',
priceCurrency: 'USD',
availability: 'https://schema.org/InStock',
},
{
'@type': 'Offer',
name: 'Pro Plan',
price: '20',
priceCurrency: 'USD',
priceSpecification: {
'@type': 'UnitPriceSpecification',
price: '20',
priceCurrency: 'USD',
unitText: 'MONTH',
billingIncrement: 1,
},
availability: 'https://schema.org/InStock',
},
{
'@type': 'Offer',
name: 'Team Plan',
price: '40',
priceCurrency: 'USD',
priceSpecification: {
'@type': 'UnitPriceSpecification',
price: '40',
priceCurrency: 'USD',
unitText: 'MONTH',
billingIncrement: 1,
},
availability: 'https://schema.org/InStock',
},
],
featureList: [
'Visual workflow builder',
'Drag-and-drop interface',
'100+ integrations',
'AI model support (OpenAI, Anthropic, Google, xAI, Mistral, Perplexity)',
'Real-time collaboration',
'Version control',
'API access',
'Custom functions',
'Scheduled workflows',
'Event triggers',
],
review: [
{
'@type': 'Review',
author: { '@type': 'Person', name: 'Hasan Toor' },
reviewBody:
'This startup just dropped the fastest way to build AI agents. This Figma-like canvas to build agents will blow your mind.',
url: 'https://x.com/hasantoxr/status/1912909502036525271',
},
{
'@type': 'Review',
author: { '@type': 'Person', name: 'nizzy' },
reviewBody:
'This is the zapier of agent building. I always believed that building agents and using AI should not be limited to technical people. I think this solves just that.',
url: 'https://x.com/nizzyabi/status/1907864421227180368',
},
{
'@type': 'Review',
author: { '@type': 'Organization', name: 'xyflow' },
reviewBody: 'A very good looking agent workflow builder and open source!',
url: 'https://x.com/xyflowdev/status/1909501499719438670',
},
],
},
{
'@type': 'FAQPage',
'@id': 'https://sim.ai/#faq',
mainEntity: [
{
'@type': 'Question',
name: 'What is Sim?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Sim is an open-source AI agent workflow builder used by 60,000+ developers at trail-blazing startups to Fortune 500 companies. It provides a visual drag-and-drop interface for building and deploying agentic workflows. Sim is SOC2 and HIPAA compliant.',
},
},
{
'@type': 'Question',
name: 'Which AI models does Sim support?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Sim supports all major AI models including OpenAI (GPT-5, GPT-4o), Anthropic (Claude), Google (Gemini), xAI (Grok), Mistral, Perplexity, and many more. You can also connect to open-source models via Ollama.',
},
},
{
'@type': 'Question',
name: 'How much does Sim cost?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Sim offers a free Community plan with $20 usage limit, a Pro plan at $20/month, a Team plan at $40/month, and custom Enterprise pricing. All plans include CLI/SDK access.',
},
},
{
'@type': 'Question',
name: 'Do I need coding skills to use Sim?',
acceptedAnswer: {
'@type': 'Answer',
text: 'No coding skills are required. Sim features a visual drag-and-drop interface that makes it easy to build AI workflows. However, developers can also use custom functions and the API for advanced use cases.',
},
},
{
'@type': 'Question',
name: 'What enterprise features does Sim offer?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Sim offers SOC2 and HIPAA compliance, SSO/SAML authentication, role-based access control, audit logs, dedicated support, custom SLAs, and on-premise deployment options for enterprise customers.',
},
},
],
},
],
}
return (
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
)
}

View File

@@ -0,0 +1,16 @@
/**
* Templates section — pre-built workflow templates and use cases.
*
* SEO:
* - `<section id="templates" aria-labelledby="templates-heading">`.
* - `<h2 id="templates-heading">` for the section title.
* - Each template: `<article>` with `<h3>`, `<p>` description, `<Link>` to signup.
*
* GEO:
* - Each card is an atomic answer block: `<h3>Name</h3><p>1-2 sentences</p><ul>Features</ul>`.
* - Name specific integrations (Slack, Gmail, Pinecone) and AI models (GPT-5, Claude).
* - Add `id` anchors per card (e.g., `id="template-slack-summarizer"`) for fragment citations.
*/
export default function Templates() {
return null
}

View File

@@ -0,0 +1,18 @@
/**
* Testimonials section — social proof via user quotes.
*
* SEO:
* - `<section id="testimonials" aria-labelledby="testimonials-heading">`.
* - `<h2 id="testimonials-heading">` for the section title.
* - Each testimonial: `<blockquote cite="tweet-url">` with `<footer><cite>Author</cite></footer>`.
* - Profile images use `loading="lazy"` (below the fold).
*
* GEO:
* - Keep quote text as plain text in `<blockquote>` — not split across `<span>` elements.
* - Include full author name + handle (LLMs weigh attributed quotes higher).
* - Testimonials mentioning "Sim" by name carry more citation weight.
* - Review data here aligns with `review` entries in structured-data.tsx.
*/
export default function Testimonials() {
return null
}

View File

@@ -0,0 +1,51 @@
import {
Collaboration,
Enterprise,
Features,
Footer,
Hero,
Navbar,
Pricing,
StructuredData,
Templates,
Testimonials,
} from '@/app/(home)/components'
/**
* Landing page root component.
*
* ## SEO Architecture
* - Single `<h1>` inside Hero (only one per page).
* - Heading hierarchy: H1 (Hero) -> H2 (each section) -> H3 (sub-items).
* - Semantic landmarks: `<header>`, `<main>`, `<footer>`.
* - Every `<section>` has an `id` for anchor linking and `aria-labelledby` for accessibility.
* - `StructuredData` emits JSON-LD before any visible content.
*
* ## GEO Architecture
* - Above-fold content (Navbar, Hero) is statically rendered (Server Components where possible)
* for immediate availability to AI crawlers.
* - Section `id` attributes serve as fragment anchors for precise AI citations.
* - Content ordering prioritizes answer-first patterns: definition (Hero) ->
* examples (Templates) -> capabilities (Features) -> social proof (Collaboration, Testimonials) ->
* pricing (Pricing) -> enterprise (Enterprise).
*/
export default function Landing() {
return (
<>
<StructuredData />
<header>
<Navbar />
</header>
<main>
<Hero />
<Templates />
<Features />
<Collaboration />
<Pricing />
<Enterprise />
<Testimonials />
</main>
<Footer />
</>
)
}

View File

@@ -0,0 +1,87 @@
import type { Metadata } from 'next'
import { martianMono } from '@/app/_styles/fonts/martian-mono/martian-mono'
import { season } from '@/app/_styles/fonts/season/season'
/**
* SEO metadata for the landing page.
*
* - `title` contains the primary keyword and brand name.
* - `description` is 150-160 chars, front-loaded with the value proposition.
* - `openGraph` provides social sharing metadata with a dedicated OG image.
* - `robots` ensures the landing page is fully indexable.
* - `alternates.canonical` prevents duplicate content issues.
*
* GEO note: The description is written as a direct answer to "What is Sim?"
* so LLMs can extract it as a cited definition.
*/
export const metadata: Metadata = {
metadataBase: new URL('https://sim.ai'),
title: 'Sim — Workflows for LLMs | Build AI Agent Workflows',
description:
'Sim is an open-source AI agent workflow builder. Build and deploy agentic workflows with a visual drag-and-drop interface. 100+ integrations. SOC2 and HIPAA compliant.',
manifest: '/manifest.json',
icons: {
icon: '/favicon.ico',
apple: '/apple-icon.png',
},
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://sim.ai',
siteName: 'Sim',
title: 'Sim — Workflows for LLMs | Build AI Agent Workflows',
description:
'Open-source AI agent workflow builder used by 60,000+ developers. Visual drag-and-drop interface for building agentic workflows.',
images: [
{
url: '/og-image.png',
width: 1200,
height: 630,
alt: 'Sim — AI Agent Workflow Builder',
},
],
},
twitter: {
card: 'summary_large_image',
site: '@simdotai',
creator: '@simdotai',
title: 'Sim — Workflows for LLMs',
description:
'Open-source AI agent workflow builder. Build and deploy agentic workflows with a visual drag-and-drop interface.',
images: ['/og-image.png'],
},
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
alternates: {
canonical: 'https://sim.ai',
},
other: {
'msapplication-TileColor': '#000000',
'theme-color': '#ffffff',
},
}
/**
* Landing page layout.
*
* Applies landing-specific font CSS variables to the subtree:
* - `--font-season` (Season Sans): Headings and display text
* - `--font-martian-mono` (Martian Mono): Code snippets and technical accents
*
* These CSS variables are available to all child components via Tailwind classes
* (`font-season`, `font-martian-mono`) or direct `var()` usage.
*
* The layout is a Server Component — no `'use client'` needed.
*/
export default function HomeLayout({ children }: { children: React.ReactNode }) {
return <div className={`${season.variable} ${martianMono.variable}`}>{children}</div>
}

View File

@@ -0,0 +1,14 @@
import { Martian_Mono } from 'next/font/google'
/**
* Martian Mono font configuration
* Monospaced variable font used for code snippets, technical content, and accent text
* on the landing page. Supports weights 100-800.
*/
export const martianMono = Martian_Mono({
subsets: ['latin'],
display: 'swap',
variable: '--font-martian-mono',
weight: 'variable',
fallback: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'monospace'],
})

View File

@@ -19,6 +19,7 @@ import {
import { getBaseUrl } from '@/lib/core/utils/urls'
import { getInputFormatExample as getInputFormatExampleUtil } from '@/lib/workflows/operations/deployment-utils'
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
import { runPreDeployChecks } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-predeploy-checks'
import { CreateApiKeyModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/api-keys/components'
import { startsWithUuid } from '@/executor/constants'
import { useA2AAgentByWorkflow } from '@/hooks/queries/a2a/agents'
@@ -38,6 +39,7 @@ import { useWorkspaceSettings } from '@/hooks/queries/workspace'
import { usePermissionConfig } from '@/hooks/use-permission-config'
import { useSettingsModalStore } from '@/stores/modals/settings/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { mergeSubblockState } from '@/stores/workflows/utils'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import type { WorkflowState } from '@/stores/workflows/workflow/types'
import { A2aDeploy } from './components/a2a/a2a'
@@ -335,6 +337,20 @@ export function DeployModal({
setDeployError(null)
setDeployWarnings([])
const { blocks, edges, loops, parallels } = useWorkflowStore.getState()
const liveBlocks = mergeSubblockState(blocks, workflowId)
const checkResult = runPreDeployChecks({
blocks: liveBlocks,
edges,
loops,
parallels,
workflowId,
})
if (!checkResult.passed) {
setDeployError(checkResult.error || 'Pre-deploy validation failed')
return
}
try {
const result = await deployMutation.mutateAsync({ workflowId, deployChatEnabled: false })
if (result.warnings && result.warnings.length > 0) {

View File

@@ -2,9 +2,6 @@ import { useCallback, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useNotificationStore } from '@/stores/notifications'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { mergeSubblockState } from '@/stores/workflows/utils'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import { runPreDeployChecks } from './use-predeploy-checks'
const logger = createLogger('useDeployment')
@@ -25,36 +22,16 @@ export function useDeployment({
const [isDeploying, setIsDeploying] = useState(false)
const setDeploymentStatus = useWorkflowRegistry((state) => state.setDeploymentStatus)
const addNotification = useNotificationStore((state) => state.addNotification)
const blocks = useWorkflowStore((state) => state.blocks)
const edges = useWorkflowStore((state) => state.edges)
const loops = useWorkflowStore((state) => state.loops)
const parallels = useWorkflowStore((state) => state.parallels)
/**
* Handle deploy button click
* First deploy: calls API to deploy, then opens modal on success
* Redeploy: validates client-side, then opens modal if valid
* Already deployed: opens modal directly (validation happens on Update in modal)
*/
const handleDeployClick = useCallback(async () => {
if (!workflowId) return { success: false, shouldOpenModal: false }
if (isDeployed) {
const liveBlocks = mergeSubblockState(blocks, workflowId)
const checkResult = runPreDeployChecks({
blocks: liveBlocks,
edges,
loops,
parallels,
workflowId,
})
if (!checkResult.passed) {
addNotification({
level: 'error',
message: checkResult.error || 'Pre-deploy validation failed',
workflowId,
})
return { success: false, shouldOpenModal: false }
}
return { success: true, shouldOpenModal: true }
}
@@ -101,17 +78,7 @@ export function useDeployment({
} finally {
setIsDeploying(false)
}
}, [
workflowId,
isDeployed,
blocks,
edges,
loops,
parallels,
refetchDeployedState,
setDeploymentStatus,
addNotification,
])
}, [workflowId, isDeployed, refetchDeployedState, setDeploymentStatus, addNotification])
return {
isDeploying,

View File

@@ -1,7 +1,7 @@
/**
* Environment utility functions for consistent environment detection across the application
*/
import { env, getEnv, isFalsy, isTruthy } from './env'
import { env, isFalsy, isTruthy } from './env'
/**
* Is the application running in production mode
@@ -21,9 +21,10 @@ export const isTest = env.NODE_ENV === 'test'
/**
* Is this the hosted version of the application
*/
export const isHosted =
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.sim.ai' ||
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.staging.sim.ai'
// export const isHosted =
// getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.sim.ai' ||
// getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.staging.sim.ai'
export const isHosted = true
/**
* Is billing enforcement enabled

View File

@@ -62,6 +62,45 @@ const shouldSkipEntry = (output: any): boolean => {
return false
}
interface NotifyBlockErrorParams {
error: unknown
blockName: string
workflowId?: string
logContext: Record<string, unknown>
}
/**
* Sends an error notification for a block failure if error notifications are enabled.
*/
const notifyBlockError = ({ error, blockName, workflowId, logContext }: NotifyBlockErrorParams) => {
const settings = getQueryClient().getQueryData<GeneralSettings>(generalSettingsKeys.settings())
const isErrorNotificationsEnabled = settings?.errorNotificationsEnabled ?? true
if (!isErrorNotificationsEnabled) return
try {
const errorMessage = String(error)
const displayName = blockName || 'Unknown Block'
const displayMessage = `${displayName}: ${errorMessage}`
const copilotMessage = `${errorMessage}\n\nError in ${displayName}.\n\nPlease fix this.`
useNotificationStore.getState().addNotification({
level: 'error',
message: displayMessage,
workflowId,
action: {
type: 'copilot',
message: copilotMessage,
},
})
} catch (notificationError) {
logger.error('Failed to create block error notification', {
...logContext,
error: notificationError,
})
}
}
export const useTerminalConsoleStore = create<ConsoleStore>()(
devtools(
persist(
@@ -154,35 +193,12 @@ export const useTerminalConsoleStore = create<ConsoleStore>()(
const newEntry = get().entries[0]
if (newEntry?.error) {
const settings = getQueryClient().getQueryData<GeneralSettings>(
generalSettingsKeys.settings()
)
const isErrorNotificationsEnabled = settings?.errorNotificationsEnabled ?? true
if (isErrorNotificationsEnabled) {
try {
const errorMessage = String(newEntry.error)
const blockName = newEntry.blockName || 'Unknown Block'
const displayMessage = `${blockName}: ${errorMessage}`
const copilotMessage = `${errorMessage}\n\nError in ${blockName}.\n\nPlease fix this.`
useNotificationStore.getState().addNotification({
level: 'error',
message: displayMessage,
workflowId: entry.workflowId,
action: {
type: 'copilot',
message: copilotMessage,
},
})
} catch (notificationError) {
logger.error('Failed to create block error notification', {
entryId: newEntry.id,
error: notificationError,
})
}
}
notifyBlockError({
error: newEntry.error,
blockName: newEntry.blockName || 'Unknown Block',
workflowId: entry.workflowId,
logContext: { entryId: newEntry.id },
})
}
return newEntry
@@ -376,6 +392,18 @@ export const useTerminalConsoleStore = create<ConsoleStore>()(
return { entries: updatedEntries }
})
if (typeof update === 'object' && update.error) {
const matchingEntry = get().entries.find(
(e) => e.blockId === blockId && e.executionId === executionId
)
notifyBlockError({
error: update.error,
blockName: matchingEntry?.blockName || 'Unknown Block',
workflowId: matchingEntry?.workflowId,
logContext: { blockId },
})
}
},
cancelRunningEntries: (workflowId: string) => {