mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-11 07:04:58 -05:00
feat/platform-v3 (#1698)
* feat(fonts): season replacing geist * feat(emcnn): created emcn * feat(sidebar): created new sidebar with header and workflow list * improvement(sidebar): expanded workflow/folder item text sizing and adjusted button padding * feat(sidebar): added search UI, updated workflows styling * improvement: globals styling with antialiased in dark mode only * feat(sidebar): blocks and triggers ui/ux updated * refactor(sidebar): moved logic into hooks * feat(sidebar): improved workflow/folder dragging UI/UX; refactored logic into hooks * improvement(sidebar): adjusted triggers/blocks padding for header * improvement(sidebar): dragging hover handler; closed folders by default minus active path * improvement(sidebar): panel resize logic * improvement(sidebar): blocks and triggers expanded indicator * feat(tooltips): new emcn component emerged * feat(sidebar): workflow list handling updated * refactor: added cursorrules * feat(panel): new panel layout * improvement(workspaces): firname's workspace instead of fn ln's workspace * feat(platform): panel header, new emcn icons, more button variants, refactor sidebar components * improvement(emcn): added button variants * feat(panel): tab system * feat(copilot): refactor, adjusted welcome and user-input UI/UX * feat(copilot): baseline user-input ui/ux improvement * feat(emcn): badge outline variant * fix: build errors * feat(copilot): base UI copilot * refactor(workflow-block): added hooks, components * feat(design): created design panel and removed isWide * refactor(subblock): edited components, styling * feat: emcn, editor * feat(panel): toolbar, editor * feat(workflow-block): refactor, adjust base styling * feat(workflow-block): new block, edge * feat: workflow-block, connections, action-bar, copilot * feat: panel, workflow, emcn, workflow block, subblocks; clean copilot * sim-326: remove remote code execution toggle, hide dropdown for language if E2B is not enabled * feat: sidebar navigation, tag coloring; refactor: rebased to staging * fix: build errors * improvement: subblock styles * feat: workspaces, terminal, emcn, controls * feat: delete workflow * fix: rebased * fix build errors --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
This commit is contained in:
@@ -52,7 +52,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should create secure fetch in VM context', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "test"',
|
||||
useLocalVM: true,
|
||||
})
|
||||
|
||||
const { POST } = await import('@/app/api/function/execute/route')
|
||||
@@ -97,7 +96,6 @@ describe('Function Execute API Route', () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "Hello World"',
|
||||
timeout: 5000,
|
||||
useLocalVM: true,
|
||||
})
|
||||
|
||||
const { POST } = await import('@/app/api/function/execute/route')
|
||||
@@ -127,19 +125,14 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should use default timeout when not provided', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "test"',
|
||||
useLocalVM: true,
|
||||
})
|
||||
|
||||
const { POST } = await import('@/app/api/function/execute/route')
|
||||
const response = await POST(req)
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/\[.*\] Function execution request/),
|
||||
expect.objectContaining({
|
||||
timeout: 5000, // default timeout
|
||||
})
|
||||
)
|
||||
// The logger now logs execution success, not the request details
|
||||
expect(mockLogger.info).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -147,7 +140,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should resolve environment variables with {{var_name}} syntax', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return {{API_KEY}}',
|
||||
useLocalVM: true,
|
||||
envVars: {
|
||||
API_KEY: 'secret-key-123',
|
||||
},
|
||||
@@ -163,7 +155,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should resolve tag variables with <tag_name> syntax', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return <email>',
|
||||
useLocalVM: true,
|
||||
params: {
|
||||
email: { id: '123', subject: 'Test Email' },
|
||||
},
|
||||
@@ -179,7 +170,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should NOT treat email addresses as template variables', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "Email sent to user"',
|
||||
useLocalVM: true,
|
||||
params: {
|
||||
email: {
|
||||
from: 'Waleed Latif <waleed@sim.ai>',
|
||||
@@ -198,7 +188,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should only match valid variable names in angle brackets', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return <validVar> + "<invalid@email.com>" + <another_valid>',
|
||||
useLocalVM: true,
|
||||
params: {
|
||||
validVar: 'hello',
|
||||
another_valid: 'world',
|
||||
@@ -238,7 +227,6 @@ describe('Function Execute API Route', () => {
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return <email>',
|
||||
useLocalVM: true,
|
||||
params: gmailData,
|
||||
})
|
||||
|
||||
@@ -264,7 +252,6 @@ describe('Function Execute API Route', () => {
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return <email>',
|
||||
useLocalVM: true,
|
||||
params: complexEmailData,
|
||||
})
|
||||
|
||||
@@ -280,7 +267,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should handle custom tool execution with direct parameter access', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return location + " weather is sunny"',
|
||||
useLocalVM: true,
|
||||
params: {
|
||||
location: 'San Francisco',
|
||||
},
|
||||
@@ -312,7 +298,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should handle timeout parameter', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "test"',
|
||||
useLocalVM: true,
|
||||
timeout: 10000,
|
||||
})
|
||||
|
||||
@@ -330,7 +315,6 @@ describe('Function Execute API Route', () => {
|
||||
it.concurrent('should handle empty parameters object', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "no params"',
|
||||
useLocalVM: true,
|
||||
params: {},
|
||||
})
|
||||
|
||||
@@ -364,7 +348,6 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const obj = {\n name: "test",\n description: "This has a missing closing quote\n};\nreturn obj;',
|
||||
useLocalVM: true,
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
@@ -408,7 +391,6 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const obj = null;\nreturn obj.someMethod();',
|
||||
useLocalVM: true,
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
@@ -450,7 +432,6 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const x = 42;\nreturn undefinedVariable + x;',
|
||||
useLocalVM: true,
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
@@ -481,7 +462,6 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "test";',
|
||||
useLocalVM: true,
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
@@ -518,7 +498,6 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const a = 1;\nconst b = 2;\nconst c = 3;\nconst d = 4;\nreturn a + b + c + d;',
|
||||
useLocalVM: true,
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
@@ -550,7 +529,6 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const obj = {\n name: "test"\n// Missing closing brace',
|
||||
useLocalVM: true,
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
@@ -571,7 +549,6 @@ SyntaxError: Invalid or unexpected token
|
||||
// This tests the escapeRegExp function indirectly
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return {{special.chars+*?}}',
|
||||
useLocalVM: true,
|
||||
envVars: {
|
||||
'special.chars+*?': 'escaped-value',
|
||||
},
|
||||
@@ -588,7 +565,6 @@ SyntaxError: Invalid or unexpected token
|
||||
// Test with complex but not circular data first
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return <complexData>',
|
||||
useLocalVM: true,
|
||||
params: {
|
||||
complexData: {
|
||||
special: 'chars"with\'quotes',
|
||||
|
||||
@@ -8,9 +8,8 @@ import { validateProxyUrl } from '@/lib/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/utils'
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const runtime = 'nodejs'
|
||||
// Segment config exports must be statically analyzable.
|
||||
// Mirror MAX_EXECUTION_DURATION (210s) from '@/lib/execution/constants'.
|
||||
export const maxDuration = 210
|
||||
|
||||
export const MAX_DURATION = 210
|
||||
|
||||
const logger = createLogger('FunctionExecuteAPI')
|
||||
|
||||
@@ -658,7 +657,6 @@ export async function POST(req: NextRequest) {
|
||||
params = {},
|
||||
timeout = DEFAULT_EXECUTION_TIMEOUT_MS,
|
||||
language = DEFAULT_CODE_LANGUAGE,
|
||||
useLocalVM = false,
|
||||
envVars = {},
|
||||
blockData = {},
|
||||
blockNameMapping = {},
|
||||
@@ -693,11 +691,46 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
const e2bEnabled = isTruthy(env.E2B_ENABLED)
|
||||
const lang = isValidCodeLanguage(language) ? language : DEFAULT_CODE_LANGUAGE
|
||||
|
||||
// Extract imports once for JavaScript code (reuse later to avoid double extraction)
|
||||
let jsImports = ''
|
||||
let jsRemainingCode = resolvedCode
|
||||
let hasImports = false
|
||||
|
||||
if (lang === CodeLanguage.JavaScript) {
|
||||
const extractionResult = await extractJavaScriptImports(resolvedCode)
|
||||
jsImports = extractionResult.imports
|
||||
jsRemainingCode = extractionResult.remainingCode
|
||||
|
||||
// Check for ES6 imports or CommonJS require statements
|
||||
// ES6 imports are extracted by the TypeScript parser
|
||||
// Also check for require() calls which indicate external dependencies
|
||||
const hasRequireStatements = /require\s*\(\s*['"`]/.test(resolvedCode)
|
||||
hasImports = jsImports.trim().length > 0 || hasRequireStatements
|
||||
}
|
||||
|
||||
// Python always requires E2B
|
||||
if (lang === CodeLanguage.Python && !e2bEnabled) {
|
||||
throw new Error(
|
||||
'Python execution requires E2B to be enabled. Please contact your administrator to enable E2B, or use JavaScript instead.'
|
||||
)
|
||||
}
|
||||
|
||||
// JavaScript with imports requires E2B
|
||||
if (lang === CodeLanguage.JavaScript && hasImports && !e2bEnabled) {
|
||||
throw new Error(
|
||||
'JavaScript code with import statements requires E2B to be enabled. Please remove the import statements, or contact your administrator to enable E2B.'
|
||||
)
|
||||
}
|
||||
|
||||
// Use E2B if:
|
||||
// - E2B is enabled AND
|
||||
// - Not a custom tool AND
|
||||
// - (Python OR JavaScript with imports)
|
||||
const useE2B =
|
||||
e2bEnabled &&
|
||||
!useLocalVM &&
|
||||
!isCustomTool &&
|
||||
(lang === CodeLanguage.JavaScript || lang === CodeLanguage.Python)
|
||||
(lang === CodeLanguage.Python || (lang === CodeLanguage.JavaScript && hasImports))
|
||||
|
||||
if (useE2B) {
|
||||
logger.info(`[${requestId}] E2B status`, {
|
||||
@@ -712,7 +745,9 @@ export async function POST(req: NextRequest) {
|
||||
// Track prologue lines for error adjustment
|
||||
let prologueLineCount = 0
|
||||
|
||||
const { imports, remainingCode } = await extractJavaScriptImports(resolvedCode)
|
||||
// Reuse the imports we already extracted earlier
|
||||
const imports = jsImports
|
||||
const remainingCode = jsRemainingCode
|
||||
|
||||
const importSection: string = imports ? `${imports}\n` : ''
|
||||
const importLineCount = imports ? imports.split('\n').length : 0
|
||||
|
||||
Reference in New Issue
Block a user