Skip to content

Commit 0cb92c1

Browse files
committed
Merge branch 'staging' into improvement/copilot-6
2 parents b433205 + 506d382 commit 0cb92c1

File tree

218 files changed

+15663
-1082
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

218 files changed

+15663
-1082
lines changed

.claude/commands/add-block.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ When the user asks you to create a block:
1919
```typescript
2020
import { {ServiceName}Icon } from '@/components/icons'
2121
import type { BlockConfig } from '@/blocks/types'
22-
import { AuthMode } from '@/blocks/types'
22+
import { AuthMode, IntegrationType } from '@/blocks/types'
2323
import { getScopesForService } from '@/lib/oauth/utils'
2424

2525
export const {ServiceName}Block: BlockConfig = {
@@ -29,6 +29,8 @@ export const {ServiceName}Block: BlockConfig = {
2929
longDescription: 'Detailed description for docs',
3030
docsLink: 'https://docs.sim.ai/tools/{service}',
3131
category: 'tools', // 'tools' | 'blocks' | 'triggers'
32+
integrationType: IntegrationType.X, // Primary category (see IntegrationType enum)
33+
tags: ['oauth', 'api'], // Cross-cutting tags (see IntegrationTag type)
3234
bgColor: '#HEXCOLOR', // Brand color
3335
icon: {ServiceName}Icon,
3436

@@ -629,7 +631,7 @@ export const registry: Record<string, BlockConfig> = {
629631
```typescript
630632
import { ServiceIcon } from '@/components/icons'
631633
import type { BlockConfig } from '@/blocks/types'
632-
import { AuthMode } from '@/blocks/types'
634+
import { AuthMode, IntegrationType } from '@/blocks/types'
633635
import { getScopesForService } from '@/lib/oauth/utils'
634636

