2025-06-09 05:15:35 -04:00
import {
FieldType ,
IDataObject ,
IExecuteSingleFunctions ,
IHttpRequestMethods ,
IHttpRequestOptions ,
ILoadOptionsFunctions ,
INodePropertyOptions ,
INodeType ,
INodeTypeDescription ,
ResourceMapperField ,
ResourceMapperFields ,
} from 'n8n-workflow' ;
2025-04-15 01:43:53 +08:00
2025-06-09 05:15:35 -04:00
async function skyvernApiRequest (
this : IExecuteSingleFunctions | ILoadOptionsFunctions ,
method : IHttpRequestMethods ,
endpoint : string ,
body : IDataObject | undefined = undefined ,
) : Promise < any > {
const credentials = await this . getCredentials ( 'skyvernApi' ) ;
const options : IHttpRequestOptions = {
baseURL : credentials.baseUrl as string ,
method ,
url : endpoint ,
body ,
json : true ,
} ;
return this . helpers . requestWithAuthentication . call ( this , 'skyvernApi' , options ) ;
2025-04-15 01:43:53 +08:00
}
2025-03-20 01:32:55 +08:00
export class Skyvern implements INodeType {
description : INodeTypeDescription = {
displayName : 'Skyvern' ,
name : 'skyvern' ,
2025-06-09 05:15:35 -04:00
icon : 'file:skyvern.svg' ,
2025-03-20 01:32:55 +08:00
group : [ 'transform' ] ,
description : 'Node to interact with Skyvern' ,
defaults : {
name : 'Skyvern' ,
} ,
2025-06-09 05:15:35 -04:00
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
2025-03-20 01:32:55 +08:00
credentials : [
{
name : 'skyvernApi' ,
required : true ,
} ,
] ,
properties : [
{
displayName : 'Resource' ,
name : 'resource' ,
type : 'options' ,
2025-04-30 11:10:14 -07:00
noDataExpression : true ,
2025-03-20 01:32:55 +08:00
options : [
{
name : 'Task' ,
value : 'task' ,
} ,
{
name : 'Workflow' ,
value : 'workflow' ,
} ,
] ,
default : 'task' ,
} ,
{
displayName : 'Operation' ,
2025-06-09 05:15:35 -04:00
name : 'operation' ,
2025-03-20 01:32:55 +08:00
type : 'options' ,
2025-06-09 05:15:35 -04:00
noDataExpression : true ,
2025-03-20 01:32:55 +08:00
required : true ,
2025-06-09 05:15:35 -04:00
default : 'dispatchTask' ,
2025-03-20 01:32:55 +08:00
options : [
{
name : 'Dispatch a Task' ,
2025-06-09 05:15:35 -04:00
value : 'dispatchTask' ,
action : 'Dispatch a task to execute asynchronously' ,
2025-03-20 01:32:55 +08:00
description : 'Dispatch a task to execute asynchronously' ,
2025-06-09 05:15:35 -04:00
displayOptions : {
show : {
resource : [ 'task' ] ,
} ,
} ,
routing : {
request : {
baseURL : '={{$credentials.baseUrl}}' ,
method : 'POST' ,
url : '/v1/run/tasks' ,
} ,
send : {
preSend : [
async function (
this : IExecuteSingleFunctions ,
requestOptions : IHttpRequestOptions ,
) : Promise < IHttpRequestOptions > {
const taskOptions = this . getNodeParameter ( 'taskOptions' ) as IDataObject ;
const legacy_engine = taskOptions [ 'engine' ] as string | null ;
if ( legacy_engine === 'v1' ) {
( requestOptions . body as IDataObject ) [ 'engine' ] = 'skyvern-1.0' ;
} else if ( legacy_engine === 'v2' ) {
( requestOptions . body as IDataObject ) [ 'engine' ] = 'skyvern-2.0' ;
}
return requestOptions ;
} ,
] ,
} ,
} ,
2025-03-20 01:32:55 +08:00
} ,
{
name : 'Get a Task' ,
2025-06-09 05:15:35 -04:00
value : 'getTask' ,
action : 'Get a task by ID' ,
2025-03-20 01:32:55 +08:00
description : 'Get a task by ID' ,
2025-06-09 05:15:35 -04:00
displayOptions : {
show : {
resource : [ 'task' ] ,
} ,
} ,
routing : {
request : {
baseURL : '={{$credentials.baseUrl}}' ,
method : 'GET' ,
url : '/v1/run/tasks' ,
} ,
} ,
2025-03-20 01:32:55 +08:00
} ,
2025-06-09 05:15:35 -04:00
{
name : 'Get a Workflow Run' ,
value : 'getWorkflow' ,
action : 'Get a workflow run by ID' ,
description : 'Get a workflow run by ID' ,
displayOptions : {
show : {
resource : [ 'workflow' ] ,
} ,
} ,
routing : {
request : {
baseURL : '={{$credentials.baseUrl}}' ,
method : 'GET' ,
} ,
} ,
2025-03-20 01:32:55 +08:00
} ,
2025-06-09 05:15:35 -04:00
{
name : 'Dispatch a Workflow Run' ,
value : 'dispatchWorkflow' ,
action : 'Dispatch a workflow run to execute asynchronously' ,
description : 'Dispatch a workflow run to execute asynchronously' ,
displayOptions : {
show : {
resource : [ 'workflow' ] ,
2025-03-20 01:32:55 +08:00
} ,
2025-06-09 05:15:35 -04:00
} ,
routing : {
request : {
baseURL : '={{$credentials.baseUrl}}' ,
method : 'POST' ,
} ,
} ,
2025-03-20 01:32:55 +08:00
} ,
2025-06-09 05:15:35 -04:00
] ,
2025-03-20 01:32:55 +08:00
} ,
{
displayName : 'User Prompt' ,
description : 'The prompt for Skyvern to execute' ,
name : 'userPrompt' ,
type : 'string' ,
required : true ,
default : '' ,
placeholder : 'eg: Navigate to the Hacker News homepage and get the top 3 posts.' ,
displayOptions : {
show : {
resource : [ 'task' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'dispatchTask' ] ,
2025-03-20 01:32:55 +08:00
} ,
} ,
routing : {
request : {
body : {
2025-05-22 22:15:20 +08:00
prompt : '={{$value}}' ,
2025-03-20 01:32:55 +08:00
} ,
} ,
} ,
} ,
{
displayName : 'URL' ,
description : 'The URL to navigate to' ,
name : 'url' ,
type : 'string' ,
default : '' ,
placeholder : 'eg: https://news.ycombinator.com/' ,
displayOptions : {
show : {
resource : [ 'task' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'dispatchTask' ] ,
2025-03-20 01:32:55 +08:00
} ,
} ,
routing : {
request : {
body : {
url : '={{$value ? $value : null}}' ,
} ,
} ,
} ,
} ,
2025-04-30 11:10:14 -07:00
{
displayName : 'Webhook Callback URL' ,
description : 'Optional URL that Skyvern will call when the task finishes' ,
name : 'webhookUrl' ,
type : 'string' ,
default : '' ,
placeholder : 'https://example.com/webhook' ,
displayOptions : {
show : {
resource : [ 'task' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'dispatchTask' ] ,
2025-04-30 11:10:14 -07:00
} ,
} ,
routing : {
request : {
body : {
2025-05-22 22:15:20 +08:00
webhook_url : '={{$value ? $value : null}}' ,
2025-04-30 11:10:14 -07:00
} ,
} ,
} ,
} ,
2025-03-20 01:32:55 +08:00
{
displayName : 'Task ID' ,
description : 'The ID of the task' ,
name : 'taskId' ,
type : 'string' ,
required : true ,
default : '' ,
displayOptions : {
show : {
resource : [ 'task' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'getTask' ] ,
2025-03-20 01:32:55 +08:00
} ,
} ,
routing : {
request : {
method : 'GET' ,
2025-05-22 22:15:20 +08:00
url : '={{"/v1/runs/" + $value}}' ,
2025-03-20 01:32:55 +08:00
} ,
} ,
} ,
{
displayName : 'Task Options' ,
name : 'taskOptions' ,
type : 'collection' ,
description : 'Optional Configuration for the task' ,
placeholder : 'Add Task Options' ,
default : { } ,
options : [
{
2025-05-22 22:15:20 +08:00
displayName : 'Engine(Deprecated)' ,
description : 'Deprecated: please migrate to use "Engine" option' ,
2025-03-20 01:32:55 +08:00
name : 'engine' ,
type : 'options' ,
2025-05-22 22:15:20 +08:00
default : '' ,
2025-03-20 01:32:55 +08:00
options : [
{
name : 'TaskV1' ,
value : 'v1' ,
} ,
{
name : 'TaskV2' ,
value : 'v2' ,
} ,
2025-05-23 02:14:31 +08:00
{
name : 'THIS FIELD IS DEPRECATED' ,
value : '' ,
} ,
2025-03-20 01:32:55 +08:00
] ,
} ,
2025-05-22 22:15:20 +08:00
{
displayName : 'Engine' ,
name : 'runEngine' ,
type : 'options' ,
default : 'skyvern-2.0' ,
options : [
{
name : 'Skyvern 1.0' ,
value : 'skyvern-1.0' ,
} ,
{
name : 'Skyvern 2.0' ,
value : 'skyvern-2.0' ,
} ,
{
name : 'OpenAI CUA' ,
value : 'openai-cua' ,
} ,
{
name : 'Anthropic CUA' ,
value : 'anthropic-cua' ,
}
] ,
routing : {
request : {
body : {
engine : '={{$value}}' ,
} ,
} ,
} ,
}
2025-03-20 01:32:55 +08:00
] ,
displayOptions : {
show : {
resource : [ 'task' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'dispatchTask' ] ,
2025-03-20 01:32:55 +08:00
} ,
} ,
} ,
{
2025-06-09 05:15:35 -04:00
displayName : 'Workflow Name or ID' ,
2025-03-20 01:32:55 +08:00
description : 'The title of the workflow. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.' ,
name : 'workflowId' ,
type : 'options' ,
typeOptions : {
loadOptionsMethod : 'getWorkflows' ,
loadOptionsDependsOn : [ 'resource' ] ,
} ,
required : true ,
default : '' ,
displayOptions : {
show : {
resource : [ 'workflow' ] ,
} ,
} ,
} ,
{
displayName : 'Workflow Run ID' ,
description : 'The ID of the workflow run' ,
name : 'workflowRunId' ,
type : 'string' ,
required : true ,
default : '' ,
displayOptions : {
show : {
resource : [ 'workflow' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'getWorkflow' ] ,
2025-03-20 01:32:55 +08:00
} ,
} ,
routing : {
request : {
url : '={{"/api/v1/workflows/" + $parameter["workflowId"] + "/runs/" + $value}}' ,
} ,
} ,
} ,
{
displayName : 'Workflow Run Parameters' ,
name : 'workflowRunParameters' ,
type : 'resourceMapper' ,
noDataExpression : true ,
description : 'The JSON-formatted parameters to pass the workflow run to execute' ,
required : true ,
default : {
mappingMode : 'defineBelow' ,
value : null ,
} ,
displayOptions : {
show : {
resource : [ 'workflow' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'dispatchWorkflow' ] ,
2025-03-20 01:32:55 +08:00
} ,
} ,
typeOptions : {
loadOptionsDependsOn : [ 'workflowId' ] ,
resourceMapper : {
resourceMapperMethod : 'getWorkflowRunParameters' ,
mode : 'update' ,
fieldWords : {
singular : 'workflowRunParameter' ,
plural : 'workflowRunParameters' ,
} ,
addAllFields : true ,
multiKeyMatch : true ,
} ,
} ,
routing : {
request : {
url : '={{"/api/v1/workflows/" + $parameter["workflowId"] + "/run"}}' ,
body : {
data : '={{$value["value"]}}' ,
} ,
} ,
} ,
} ,
2025-04-30 11:10:14 -07:00
{
displayName : 'Webhook Callback URL' ,
description : 'Optional URL that Skyvern will call when the workflow run finishes' ,
name : 'webhookCallbackUrl' ,
type : 'string' ,
default : '' ,
placeholder : 'https://example.com/webhook' ,
displayOptions : {
show : {
resource : [ 'workflow' ] ,
2025-06-09 05:15:35 -04:00
operation : [ 'dispatchWorkflow' ] ,
2025-04-30 11:10:14 -07:00
} ,
} ,
routing : {
request : {
body : {
2025-05-22 22:15:20 +08:00
webhook_callback_url : '={{$value ? $value : null}}' ,
2025-04-30 11:10:14 -07:00
} ,
} ,
} ,
} ,
2025-03-20 01:32:55 +08:00
] ,
version : 1 ,
} ;
2025-04-30 11:10:14 -07:00
2025-03-20 01:32:55 +08:00
methods = {
loadOptions : {
async getWorkflows ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const resource = this . getCurrentNodeParameter ( 'resource' ) as string ;
if ( resource !== 'workflow' ) return [ ] ;
2025-06-09 05:15:35 -04:00
const response = await skyvernApiRequest . call (
this ,
'GET' ,
'/api/v1/workflows?page_size=100' ,
) ;
const data = response ;
2025-03-20 01:32:55 +08:00
return data . map ( ( workflow : any ) = > ( {
name : workflow.title ,
value : workflow.workflow_permanent_id ,
} ) ) ;
} ,
} ,
resourceMapping : {
async getWorkflowRunParameters ( this : ILoadOptionsFunctions ) : Promise < ResourceMapperFields > {
const resource = this . getCurrentNodeParameter ( 'resource' ) as string ;
if ( resource !== 'workflow' ) return { fields : [ ] } ;
2025-06-09 05:15:35 -04:00
const operation = this . getCurrentNodeParameter ( 'operation' ) as string ;
if ( operation !== 'dispatchWorkflow' ) return { fields : [ ] } ;
2025-04-30 11:10:14 -07:00
2025-03-20 01:32:55 +08:00
const workflowId = this . getCurrentNodeParameter ( 'workflowId' ) as string ;
if ( ! workflowId ) return { fields : [ ] } ;
2025-06-09 05:15:35 -04:00
const workflow = await skyvernApiRequest . call (
this ,
'GET' ,
` /api/v1/workflows/ ${ workflowId } ` ,
) ;
2025-03-20 01:32:55 +08:00
const parameters : any [ ] = workflow . workflow_definition . parameters ;
const fields : ResourceMapperField [ ] = await Promise . all (
2025-04-30 11:10:14 -07:00
parameters
. filter ( ( parameter : any ) = > parameter . parameter_type === 'workflow' || parameter . parameter_type === 'credential' )
. map ( async ( parameter : any ) = > {
let options : INodePropertyOptions [ ] | undefined = undefined ;
let parameterType : FieldType | undefined = undefined ;
if ( parameter . parameter_type === 'credential' ) {
2025-06-09 05:15:35 -04:00
const credData = await skyvernApiRequest . call (
this ,
'GET' ,
'/api/v1/credentials' ,
) ;
2025-04-30 11:10:14 -07:00
options = credData . map ( ( credential : any ) = > ( {
name : credential.name ,
value : credential.credential_id ,
} ) ) ;
parameterType = 'options' ;
} else {
const parameter_type_map : Record < string , FieldType > = {
string : 'string' ,
integer : 'number' ,
float : 'number' ,
boolean : 'boolean' ,
2025-05-13 22:14:41 +08:00
json : 'object' ,
2025-04-30 11:10:14 -07:00
file_url : 'url' ,
}
parameterType = parameter_type_map [ parameter . workflow_parameter_type ] ;
2025-03-20 01:32:55 +08:00
}
2025-04-30 11:10:14 -07:00
return {
id : parameter.key ,
displayName : parameter.key ,
defaultMatch : true ,
canBeUsedToMatch : false ,
required : parameter.default_value === undefined || parameter . default_value === null ,
display : true ,
type : parameterType ,
options : options ,
} ;
} )
2025-03-20 01:32:55 +08:00
) ;
// HACK: If there are no parameters, add a empty field to avoid the resource mapper from crashing
if ( fields . length === 0 ) {
fields . push ( {
id : 'NO_PARAMETERS' ,
displayName : 'No Parameters' ,
defaultMatch : false ,
canBeUsedToMatch : false ,
required : false ,
display : true ,
type : 'string' ,
} ) ;
}
return {
fields : fields ,
}
} ,
} ,
}
2025-04-23 23:49:03 -07:00
}