migrate n8n integration (#3004)
This commit is contained in:
30
.github/workflows/n8n-ci.yml
vendored
30
.github/workflows/n8n-ci.yml
vendored
@@ -1,30 +0,0 @@
|
|||||||
name: Run n8n package CI
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- 'integrations/n8n/**'
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- 'integrations/n8n/**'
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: .nvmrc
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v2
|
|
||||||
with:
|
|
||||||
version: 9
|
|
||||||
run_install: false
|
|
||||||
- name: Install dependencies
|
|
||||||
working-directory: integrations/n8n
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
- name: Build and Lint
|
|
||||||
working-directory: integrations/n8n
|
|
||||||
run: pnpm prepublishOnly
|
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "integrations/n8n"]
|
||||||
|
path = integrations/n8n
|
||||||
|
url = https://github.com/Skyvern-AI/skyvern-n8n
|
||||||
1
integrations/n8n
Submodule
1
integrations/n8n
Submodule
Submodule integrations/n8n added at c14752f27a
@@ -1,20 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[package.json]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
||||||
[*.yml]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/**
|
|
||||||
* @type {import('@types/eslint').ESLint.ConfigData}
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es6: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
parser: "@typescript-eslint/parser",
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
project: ["./tsconfig.json"],
|
|
||||||
sourceType: "module",
|
|
||||||
extraFileExtensions: [".json"],
|
|
||||||
},
|
|
||||||
|
|
||||||
ignorePatterns: [
|
|
||||||
".eslintrc.js",
|
|
||||||
"**/*.js",
|
|
||||||
"**/node_modules/**",
|
|
||||||
"**/dist/**",
|
|
||||||
],
|
|
||||||
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["package.json"],
|
|
||||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
|
||||||
extends: ["plugin:n8n-nodes-base/community"],
|
|
||||||
rules: {
|
|
||||||
"n8n-nodes-base/community-package-json-name-still-default": "off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ["./credentials/**/*.ts"],
|
|
||||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
|
||||||
extends: ["plugin:n8n-nodes-base/credentials"],
|
|
||||||
rules: {
|
|
||||||
"n8n-nodes-base/cred-class-field-documentation-url-missing": "off",
|
|
||||||
"n8n-nodes-base/cred-class-field-documentation-url-miscased": "off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ["./nodes/**/*.ts"],
|
|
||||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
|
||||||
extends: ["plugin:n8n-nodes-base/nodes"],
|
|
||||||
rules: {
|
|
||||||
"n8n-nodes-base/node-execute-block-missing-continue-on-fail": "off",
|
|
||||||
"n8n-nodes-base/node-resource-description-filename-against-convention":
|
|
||||||
"off",
|
|
||||||
"n8n-nodes-base/node-param-fixed-collection-type-unsorted-items": "off",
|
|
||||||
"n8n-nodes-base/node-class-description-inputs-wrong-regular-node":
|
|
||||||
"off",
|
|
||||||
"n8n-nodes-base/node-class-description-outputs-wrong": "off",
|
|
||||||
"n8n-nodes-base/node-param-description-wrong-for-dynamic-options":
|
|
||||||
"off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* @type {import('@types/eslint').ESLint.ConfigData}
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
extends: "./.eslintrc.js",
|
|
||||||
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["package.json"],
|
|
||||||
plugins: ["eslint-plugin-n8n-nodes-base"],
|
|
||||||
rules: {
|
|
||||||
"n8n-nodes-base/community-package-json-name-still-default": "error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
8
integrations/n8n/.gitignore
vendored
8
integrations/n8n/.gitignore
vendored
@@ -1,8 +0,0 @@
|
|||||||
node_modules
|
|
||||||
.DS_Store
|
|
||||||
.tmp
|
|
||||||
tmp
|
|
||||||
dist
|
|
||||||
npm-debug.log*
|
|
||||||
yarn.lock
|
|
||||||
.vscode/launch.json
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
*.tsbuildinfo
|
|
||||||
node_modules/
|
|
||||||
dist/
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#semicolons
|
|
||||||
*/
|
|
||||||
semi: true,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#trailing-commas
|
|
||||||
*/
|
|
||||||
trailingComma: "all",
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#bracket-spacing
|
|
||||||
*/
|
|
||||||
bracketSpacing: true,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#tabs
|
|
||||||
*/
|
|
||||||
useTabs: true,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#tab-width
|
|
||||||
*/
|
|
||||||
tabWidth: 2,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#arrow-function-parentheses
|
|
||||||
*/
|
|
||||||
arrowParens: "always",
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#quotes
|
|
||||||
*/
|
|
||||||
singleQuote: true,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#quote-props
|
|
||||||
*/
|
|
||||||
quoteProps: "as-needed",
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#end-of-line
|
|
||||||
*/
|
|
||||||
endOfLine: "lf",
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://prettier.io/docs/en/options.html#print-width
|
|
||||||
*/
|
|
||||||
printWidth: 100,
|
|
||||||
};
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
||||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
||||||
|
|
||||||
- [Contributor Covenant Code of Conduct](#contributor-covenant-code-of-conduct)
|
|
||||||
- [Our Pledge](#our-pledge)
|
|
||||||
- [Our Standards](#our-standards)
|
|
||||||
- [Our Responsibilities](#our-responsibilities)
|
|
||||||
- [Scope](#scope)
|
|
||||||
- [Enforcement](#enforcement)
|
|
||||||
- [Attribution](#attribution)
|
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
||||||
|
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to making participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
||||||
level of experience, education, socio-economic status, nationality, personal
|
|
||||||
appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community. Examples of
|
|
||||||
representing a project or community include using an official project e-mail
|
|
||||||
address, posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event. Representation of a project may be
|
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at jan@n8n.io. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see
|
|
||||||
https://www.contributor-covenant.org/faq
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
Copyright 2022 n8n
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
||||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
||||||
|
|
||||||
- [n8n-nodes-skyvern](#n8n-nodes-skyvern)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [Operations](#operations)
|
|
||||||
- [Credentials](#credentials)
|
|
||||||
- [Resources](#resources)
|
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
||||||
|
|
||||||
# n8n-nodes-skyvern
|
|
||||||
|
|
||||||
This is an n8n community node. It lets you use [Skyvern](https://www.skyvern.com/) in your n8n workflows.
|
|
||||||
|
|
||||||
[Skyvern](https://www.skyvern.com/) is an [open-source](https://github.com/Skyvern-AI/skyvern) product. It automates browser-based workflows using LLMs and computer vision. It provides a simple API endpoint to fully automate manual workflows on a large number of websites, replacing brittle or unreliable automation solutions.
|
|
||||||
|
|
||||||
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
|
|
||||||
|
|
||||||
[Installation](#installation)
|
|
||||||
[Operations](#operations)
|
|
||||||
[Credentials](#credentials)
|
|
||||||
[Resources](#resources)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
|
|
||||||
|
|
||||||
## Operations
|
|
||||||
|
|
||||||
- Task: interact with Skyvern task
|
|
||||||
- Workflow: interact with Skyvern workflow
|
|
||||||
|
|
||||||
## Credentials
|
|
||||||
|
|
||||||
- API Token: Skyvern API token. You can get it from the Skyvern UI
|
|
||||||
- Base URL: The endpoint for your Skyvern service. Default is Skyvern Cloud Service, you can also set it to your self-host Skyvern.
|
|
||||||
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
* [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
|
|
||||||
* [Skyvern documentation](https://docs.skyvern.com/introduction)
|
|
||||||
|
|
||||||
* [n8n integration code](https://github.com/Skyvern-AI/skyvern/tree/main/integrations/n8n)
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import {
|
|
||||||
IAuthenticateGeneric,
|
|
||||||
ICredentialTestRequest,
|
|
||||||
ICredentialType,
|
|
||||||
INodeProperties,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
|
|
||||||
export class SkyvernApi implements ICredentialType {
|
|
||||||
name = 'skyvernApi';
|
|
||||||
displayName = 'Skyvern API';
|
|
||||||
// Uses the link to this tutorial as an example
|
|
||||||
// Replace with your own docs links when building your own nodes
|
|
||||||
documentationUrl = 'https://docs.skyvern.ai/';
|
|
||||||
properties: INodeProperties[] = [
|
|
||||||
{
|
|
||||||
displayName: 'API Key',
|
|
||||||
name: 'apiKey',
|
|
||||||
type: 'string',
|
|
||||||
typeOptions: { password: true },
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Base URL',
|
|
||||||
name: 'baseUrl',
|
|
||||||
type: 'string',
|
|
||||||
default: 'https://api.skyvern.com',
|
|
||||||
placeholder: 'https://api.skyvern.com',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
authenticate: IAuthenticateGeneric = {
|
|
||||||
type: 'generic',
|
|
||||||
properties: {
|
|
||||||
headers: {
|
|
||||||
'x-api-key': '={{$credentials.apiKey}}',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
test: ICredentialTestRequest = {
|
|
||||||
request: {
|
|
||||||
baseURL: '={{$credentials?.baseUrl}}',
|
|
||||||
url: '/api/v1/organizations',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
const { task, src, dest } = require("gulp");
|
|
||||||
|
|
||||||
task("build:icons", copyIcons);
|
|
||||||
|
|
||||||
function copyIcons() {
|
|
||||||
const nodeSource = path.resolve("nodes", "**", "*.{png,svg}");
|
|
||||||
const nodeDestination = path.resolve("dist", "nodes");
|
|
||||||
|
|
||||||
src(nodeSource).pipe(dest(nodeDestination));
|
|
||||||
|
|
||||||
const credSource = path.resolve("credentials", "**", "*.{png,svg}");
|
|
||||||
const credDestination = path.resolve("dist", "credentials");
|
|
||||||
|
|
||||||
return src(credSource).pipe(dest(credDestination));
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"node": "n8n-nodes-skyvern",
|
|
||||||
"nodeVersion": "1.0",
|
|
||||||
"codexVersion": "1.0",
|
|
||||||
"categories": [
|
|
||||||
"Miscellaneous",
|
|
||||||
"Utility"
|
|
||||||
],
|
|
||||||
"resources": {
|
|
||||||
"credentialDocumentation": [
|
|
||||||
{
|
|
||||||
"url": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"primaryDocumentation": [
|
|
||||||
{
|
|
||||||
"url": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,508 +0,0 @@
|
|||||||
import {
|
|
||||||
FieldType,
|
|
||||||
IDataObject,
|
|
||||||
IExecuteSingleFunctions,
|
|
||||||
IHttpRequestMethods,
|
|
||||||
IHttpRequestOptions,
|
|
||||||
ILoadOptionsFunctions,
|
|
||||||
INodePropertyOptions,
|
|
||||||
INodeType,
|
|
||||||
INodeTypeDescription,
|
|
||||||
NodeConnectionType,
|
|
||||||
ResourceMapperField,
|
|
||||||
ResourceMapperFields,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Skyvern implements INodeType {
|
|
||||||
description: INodeTypeDescription = {
|
|
||||||
displayName: 'Skyvern',
|
|
||||||
name: 'skyvern',
|
|
||||||
icon: 'file:skyvern.svg',
|
|
||||||
group: ['transform'],
|
|
||||||
description: 'Node to interact with Skyvern',
|
|
||||||
defaults: {
|
|
||||||
name: 'Skyvern',
|
|
||||||
},
|
|
||||||
inputs: [NodeConnectionType.Main],
|
|
||||||
outputs: [NodeConnectionType.Main],
|
|
||||||
credentials: [
|
|
||||||
{
|
|
||||||
name: 'skyvernApi',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
properties: [
|
|
||||||
{
|
|
||||||
displayName: 'Resource',
|
|
||||||
name: 'resource',
|
|
||||||
type: 'options',
|
|
||||||
noDataExpression: true,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'Task',
|
|
||||||
value: 'task',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Workflow',
|
|
||||||
value: 'workflow',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
default: 'task',
|
|
||||||
},
|
|
||||||
// Task Operations
|
|
||||||
{
|
|
||||||
displayName: 'Operation',
|
|
||||||
name: 'taskOperation',
|
|
||||||
type: 'options',
|
|
||||||
noDataExpression: true,
|
|
||||||
required: true,
|
|
||||||
default: 'dispatchTask',
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['task'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'Dispatch a Task',
|
|
||||||
value: 'dispatchTask',
|
|
||||||
action: 'Dispatch a task to execute asynchronously',
|
|
||||||
description: 'Dispatch a task to execute asynchronously',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Get a Task',
|
|
||||||
value: 'getTask',
|
|
||||||
action: 'Get a task by ID',
|
|
||||||
description: 'Get a task by ID',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
baseURL: '={{$credentials.baseUrl}}',
|
|
||||||
method: '={{ $value === "dispatchTask" ? "POST" : "GET" }}' as IHttpRequestMethods,
|
|
||||||
url: '={{"/v1/run/tasks"}}',
|
|
||||||
},
|
|
||||||
send: {
|
|
||||||
preSend: [
|
|
||||||
async function (
|
|
||||||
this: IExecuteSingleFunctions,
|
|
||||||
requestOptions: IHttpRequestOptions,
|
|
||||||
): Promise<IHttpRequestOptions> {
|
|
||||||
const taskOperation = this.getNodeParameter('taskOperation');
|
|
||||||
if (taskOperation === "getTask") return requestOptions;
|
|
||||||
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Workflow Operations
|
|
||||||
{
|
|
||||||
displayName: 'Operation',
|
|
||||||
name: 'workflowOperation',
|
|
||||||
type: 'options',
|
|
||||||
noDataExpression: true,
|
|
||||||
required: true,
|
|
||||||
default: 'getWorkflow',
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['workflow'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'Get a Workflow Run',
|
|
||||||
value: 'getWorkflow',
|
|
||||||
action: 'Get a workflow run by ID',
|
|
||||||
description: 'Get a workflow run by ID',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Dispatch a Workflow Run',
|
|
||||||
value: 'dispatchWorkflow',
|
|
||||||
action: 'Dispatch a workflow run to execute asynchronously',
|
|
||||||
description: 'Dispatch a workflow run to execute asynchronously',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
baseURL: '={{$credentials.baseUrl}}',
|
|
||||||
method: '={{ $value === "dispatchWorkflow" ? "POST" : "GET" }}' as IHttpRequestMethods,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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'],
|
|
||||||
taskOperation: ['dispatchTask'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
body: {
|
|
||||||
prompt: '={{$value}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'URL',
|
|
||||||
description: 'The URL to navigate to',
|
|
||||||
name: 'url',
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
placeholder: 'eg: https://news.ycombinator.com/',
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['task'],
|
|
||||||
taskOperation: ['dispatchTask'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
body: {
|
|
||||||
url: '={{$value ? $value : null}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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: ['dispatchTask'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
body: {
|
|
||||||
webhook_url: '={{$value ? $value : null}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Task ID',
|
|
||||||
description: 'The ID of the task',
|
|
||||||
name: 'taskId',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
default: '',
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['task'],
|
|
||||||
taskOperation: ['getTask'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
method: 'GET',
|
|
||||||
url: '={{"/v1/runs/" + $value}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Task Options',
|
|
||||||
name: 'taskOptions',
|
|
||||||
type: 'collection',
|
|
||||||
description: 'Optional Configuration for the task',
|
|
||||||
placeholder: 'Add Task Options',
|
|
||||||
default: {},
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: 'Engine(Deprecated)',
|
|
||||||
description: 'Deprecated: please migrate to use "Engine" option',
|
|
||||||
name: 'engine',
|
|
||||||
type: 'options',
|
|
||||||
default: '',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'TaskV1',
|
|
||||||
value: 'v1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TaskV2',
|
|
||||||
value: 'v2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'THIS FIELD IS DEPRECATED',
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['task'],
|
|
||||||
taskOperation: ['dispatchTask'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Workflow Name or ID',
|
|
||||||
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/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'],
|
|
||||||
workflowOperation: ['getWorkflow'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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'],
|
|
||||||
workflowOperation: ['dispatchWorkflow'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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"]}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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: ['dispatchWorkflow'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
body: {
|
|
||||||
webhook_callback_url: '={{$value ? $value : null}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
version: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
methods = {
|
|
||||||
loadOptions: {
|
|
||||||
async getWorkflows(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
||||||
const resource = this.getCurrentNodeParameter('resource') as string;
|
|
||||||
if (resource !== 'workflow') return [];
|
|
||||||
|
|
||||||
const response = await skyvernApiRequest.call(
|
|
||||||
this,
|
|
||||||
'GET',
|
|
||||||
'/api/v1/workflows?page_size=100',
|
|
||||||
);
|
|
||||||
const data = response;
|
|
||||||
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: [] };
|
|
||||||
|
|
||||||
const operation = this.getCurrentNodeParameter('workflowOperation') as string;
|
|
||||||
if (operation !== 'dispatchWorkflow') return { fields: [] };
|
|
||||||
|
|
||||||
const workflowId = this.getCurrentNodeParameter('workflowId') as string;
|
|
||||||
if (!workflowId) return { fields: [] };
|
|
||||||
|
|
||||||
const workflow = await skyvernApiRequest.call(
|
|
||||||
this,
|
|
||||||
'GET',
|
|
||||||
`/api/v1/workflows/${workflowId}`,
|
|
||||||
);
|
|
||||||
const parameters: any[] = workflow.workflow_definition.parameters;
|
|
||||||
|
|
||||||
const fields: ResourceMapperField[] = await Promise.all(
|
|
||||||
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') {
|
|
||||||
const credData = await skyvernApiRequest.call(
|
|
||||||
this,
|
|
||||||
'GET',
|
|
||||||
'/api/v1/credentials',
|
|
||||||
);
|
|
||||||
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: 'object',
|
|
||||||
file_url: 'url',
|
|
||||||
}
|
|
||||||
parameterType = parameter_type_map[parameter.workflow_parameter_type];
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 132 KiB |
8405
integrations/n8n/package-lock.json
generated
8405
integrations/n8n/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,59 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "n8n-nodes-skyvern",
|
|
||||||
"version": "0.0.8",
|
|
||||||
"description": "Skyvern Node for n8n",
|
|
||||||
"keywords": [
|
|
||||||
"n8n-community-node-package"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"homepage": "",
|
|
||||||
"author": {
|
|
||||||
"name": "lawy",
|
|
||||||
"email": "lawy@skyvern.com"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/Skyvern-AI/skyvern.git"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.10",
|
|
||||||
"pnpm": ">=9.1"
|
|
||||||
},
|
|
||||||
"packageManager": "pnpm@9.1.4",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"preinstall": "npx only-allow pnpm",
|
|
||||||
"build": "rimraf dist && tsc && gulp build:icons",
|
|
||||||
"dev": "tsc --watch",
|
|
||||||
"format": "prettier nodes credentials --write",
|
|
||||||
"lint": "eslint nodes credentials package.json",
|
|
||||||
"lintfix": "eslint nodes credentials package.json --fix",
|
|
||||||
"prepublishOnly": "pnpm build && pnpm lint -c .eslintrc.prepublish.js nodes credentials package.json"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"n8n": {
|
|
||||||
"n8nNodesApiVersion": 1,
|
|
||||||
"credentials": [
|
|
||||||
"dist/credentials/SkyvernApi.credentials.js"
|
|
||||||
],
|
|
||||||
"nodes": [
|
|
||||||
"dist/nodes/Skyvern/Skyvern.node.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^22.13.10",
|
|
||||||
"@typescript-eslint/parser": "^7.15.0",
|
|
||||||
"braces": "^3.0.3",
|
|
||||||
"eslint": "^8.56.0",
|
|
||||||
"eslint-plugin-n8n-nodes-base": "^1.16.1",
|
|
||||||
"gulp": "^4.0.2",
|
|
||||||
"prettier": "^3.3.2",
|
|
||||||
"rimraf": "^6.0.1",
|
|
||||||
"typescript": "^5.5.3"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"n8n-workflow": "^1.82.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3482
integrations/n8n/pnpm-lock.yaml
generated
3482
integrations/n8n/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"strict": true,
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es2019",
|
|
||||||
"lib": ["es2019", "es2020", "es2022.error"],
|
|
||||||
"removeComments": true,
|
|
||||||
"useUnknownInCatchVariables": false,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"strictNullChecks": true,
|
|
||||||
"preserveConstEnums": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"incremental": true,
|
|
||||||
"declaration": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"outDir": "./dist/",
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"credentials/**/*",
|
|
||||||
"nodes/**/*",
|
|
||||||
"nodes/**/*.json",
|
|
||||||
"package.json",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user