635637
export const ServiceBlock: BlockConfig = {
@@ -639,6 +641,8 @@ export const ServiceBlock: BlockConfig = {
639641
longDescription: 'Full description for documentation...',
640642
docsLink: 'https://docs.sim.ai/tools/service',
641643
category: 'tools',
644+
integrationType: IntegrationType.DeveloperTools,
645+
tags: ['oauth', 'api'],
642646
bgColor: '#FF6B6B',
643647
icon: ServiceIcon,
644648
authMode: AuthMode.OAuth,
@@ -796,6 +800,8 @@ All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MU
796800

797801
## Checklist Before Finishing
798802

803+
- [ ] `integrationType` is set to the correct `IntegrationType` enum value
804+
- [ ] `tags` array includes all applicable `IntegrationTag` values
799805
- [ ] All subBlocks have `id`, `title` (except switch), and `type`
800806
- [ ] Conditions use correct syntax (field, value, not, and)
801807
- [ ] DependsOn set for fields that need other values

.claude/commands/add-integration.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export const {service}{Action}Tool: ToolConfig<Params, Response> = {
113113
```typescript
114114
import { {Service}Icon } from '@/components/icons'
115115
import type { BlockConfig } from '@/blocks/types'
116-
import { AuthMode } from '@/blocks/types'
116+
import { AuthMode, IntegrationType } from '@/blocks/types'
117117
import { getScopesForService } from '@/lib/oauth/utils'
118118

119119
export const {Service}Block: BlockConfig = {
@@ -123,6 +123,8 @@ export const {Service}Block: BlockConfig = {
123123
longDescription: '...',
124124
docsLink: 'https://docs.sim.ai/tools/{service}',
125125
category: 'tools',
126+
integrationType: IntegrationType.X, // Primary category (see IntegrationType enum)
127+
tags: ['oauth', 'api'], // Cross-cutting tags (see IntegrationTag type)
126128
bgColor: '#HEXCOLOR',
127129
icon: {Service}Icon,
128130
authMode: AuthMode.OAuth, // or AuthMode.ApiKey
@@ -410,6 +412,8 @@ If creating V2 versions (API-aligned outputs):
410412

411413
### Block
412414
- [ ] Created `blocks/blocks/{service}.ts`
415+
- [ ] Set `integrationType` to the correct `IntegrationType` enum value
416+
- [ ] Set `tags` array with all applicable `IntegrationTag` values
413417
- [ ] Defined operation dropdown with all operations
414418
- [ ] Added credential field with `requiredScopes: getScopesForService('{service}')`
415419
- [ ] Added conditional fields per operation

apps/sim/app/(auth)/login/login-form.tsx

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

3-
import { useEffect, useRef, useState } from 'react'
3+
import { useMemo, useRef, useState } from 'react'
4+
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'
45
import { createLogger } from '@sim/logger'
56
import { Eye, EyeOff } from 'lucide-react'
67
import Link from 'next/link'
@@ -86,6 +87,9 @@ export default function LoginPage({
8687
const [password, setPassword] = useState('')
8788
const [passwordErrors, setPasswordErrors] = useState<string[]>([])
8889
const [showValidationError, setShowValidationError] = useState(false)
90+
const [formError, setFormError] = useState<string | null>(null)
91+
const turnstileRef = useRef<TurnstileInstance>(null)
92+
const turnstileSiteKey = useMemo(() => getEnv('NEXT_PUBLIC_TURNSTILE_SITE_KEY'), [])
8993
const buttonClass = useBrandedButtonClass()
9094

9195
const callbackUrlParam = searchParams?.get('callbackUrl')
@@ -115,19 +119,6 @@ export default function LoginPage({
115119
: null
116120
)
117121

118-
useEffect(() => {
119-
const handleKeyDown = (event: KeyboardEvent) => {
120-
if (event.key === 'Enter' && forgotPasswordOpen) {
121-
handleForgotPassword()
122-
}
123-
}
124-
125-
window.addEventListener('keydown', handleKeyDown)
126-
return () => {
127-
window.removeEventListener('keydown', handleKeyDown)
128-
}
129-
}, [forgotPasswordEmail, forgotPasswordOpen])
130-
131122
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
132123
const newEmail = e.target.value
133124
setEmail(newEmail)
@@ -178,13 +169,33 @@ export default function LoginPage({
178169
const safeCallbackUrl = callbackUrl
179170
let errorHandled = false
180171

172+
// Execute Turnstile challenge on submit and get a fresh token
173+
let token: string | undefined
174+
if (turnstileSiteKey && turnstileRef.current) {
175+
try {
176+
turnstileRef.current.reset()
177+
turnstileRef.current.execute()
178+
token = await turnstileRef.current.getResponsePromise(15_000)
179+
} catch {
180+
setFormError('Captcha verification failed. Please try again.')
181+
setIsLoading(false)
182+
return
183+
}
184+
}
185+
186+
setFormError(null)
181187
const result = await client.signIn.email(
182188
{
183189
email,
184190
password,
185191
callbackURL: safeCallbackUrl,
186192
},
187193
{
194+
fetchOptions: {
195+
headers: {
196+
...(token ? { 'x-captcha-response': token } : {}),
197+
},
198+
},
188199
onError: (ctx) => {
189200
logger.error('Login error:', ctx.error)
190201

@@ -460,6 +471,22 @@ export default function LoginPage({
460471
</div>
461472
</div>
462473

474+
{turnstileSiteKey && (
475+
<div className='h-0 w-0 overflow-hidden'>
476+
<Turnstile
477+
ref={turnstileRef}
478+
siteKey={turnstileSiteKey}
479+
options={{ size: 'invisible', execution: 'execute' }}
480+
/>
481+
</div>
482+
)}
483+
484+
{formError && (
485+
<div className='text-red-400 text-xs'>
486+
<p>{formError}</p>
487+
</div>
488+
)}
489+
463490
<BrandedButton
464491
type='submit'
465492
disabled={isLoading}
@@ -540,45 +567,51 @@ export default function LoginPage({
540567
<ModalContent className='dark' size='sm'>
541568
<ModalHeader>Reset Password</ModalHeader>
542569
<ModalBody>
543-
<ModalDescription className='mb-4 text-[var(--text-muted)] text-sm'>
544-
Enter your email address and we'll send you a link to reset your password if your
545-
account exists.
546-
</ModalDescription>
547-
<div className='space-y-4'>
548-
<div className='space-y-2'>
549-
<Label htmlFor='reset-email'>Email</Label>
550-
<Input
551-
id='reset-email'
552-
value={forgotPasswordEmail}
553-
onChange={(e) => setForgotPasswordEmail(e.target.value)}
554-
placeholder='Enter your email'
555-
required
556-
type='email'
557-
className={cn(
558-
resetStatus.type === 'error' && 'border-red-500 focus:border-red-500'
570+
<form
571+
onSubmit={(e) => {
572+
e.preventDefault()
573+
handleForgotPassword()
574+
}}
575+
>
576+
<ModalDescription className='mb-4 text-[var(--text-muted)] text-sm'>
577+
Enter your email address and we'll send you a link to reset your password if your
578+
account exists.
579+
</ModalDescription>
580+
<div className='space-y-4'>
581+
<div className='space-y-2'>
582+
<Label htmlFor='reset-email'>Email</Label>
583+
<Input
584+
id='reset-email'
585+
value={forgotPasswordEmail}
586+
onChange={(e) => setForgotPasswordEmail(e.target.value)}
587+
placeholder='Enter your email'
588+
required
589+
type='email'
590+
className={cn(
591+
resetStatus.type === 'error' && 'border-red-500 focus:border-red-500'
592+
)}
593+
/>
594+
{resetStatus.type === 'error' && (
595+
<div className='mt-1 text-red-400 text-xs'>
596+
<p>{resetStatus.message}</p>
597+
</div>
559598
)}
560-
/>
561-
{resetStatus.type === 'error' && (
562-
<div className='mt-1 text-red-400 text-xs'>
599+
</div>
600+
{resetStatus.type === 'success' && (
601+
<div className='mt-1 text-[#4CAF50] text-xs'>
563602
<p>{resetStatus.message}</p>
564603
</div>
565604
)}
605+
<BrandedButton
606+
type='submit'
607+
disabled={isSubmittingReset}
608+
loading={isSubmittingReset}
609+
loadingText='Sending'
610+
>
611+
Send Reset Link
612+
</BrandedButton>
566613
</div>
567-
{resetStatus.type === 'success' && (
568-
<div className='mt-1 text-[#4CAF50] text-xs'>
569-
<p>{resetStatus.message}</p>
570-
</div>
571-
)}
572-
<BrandedButton
573-
type='button'
574-
onClick={handleForgotPassword}
575-
disabled={isSubmittingReset}
576-
loading={isSubmittingReset}
577-
loadingText='Sending'
578-
>
579-
Send Reset Link
580-
</BrandedButton>
581-
</div>
614+
</form>
582615
</ModalBody>
583616
</ModalContent>
584617
</Modal>

apps/sim/app/(auth)/signup/signup-form.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

3-
import { Suspense, useMemo, useState } from 'react'
3+
import { Suspense, useMemo, useRef, useState } from 'react'
4+
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'
45
import { createLogger } from '@sim/logger'
56
import { Eye, EyeOff } from 'lucide-react'
67
import Link from 'next/link'
@@ -90,6 +91,9 @@ function SignupFormContent({
9091
const [emailError, setEmailError] = useState('')
9192
const [emailErrors, setEmailErrors] = useState<string[]>([])
9293
const [showEmailValidationError, setShowEmailValidationError] = useState(false)
94+
const [formError, setFormError] = useState<string | null>(null)
95+
const turnstileRef = useRef<TurnstileInstance>(null)
96+
const turnstileSiteKey = useMemo(() => getEnv('NEXT_PUBLIC_TURNSTILE_SITE_KEY'), [])
9397
const buttonClass = useBrandedButtonClass()
9498

9599
const redirectUrl = useMemo(
@@ -245,13 +249,33 @@ function SignupFormContent({
245249

246250
const sanitizedName = trimmedName
247251

252+
// Execute Turnstile challenge on submit and get a fresh token
253+
let token: string | undefined
254+
if (turnstileSiteKey && turnstileRef.current) {
255+
try {
256+
turnstileRef.current.reset()
257+
turnstileRef.current.execute()
258+
token = await turnstileRef.current.getResponsePromise(15_000)
259+
} catch {
260+
setFormError('Captcha verification failed. Please try again.')
261+
setIsLoading(false)
262+
return
263+
}
264+
}
265+
266+
setFormError(null)
248267
const response = await client.signUp.email(
249268
{
250269
email: emailValue,
251270
password: passwordValue,
252271
name: sanitizedName,
253272
},
254273
{
274+
fetchOptions: {
275+
headers: {
276+
...(token ? { 'x-captcha-response': token } : {}),
277+
},
278+
},
255279
onError: (ctx) => {
256280
logger.error('Signup error:', ctx.error)
257281
const errorMessage: string[] = ['Failed to create account']
@@ -453,6 +477,22 @@ function SignupFormContent({
453477
</div>
454478
</div>
455479

480+
{turnstileSiteKey && (
481+
<div className='h-0 w-0 overflow-hidden'>
482+
<Turnstile
483+
ref={turnstileRef}
484+
siteKey={turnstileSiteKey}
485+
options={{ size: 'invisible', execution: 'execute' }}
486+
/>
487+
</div>
488+
)}
489+
490+
{formError && (
491+
<div className='text-red-400 text-xs'>
492+
<p>{formError}</p>
493+
</div>
494+
)}
495+
456496
<BrandedButton
457497
type='submit'
458498
disabled={isLoading}

apps/sim/app/(home)/components/enterprise/enterprise.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ function TrustStrip() {
460460
<div className='mx-6 mt-4 grid grid-cols-1 overflow-hidden rounded-[8px] border border-[#2A2A2A] sm:grid-cols-3 md:mx-8'>
461461
{/* SOC 2 + HIPAA combined */}
462462
<Link
463-
href='https://trust.delve.co/sim-studio'
463+
href='https://app.vanta.com/sim.ai/trust/v35ia0jil4l7dteqjgaktn'
464464
target='_blank'
465465
rel='noopener noreferrer'
466466
className='group flex items-center gap-3 border-[#2A2A2A] border-b px-4 py-[14px] transition-colors hover:bg-[#212121] sm:border-r sm:border-b-0'

apps/sim/app/(landing)/components/footer/components/compliance-badges.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ export default function ComplianceBadges() {
66
return (
77
<div className='mt-[6px] flex items-center gap-[12px]'>
88
{/* SOC2 badge */}
9-
<Link href='https://trust.delve.co/sim-studio' target='_blank' rel='noopener noreferrer'>
9+
<Link
10+
href='https://app.vanta.com/sim.ai/trust/v35ia0jil4l7dteqjgaktn'
11+
target='_blank'
12+
rel='noopener noreferrer'
13+
>
1014
<Image
1115
src='/footer/soc2.png'
1216
alt='SOC2 Compliant'
@@ -18,7 +22,11 @@ export default function ComplianceBadges() {
1822
/>
1923
</Link>
2024
{/* HIPAA badge */}
21-
<Link href='https://trust.delve.co/sim-studio' target='_blank' rel='noopener noreferrer'>
25+
<Link
26+
href='https://app.vanta.com/sim.ai/trust/v35ia0jil4l7dteqjgaktn'
27+
target='_blank'
28+
rel='noopener noreferrer'
29+
>
2230
<HIPAABadgeIcon className='h-[54px] w-[54px]' />
2331
</Link>
2432
</div>

0 commit comments

Comments
 (0)