@@ -213,24 +213,81 @@ function createUserFriendlyErrorMessage(
213213}
214214
215215/**
216- * Resolves environment variables and tags in code
217- * @param code - Code with variables
218- * @param params - Parameters that may contain variable values
219- * @param envVars - Environment variables from the workflow
220- * @returns Resolved code
216+ * Resolves workflow variables with <variable.name> syntax
221217 */
218+ function resolveWorkflowVariables (
219+ code : string ,
220+ workflowVariables : Record < string , any > ,
221+ contextVariables : Record < string , any >
222+ ) : string {
223+ let resolvedCode = code
222224
223- function resolveCodeVariables (
225+ const variableMatches = resolvedCode . match ( / < v a r i a b l e \. ( [ ^ > ] + ) > / g) || [ ]
226+ for ( const match of variableMatches ) {
227+ const variableName = match . slice ( '<variable.' . length , - 1 ) . trim ( )
228+
229+ // Find the variable by name (workflowVariables is indexed by ID, values are variable objects)
230+ const foundVariable = Object . entries ( workflowVariables ) . find (
231+ ( [ _ , variable ] ) => ( variable . name || '' ) . replace ( / \s + / g, '' ) === variableName
232+ )
233+
234+ if ( foundVariable ) {
235+ const variable = foundVariable [ 1 ]
236+ // Get the typed value - handle different variable types
237+ let variableValue = variable . value
238+
239+ if ( variable . value !== undefined && variable . value !== null ) {
240+ try {
241+ // Handle 'string' type the same as 'plain' for backward compatibility
242+ const type = variable . type === 'string' ? 'plain' : variable . type
243+
244+ // For plain text, use exactly what's entered without modifications
245+ if ( type === 'plain' && typeof variableValue === 'string' ) {
246+ // Use as-is for plain text
247+ } else if ( type === 'number' ) {
248+ variableValue = Number ( variableValue )
249+ } else if ( type === 'boolean' ) {
250+ variableValue = variableValue === 'true' || variableValue === true
251+ } else if ( type === 'json' ) {
252+ try {
253+ variableValue =
254+ typeof variableValue === 'string' ? JSON . parse ( variableValue ) : variableValue
255+ } catch {
256+ // Keep original value if JSON parsing fails
257+ }
258+ }
259+ } catch ( error ) {
260+ // Fallback to original value on error
261+ variableValue = variable . value
262+ }
263+ }
264+
265+ // Create a safe variable reference
266+ const safeVarName = `__variable_${ variableName . replace ( / [ ^ a - z A - Z 0 - 9 _ ] / g, '_' ) } `
267+ contextVariables [ safeVarName ] = variableValue
268+
269+ // Replace the variable reference with the safe variable name
270+ resolvedCode = resolvedCode . replace ( new RegExp ( escapeRegExp ( match ) , 'g' ) , safeVarName )
271+ } else {
272+ // Variable not found - replace with empty string to avoid syntax errors
273+ resolvedCode = resolvedCode . replace ( new RegExp ( escapeRegExp ( match ) , 'g' ) , '' )
274+ }
275+ }
276+
277+ return resolvedCode
278+ }
279+
280+ /**
281+ * Resolves environment variables with {{var_name}} syntax
282+ */
283+ function resolveEnvironmentVariables (
224284 code : string ,
225285 params : Record < string , any > ,
226- envVars : Record < string , string > = { } ,
227- blockData : Record < string , any > = { } ,
228- blockNameMapping : Record < string , string > = { }
229- ) : { resolvedCode : string ; contextVariables : Record < string , any > } {
286+ envVars : Record < string , string > ,
287+ contextVariables : Record < string , any >
288+ ) : string {
230289 let resolvedCode = code
231- const contextVariables : Record < string , any > = { }
232290
233- // Resolve environment variables with {{var_name}} syntax
234291 const envVarMatches = resolvedCode . match ( / \{ \{ ( [ ^ } ] + ) \} \} / g) || [ ]
235292 for ( const match of envVarMatches ) {
236293 const varName = match . slice ( 2 , - 2 ) . trim ( )
@@ -245,7 +302,21 @@ function resolveCodeVariables(
245302 resolvedCode = resolvedCode . replace ( new RegExp ( escapeRegExp ( match ) , 'g' ) , safeVarName )
246303 }
247304
248- // Resolve tags with <tag_name> syntax (including nested paths like <block.response.data>)
305+ return resolvedCode
306+ }
307+
308+ /**
309+ * Resolves tags with <tag_name> syntax (including nested paths like <block.response.data>)
310+ */
311+ function resolveTagVariables (
312+ code : string ,
313+ params : Record < string , any > ,
314+ blockData : Record < string , any > ,
315+ blockNameMapping : Record < string , string > ,
316+ contextVariables : Record < string , any >
317+ ) : string {
318+ let resolvedCode = code
319+
249320 const tagMatches = resolvedCode . match ( / < ( [ a - z A - Z _ ] [ a - z A - Z 0 - 9 _ . ] * [ a - z A - Z 0 - 9 _ ] ) > / g) || [ ]
250321
251322 for ( const match of tagMatches ) {
@@ -300,6 +371,42 @@ function resolveCodeVariables(
300371 resolvedCode = resolvedCode . replace ( new RegExp ( escapeRegExp ( match ) , 'g' ) , safeVarName )
301372 }
302373
374+ return resolvedCode
375+ }
376+
377+ /**
378+ * Resolves environment variables and tags in code
379+ * @param code - Code with variables
380+ * @param params - Parameters that may contain variable values
381+ * @param envVars - Environment variables from the workflow
382+ * @returns Resolved code
383+ */
384+ function resolveCodeVariables (
385+ code : string ,
386+ params : Record < string , any > ,
387+ envVars : Record < string , string > = { } ,
388+ blockData : Record < string , any > = { } ,
389+ blockNameMapping : Record < string , string > = { } ,
390+ workflowVariables : Record < string , any > = { }
391+ ) : { resolvedCode : string ; contextVariables : Record < string , any > } {
392+ let resolvedCode = code
393+ const contextVariables : Record < string , any > = { }
394+
395+ // Resolve workflow variables with <variable.name> syntax first
396+ resolvedCode = resolveWorkflowVariables ( resolvedCode , workflowVariables , contextVariables )
397+
398+ // Resolve environment variables with {{var_name}} syntax
399+ resolvedCode = resolveEnvironmentVariables ( resolvedCode , params , envVars , contextVariables )
400+
401+ // Resolve tags with <tag_name> syntax (including nested paths like <block.response.data>)
402+ resolvedCode = resolveTagVariables (
403+ resolvedCode ,
404+ params ,
405+ blockData ,
406+ blockNameMapping ,
407+ contextVariables
408+ )
409+
303410 return { resolvedCode, contextVariables }
304411}
305412
@@ -338,6 +445,7 @@ export async function POST(req: NextRequest) {
338445 envVars = { } ,
339446 blockData = { } ,
340447 blockNameMapping = { } ,
448+ workflowVariables = { } ,
341449 workflowId,
342450 isCustomTool = false ,
343451 } = body
@@ -360,16 +468,17 @@ export async function POST(req: NextRequest) {
360468 executionParams ,
361469 envVars ,
362470 blockData ,
363- blockNameMapping
471+ blockNameMapping ,
472+ workflowVariables
364473 )
365474 resolvedCode = codeResolution . resolvedCode
366475 const contextVariables = codeResolution . contextVariables
367476
368477 const executionMethod = 'vm' // Default execution method
369478
370479 logger . info ( `[${ requestId } ] Using VM for code execution` , {
371- resolvedCode,
372480 hasEnvVars : Object . keys ( envVars ) . length > 0 ,
481+ hasWorkflowVariables : Object . keys ( workflowVariables ) . length > 0 ,
373482 } )
374483
375484 // Create a secure context with console logging
0 commit comments