Update n8n Skyvern.node.ts to include webhook_callback_url option in … (#2230)
This commit is contained in:
@@ -10,18 +10,18 @@ async function makeRequest(url: string, options: any = {}): Promise<any> {
|
|||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
hostname: parsedUrl.hostname,
|
hostname: parsedUrl.hostname,
|
||||||
path: parsedUrl.pathname + parsedUrl.search,
|
path: parsedUrl.pathname + parsedUrl.search,
|
||||||
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
|
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
|
||||||
method: options.method || 'GET',
|
method: options.method || 'GET',
|
||||||
headers: options.headers || {},
|
headers: options.headers || {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const req = transport.request(requestOptions, (res) => {
|
const req = transport.request(requestOptions, (res) => {
|
||||||
let data = '';
|
let data = '';
|
||||||
|
|
||||||
res.on('data', (chunk) => {
|
res.on('data', (chunk) => {
|
||||||
data += chunk;
|
data += chunk;
|
||||||
});
|
});
|
||||||
|
|
||||||
res.on('end', () => {
|
res.on('end', () => {
|
||||||
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
const response = {
|
const response = {
|
||||||
@@ -47,15 +47,15 @@ async function makeRequest(url: string, options: any = {}): Promise<any> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', (error) => {
|
req.on('error', (error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.body) {
|
if (options.body) {
|
||||||
req.write(options.body);
|
req.write(options.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ export class Skyvern implements INodeType {
|
|||||||
displayName: 'Resource',
|
displayName: 'Resource',
|
||||||
name: 'resource',
|
name: 'resource',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Task',
|
name: 'Task',
|
||||||
@@ -93,7 +93,6 @@ export class Skyvern implements INodeType {
|
|||||||
name: 'Workflow',
|
name: 'Workflow',
|
||||||
value: 'workflow',
|
value: 'workflow',
|
||||||
},
|
},
|
||||||
|
|
||||||
],
|
],
|
||||||
default: 'task',
|
default: 'task',
|
||||||
},
|
},
|
||||||
@@ -135,18 +134,31 @@ export class Skyvern implements INodeType {
|
|||||||
const taskOptions: IDataObject = this.getNodeParameter('taskOptions') as IDataObject;
|
const taskOptions: IDataObject = this.getNodeParameter('taskOptions') as IDataObject;
|
||||||
if (taskOptions["engine"] !== "v1") return requestOptions;
|
if (taskOptions["engine"] !== "v1") return requestOptions;
|
||||||
|
|
||||||
// trigger the generate task v1 logic
|
|
||||||
const credentials = await this.getCredentials('skyvernApi');
|
const credentials = await this.getCredentials('skyvernApi');
|
||||||
const userPrompt = this.getNodeParameter('userPrompt');
|
const userPrompt = this.getNodeParameter('userPrompt');
|
||||||
|
|
||||||
|
// *** capture optional webhook URL ***
|
||||||
|
let webhookUrl: string | undefined;
|
||||||
|
try {
|
||||||
|
webhookUrl = this.getNodeParameter('webhookUrl') as string;
|
||||||
|
} catch (e) {
|
||||||
|
webhookUrl = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateBody: IDataObject = {
|
||||||
|
prompt: userPrompt,
|
||||||
|
};
|
||||||
|
if (webhookUrl) {
|
||||||
|
generateBody['webhook_callback_url'] = webhookUrl; // include for TaskV1 generation
|
||||||
|
}
|
||||||
|
|
||||||
const response = await makeRequest(credentials['baseUrl'] + '/api/v1/generate/task', {
|
const response = await makeRequest(credentials['baseUrl'] + '/api/v1/generate/task', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'x-api-key': credentials['apiKey'],
|
'x-api-key': credentials['apiKey'],
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(generateBody),
|
||||||
prompt: userPrompt,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Request to generate Task V1 failed'); // eslint-disable-line
|
throw new Error('Request to generate Task V1 failed'); // eslint-disable-line
|
||||||
@@ -159,7 +171,11 @@ export class Skyvern implements INodeType {
|
|||||||
navigation_payload: data.navigation_payload,
|
navigation_payload: data.navigation_payload,
|
||||||
data_extraction_goal: data.data_extraction_goal,
|
data_extraction_goal: data.data_extraction_goal,
|
||||||
extracted_information_schema: data.extracted_information_schema,
|
extracted_information_schema: data.extracted_information_schema,
|
||||||
};
|
} as IDataObject;
|
||||||
|
|
||||||
|
if (webhookUrl) {
|
||||||
|
(requestOptions.body as IDataObject)['webhook_callback_url'] = webhookUrl;
|
||||||
|
}
|
||||||
return requestOptions;
|
return requestOptions;
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -209,6 +225,28 @@ export class Skyvern implements INodeType {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// *** New property: optional webhook URL for Task dispatch ***
|
||||||
|
{
|
||||||
|
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'],
|
||||||
|
taskOperation: ['dispatch'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
routing: {
|
||||||
|
request: {
|
||||||
|
body: {
|
||||||
|
webhook_callback_url: '={{$value ? $value : undefined}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Task ID',
|
displayName: 'Task ID',
|
||||||
description: 'The ID of the task',
|
description: 'The ID of the task',
|
||||||
@@ -365,10 +403,32 @@ export class Skyvern implements INodeType {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// *** New property: optional webhook URL for Workflow dispatch ***
|
||||||
|
{
|
||||||
|
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'],
|
||||||
|
workflowOperation: ['dispatch'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
routing: {
|
||||||
|
request: {
|
||||||
|
body: {
|
||||||
|
webhook_callback_url: '={{$value ? $value : undefined}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
version: 1,
|
version: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
methods = {
|
methods = {
|
||||||
loadOptions: {
|
loadOptions: {
|
||||||
async getWorkflows(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
async getWorkflows(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
@@ -398,7 +458,7 @@ export class Skyvern implements INodeType {
|
|||||||
|
|
||||||
const workflowOperation = this.getCurrentNodeParameter('workflowOperation') as string;
|
const workflowOperation = this.getCurrentNodeParameter('workflowOperation') as string;
|
||||||
if (workflowOperation !== 'dispatch') return { fields: [] };
|
if (workflowOperation !== 'dispatch') return { fields: [] };
|
||||||
|
|
||||||
const workflowId = this.getCurrentNodeParameter('workflowId') as string;
|
const workflowId = this.getCurrentNodeParameter('workflowId') as string;
|
||||||
if (!workflowId) return { fields: [] };
|
if (!workflowId) return { fields: [] };
|
||||||
|
|
||||||
@@ -415,48 +475,49 @@ export class Skyvern implements INodeType {
|
|||||||
const parameters: any[] = workflow.workflow_definition.parameters;
|
const parameters: any[] = workflow.workflow_definition.parameters;
|
||||||
|
|
||||||
const fields: ResourceMapperField[] = await Promise.all(
|
const fields: ResourceMapperField[] = await Promise.all(
|
||||||
parameters.filter((parameter: any) => parameter.parameter_type === 'workflow' || parameter.parameter_type === 'credential')
|
parameters
|
||||||
.map(async (parameter: any) => {
|
.filter((parameter: any) => parameter.parameter_type === 'workflow' || parameter.parameter_type === 'credential')
|
||||||
let options: INodePropertyOptions[] | undefined = undefined;
|
.map(async (parameter: any) => {
|
||||||
let parameterType: FieldType | undefined = undefined;
|
let options: INodePropertyOptions[] | undefined = undefined;
|
||||||
if (parameter.parameter_type === 'credential') {
|
let parameterType: FieldType | undefined = undefined;
|
||||||
const response = await makeRequest(credentials['baseUrl'] + '/api/v1/credentials', {
|
if (parameter.parameter_type === 'credential') {
|
||||||
headers: {
|
const credResponse = await makeRequest(credentials['baseUrl'] + '/api/v1/credentials', {
|
||||||
'x-api-key': credentials['apiKey'],
|
headers: {
|
||||||
},
|
'x-api-key': credentials['apiKey'],
|
||||||
});
|
},
|
||||||
if (!response.ok) {
|
});
|
||||||
throw new Error('Request to get credentials failed'); // eslint-disable-line
|
if (!credResponse.ok) {
|
||||||
|
throw new Error('Request to get credentials failed');
|
||||||
|
}
|
||||||
|
const credData = await credResponse.json();
|
||||||
|
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',
|
||||||
|
json: 'json',
|
||||||
|
file_url: 'url',
|
||||||
|
}
|
||||||
|
parameterType = parameter_type_map[parameter.workflow_parameter_type];
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
|
||||||
options = data.map((credential: any) => ({
|
|
||||||
name: credential.name,
|
|
||||||
value: credential.credential_id,
|
|
||||||
}));
|
|
||||||
parameterType = 'options';
|
|
||||||
}else{
|
|
||||||
const parameter_type_map: Record<string, string> = {
|
|
||||||
'string': 'string',
|
|
||||||
'integer': 'number',
|
|
||||||
'float': 'number',
|
|
||||||
'boolean': 'boolean',
|
|
||||||
'json': 'json',
|
|
||||||
'file_url': 'url',
|
|
||||||
}
|
|
||||||
parameterType = parameter_type_map[parameter.workflow_parameter_type] as FieldType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: parameter.key,
|
id: parameter.key,
|
||||||
displayName: parameter.key,
|
displayName: parameter.key,
|
||||||
defaultMatch: true,
|
defaultMatch: true,
|
||||||
canBeUsedToMatch: false,
|
canBeUsedToMatch: false,
|
||||||
required: parameter.default_value === undefined || parameter.default_value === null,
|
required: parameter.default_value === undefined || parameter.default_value === null,
|
||||||
display: true,
|
display: true,
|
||||||
type: parameterType,
|
type: parameterType,
|
||||||
options: options,
|
options: options,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user