@steprotech/n8n-nodes-composio 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  An n8n node for [Composio](https://composio.dev). Connect your own apps through Composio, choose an
4
4
  action, and run it in any workflow. No AI agent is required, and the node can also be used as a tool
5
- inside an agent.
5
+ inside an agent. A trigger node starts workflows from app events too.
6
6
 
7
7
  Composio is an integration and authentication layer: it holds the OAuth grants for around 1,600
8
8
  apps, refreshes the tokens, and executes the action on your behalf. This node is the n8n front door
@@ -36,26 +36,67 @@ Never paste a key into a chat, an issue or a repository. Keys belong in n8n's cr
36
36
 
37
37
  ## Operations
38
38
 
39
+ Every app and every action is chosen from a **live search list**, so you never need to know a Composio
40
+ slug. The inputs of an action are read from the action's own schema, so you pick parameter names from a
41
+ list instead of writing JSON. Raw JSON is still there as an opt-in escape hatch.
42
+
39
43
  **Connection**
40
44
 
41
45
  - **Connect an App** returns the authorization link. Open it, approve in the browser, and the app is
42
46
  connected to your Composio account. Managed auth means you never create an OAuth app yourself.
43
- - **Wait for Connection** pauses until you finish authorizing, so the next node can rely on it.
44
- - **List Connections** shows which apps are connected.
45
- - **Remove Connection** disconnects an app and deletes its stored credentials.
47
+ - **Check Connections** reports which of the apps you name are connected. Any app that is not connected
48
+ yet gets an authorization link in the output, so this doubles as "connect these three apps".
49
+ - **Reconnect an App** replaces an existing connection with a fresh authorization flow, which is what
50
+ you want when credentials go stale or when you need to re-consent after a permission change.
51
+
52
+ Disconnecting an app is not exposed on the consumer surface this node uses, so removal stays in the
53
+ Composio dashboard.
46
54
 
47
55
  **Action**
48
56
 
49
- - **Search Actions** takes what you want to do in plain words and returns matching actions.
50
- - **Get Action Schemas** fetches the input schema for an action slug, so you know which fields to send.
51
- - **Execute an Action** runs an action slug with JSON arguments against the connected account. It can
52
- also be used as a tool by an AI Agent.
57
+ - **Search Actions** takes what you want to do in plain words and returns matching actions, which app
58
+ each belongs to, and which of those apps are already connected.
59
+ - **Get Action Inputs** lists the inputs one or more actions expect, with types and required markers.
60
+ - **Execute an Action** runs the chosen action. Set **Set Inputs** to *Using Fields* and add one row per
61
+ input, picking the parameter name from the list, or switch to *Using JSON* to write the arguments
62
+ object yourself. The node can also be used as a tool by an AI Agent.
63
+
64
+ Values typed into the fields are converted sensibly: `true`, `false`, `null`, numbers and JSON objects
65
+ or arrays are sent as those types, while identifiers keep their form, so a phone number entered as
66
+ `0244123456` is not turned into a number.
53
67
 
54
68
  **Advanced**
55
69
 
56
- - **Call a Meta-Tool** sends any of Composio's seven meta-tools with arguments you supply. Composio
57
- has thousands of actions with their own schemas, while n8n node parameters are fixed, so this
58
- operation is the escape hatch if an action needs something the other operations do not cover yet.
70
+ - **Call a Meta-Tool** sends any of Composio's meta-tools with arguments you supply. Composio has
71
+ thousands of actions with their own schemas, while n8n node parameters are fixed, so this is the
72
+ escape hatch for anything the operations above do not cover yet.
73
+
74
+ ## Documentation
75
+
76
+ - `docs/INSTALL-OPTIONS.md` covers every way to install or update it, including troubleshooting.
77
+ - `docs/PUBLISHING.md` covers the release loop, CI, and the tokenless trusted-publishing path.
78
+ - `docs/PUBLISH-FROM-WINDOWS.md` is the PowerShell route for publishing from your own machine.
79
+
80
+ ## Trigger
81
+
82
+ The **Composio Connect Trigger** starts a workflow when something happens in a connected app, such as
83
+ a new Gmail message or a new GitHub commit.
84
+
85
+ 1. In Composio, note the single **webhook URL** your project delivers events to, and register the URL
86
+ n8n shows in the trigger node. Composio sends every project event to one URL, so this is a
87
+ per-project setting, not a per-workflow one.
88
+ 2. Copy the webhook **signing secret** from that subscription into the credential's *Webhook Signing
89
+ Secret* field.
90
+ 3. Choose the events you want. `Trigger Message` covers app events; `Connected Account Expired` tells
91
+ you when a connection needs re-authorizing. Use `Only These Triggers` to accept a single trigger
92
+ slug such as `GITHUB_COMMIT_EVENT`.
93
+
94
+ Signature verification is **on by default**. Every request carries `webhook-id`, `webhook-timestamp`
95
+ and `webhook-signature`, and the trigger recomputes an HMAC-SHA256 over
96
+ `{webhook-id}.{webhook-timestamp}.{body}`, compares it in constant time, and refuses anything older
97
+ than the tolerance (300 seconds by default) so a captured payload cannot be replayed. Note this while
98
+ writing it down: with verification on and no secret in the credential, the trigger refuses to run
99
+ rather than trusting the request, which is deliberate.
59
100
 
60
101
  ## Composio's meta-tools
61
102
 
@@ -31,6 +31,14 @@ class ComposioApi {
31
31
  default: '',
32
32
  description: 'Optional. The Composio user ID whose connections this credential uses by default. Composio derives one from the key when this is left empty. Use separate credentials if you keep several users apart.',
33
33
  },
34
+ {
35
+ displayName: 'Webhook Signing Secret',
36
+ name: 'webhookSecret',
37
+ type: 'string',
38
+ typeOptions: { password: true },
39
+ default: '',
40
+ description: 'Optional. The signing secret of your Composio webhook subscription, used by the Composio Trigger to verify that events really came from Composio. Copy it from your Composio webhook subscription settings.',
41
+ },
34
42
  ];
35
43
  this.authenticate = {
36
44
  type: 'generic',
@@ -58,7 +66,7 @@ class ComposioApi {
58
66
  capabilities: {},
59
67
  clientInfo: {
60
68
  name: 'n8n-nodes-composio',
61
- version: '0.1.0',
69
+ version: '0.1.1',
62
70
  },
63
71
  },
64
72
  },
@@ -1 +1 @@
1
- {"version":3,"file":"ComposioApi.credentials.js","sourceRoot":"","sources":["../../credentials/ComposioApi.credentials.ts"],"names":[],"mappings":";;;AAEA,MAAa,WAAW;IAAxB;QACC,SAAI,GAAG,aAAa,CAAC;QAErB,gBAAW,GAAG,cAAc,CAAC;QAE7B,qBAAgB,GAAG,mFAAmF,CAAC;QAEvG,SAAI,GAAG,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,wBAAwB,EAAW,CAAC;QAE/E,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,cAAc;gBAC3B,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,2KAA2K;aAC5K;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,8BAA8B;gBACvC,WAAW,EAAE,2EAA2E;aACxF;YACD;gBACC,WAAW,EAAE,iBAAiB;gBAC9B,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EACV,wMAAwM;aACzM;SACD,CAAC;QAEF,iBAAY,GAAG;YACd,IAAI,EAAE,SAAkB;YACxB,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,oBAAoB,EAAE,0BAA0B;iBAChD;aACD;SACD,CAAC;QAEF,SAAI,GAAG;YACN,OAAO,EAAE;gBACR,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,MAAM;gBACX,MAAM,EAAE,MAAe;gBACvB,OAAO,EAAE;oBACR,MAAM,EAAE,qCAAqC;oBAC7C,cAAc,EAAE,kBAAkB;iBAClC;gBACD,IAAI,EAAE;oBACL,OAAO,EAAE,KAAK;oBACd,EAAE,EAAE,CAAC;oBACL,MAAM,EAAE,YAAY;oBACpB,MAAM,EAAE;wBACP,eAAe,EAAE,YAAY;wBAC7B,YAAY,EAAE,EAAE;wBAChB,UAAU,EAAE;4BACX,IAAI,EAAE,oBAAoB;4BAC1B,OAAO,EAAE,OAAO;yBAChB;qBACD;iBACD;aACD;SACD,CAAC;IACH,CAAC;CAAA;AAtED,kCAsEC"}
1
+ {"version":3,"file":"ComposioApi.credentials.js","sourceRoot":"","sources":["../../credentials/ComposioApi.credentials.ts"],"names":[],"mappings":";;;AAEA,MAAa,WAAW;IAAxB;QACC,SAAI,GAAG,aAAa,CAAC;QAErB,gBAAW,GAAG,cAAc,CAAC;QAE7B,qBAAgB,GAAG,mFAAmF,CAAC;QAEvG,SAAI,GAAG,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,wBAAwB,EAAW,CAAC;QAE/E,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,cAAc;gBAC3B,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,2KAA2K;aAC5K;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,8BAA8B;gBACvC,WAAW,EAAE,2EAA2E;aACxF;YACD;gBACC,WAAW,EAAE,iBAAiB;gBAC9B,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EACV,wMAAwM;aACzM;YACD;gBACC,WAAW,EAAE,wBAAwB;gBACrC,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EACV,6MAA6M;aAC9M;SACD,CAAC;QAEF,iBAAY,GAAG;YACd,IAAI,EAAE,SAAkB;YACxB,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,oBAAoB,EAAE,0BAA0B;iBAChD;aACD;SACD,CAAC;QAEF,SAAI,GAAG;YACN,OAAO,EAAE;gBACR,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,MAAM;gBACX,MAAM,EAAE,MAAe;gBACvB,OAAO,EAAE;oBACR,MAAM,EAAE,qCAAqC;oBAC7C,cAAc,EAAE,kBAAkB;iBAClC;gBACD,IAAI,EAAE;oBACL,OAAO,EAAE,KAAK;oBACd,EAAE,EAAE,CAAC;oBACL,MAAM,EAAE,YAAY;oBACpB,MAAM,EAAE;wBACP,eAAe,EAAE,YAAY;wBAC7B,YAAY,EAAE,EAAE;wBAChB,UAAU,EAAE;4BACX,IAAI,EAAE,oBAAoB;4BAC1B,OAAO,EAAE,OAAO;yBAChB;qBACD;iBACD;aACD;SACD,CAAC;IACH,CAAC;CAAA;AA/ED,kCA+EC"}
@@ -1,5 +1,12 @@
1
- import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
1
+ import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
2
  export declare class Composio implements INodeType {
3
3
  description: INodeTypeDescription;
4
+ methods: {
5
+ loadOptions: {
6
+ searchToolkits(this: ILoadOptionsFunctions, filter?: string): Promise<INodePropertyOptions[]>;
7
+ searchActions(this: ILoadOptionsFunctions, filter?: string): Promise<INodePropertyOptions[]>;
8
+ getToolParameters(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
9
+ };
10
+ };
4
11
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
12
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Composio = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
+ const catalog_1 = require("./catalog");
5
6
  const GenericFunctions_1 = require("./GenericFunctions");
6
7
  const metatools_1 = require("./metatools");
7
8
  const resourceOptions = {
@@ -18,7 +19,7 @@ const resourceOptions = {
18
19
  {
19
20
  name: 'Connection',
20
21
  value: 'connection',
21
- description: 'Connect an app, wait for it to finish, list connections or remove one',
22
+ description: 'Connect an app, check which apps are connected, or reconnect one',
22
23
  },
23
24
  {
24
25
  name: 'Advanced',
@@ -39,19 +40,19 @@ const operationOptions = [
39
40
  {
40
41
  name: 'Execute an Action',
41
42
  value: 'execute',
42
- description: 'Run a tool slug with arguments against the connected account',
43
+ description: 'Run the chosen action with the inputs you fill in',
43
44
  action: 'Execute an action',
44
45
  },
45
46
  {
46
- name: 'Get Action Schemas',
47
+ name: 'Get Action Inputs',
47
48
  value: 'schemas',
48
- description: 'Fetch the full input schemas for tool slugs',
49
- action: 'Get action schemas',
49
+ description: 'List the inputs one or more actions expect',
50
+ action: 'Get action inputs',
50
51
  },
51
52
  {
52
53
  name: 'Search Actions',
53
54
  value: 'search',
54
- description: 'Search the Composio catalog for actions matching a request',
55
+ description: 'Search the Composio catalog for actions matching what you want to do',
55
56
  action: 'Search actions',
56
57
  },
57
58
  ],
@@ -64,6 +65,12 @@ const operationOptions = [
64
65
  noDataExpression: true,
65
66
  displayOptions: { show: { resource: ['connection'] } },
66
67
  options: [
68
+ {
69
+ name: 'Check Connections',
70
+ value: 'check',
71
+ description: 'Report which of the chosen apps are connected',
72
+ action: 'Check connections',
73
+ },
67
74
  {
68
75
  name: 'Connect an App',
69
76
  value: 'connect',
@@ -71,22 +78,10 @@ const operationOptions = [
71
78
  action: 'Connect an app',
72
79
  },
73
80
  {
74
- name: 'Wait for Connection',
75
- value: 'wait',
76
- description: 'Wait until you finish authorizing the app in your browser',
77
- action: 'Wait for a connection',
78
- },
79
- {
80
- name: 'List Connections',
81
- value: 'list',
82
- description: 'List the apps already connected to this Composio account',
83
- action: 'List connections',
84
- },
85
- {
86
- name: 'Remove Connection',
87
- value: 'remove',
88
- description: 'Disconnect an app and delete its stored credentials',
89
- action: 'Remove a connection',
81
+ name: 'Reconnect an App',
82
+ value: 'reconnect',
83
+ description: 'Replace a connection with a fresh authorization flow',
84
+ action: 'Reconnect an app',
90
85
  },
91
86
  ],
92
87
  default: 'connect',
@@ -108,72 +103,124 @@ const operationOptions = [
108
103
  default: 'call',
109
104
  },
110
105
  ];
111
- const toolkitField = (displayName, description) => ({
112
- displayName,
113
- name: 'toolkit',
114
- type: 'string',
115
- default: '',
116
- required: true,
117
- description,
118
- placeholder: 'gmail',
119
- });
120
106
  const fields = [
121
107
  {
122
- ...toolkitField('App (Toolkit)', 'The app to connect, as a Composio toolkit slug such as gmail, slack, github or googlesheets. Use Action, Search Actions if you do not know the slug.'),
123
- displayOptions: { show: { resource: ['connection'], operation: ['connect', 'remove'] } },
108
+ displayName: 'App Name or ID',
109
+ name: 'toolkit',
110
+ type: 'options',
111
+ typeOptions: { loadOptionsMethod: 'searchToolkits' },
112
+ default: '',
113
+ required: true,
114
+ description: 'The app to work with. Type part of a name such as gmail or sheets and pick from the list. Apps you have already connected are marked. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
115
+ displayOptions: { show: { resource: ['connection'], operation: ['connect', 'reconnect'] } },
124
116
  },
125
117
  {
126
- displayName: 'Wait Timeout (Seconds)',
127
- name: 'timeout',
128
- type: 'number',
129
- default: 120,
130
- description: 'How long to wait for you to finish authorizing in the browser',
131
- displayOptions: { show: { resource: ['connection'], operation: ['wait'] } },
118
+ displayName: 'App Names or IDs',
119
+ name: 'toolkits',
120
+ type: 'multiOptions',
121
+ typeOptions: { loadOptionsMethod: 'searchToolkits' },
122
+ default: [],
123
+ required: true,
124
+ description: 'The apps to check. Search and add each one. Any app that is not connected yet gets an authorization link in the output. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
125
+ displayOptions: { show: { resource: ['connection'], operation: ['check'] } },
132
126
  },
133
127
  {
134
- displayName: 'Search Query',
135
- name: 'query',
128
+ displayName: 'Search For',
129
+ name: 'useCase',
136
130
  type: 'string',
137
131
  default: '',
138
132
  required: true,
139
- placeholder: 'send an email to a customer',
140
- description: 'What you want to do, in plain words. Composio returns matching actions and a plan.',
133
+ placeholder: 'send an email with an attachment',
134
+ description: 'What you want to do, in plain words. Composio returns matching actions, which app each belongs to, and which of those apps are already connected.',
141
135
  displayOptions: { show: { resource: ['action'], operation: ['search'] } },
142
136
  },
143
137
  {
144
- displayName: 'Tool Slugs',
145
- name: 'toolSlugs',
146
- type: 'string',
138
+ displayName: 'Action Name or ID',
139
+ name: 'toolSlug',
140
+ type: 'options',
141
+ typeOptions: { loadOptionsMethod: 'searchActions' },
147
142
  default: '',
148
143
  required: true,
149
- placeholder: 'GMAIL_SEND_EMAIL, GMAIL_FETCH_EMAILS',
150
- description: 'One or more action slugs, separated by commas. Get them from Search Actions.',
151
- displayOptions: { show: { resource: ['action'], operation: ['schemas'] } },
144
+ description: 'The action to run. Type part of what it does, or of its name, and pick from the list. Use Search Actions first if you are unsure. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
145
+ displayOptions: { show: { resource: ['action'], operation: ['execute'] } },
152
146
  },
153
147
  {
154
- displayName: 'Action Slug',
155
- name: 'toolSlug',
156
- type: 'string',
157
- default: '',
148
+ displayName: 'Action Names or IDs',
149
+ name: 'toolSlugs',
150
+ type: 'multiOptions',
151
+ typeOptions: { loadOptionsMethod: 'searchActions' },
152
+ default: [],
158
153
  required: true,
159
- placeholder: 'GMAIL_SEND_EMAIL',
160
- description: 'The action to run, as Composio names it. Get it from Search Actions.',
154
+ description: 'The actions whose inputs you want to see. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
155
+ displayOptions: { show: { resource: ['action'], operation: ['schemas'] } },
156
+ },
157
+ {
158
+ displayName: 'Set Inputs',
159
+ name: 'inputMode',
160
+ type: 'options',
161
+ options: [
162
+ {
163
+ name: 'Using Fields',
164
+ value: 'fields',
165
+ description: 'Pick each input by name and type its value, with no JSON to write',
166
+ },
167
+ {
168
+ name: 'Using JSON',
169
+ value: 'json',
170
+ description: 'Write the whole arguments object yourself',
171
+ },
172
+ ],
173
+ default: 'fields',
174
+ description: 'How to provide the action inputs',
161
175
  displayOptions: { show: { resource: ['action'], operation: ['execute'] } },
162
176
  },
177
+ {
178
+ displayName: 'Input Parameters',
179
+ name: 'parameterRows',
180
+ type: 'fixedCollection',
181
+ typeOptions: { multipleValues: true },
182
+ default: {},
183
+ placeholder: 'Add Input',
184
+ description: 'The inputs this action needs. Add one row per input and pick its name from the list, which is read from the action itself. Values that look like JSON, such as true, 42 or {"a":1}, are sent as JSON.',
185
+ displayOptions: { show: { resource: ['action'], operation: ['execute'], inputMode: ['fields'] } },
186
+ options: [
187
+ {
188
+ displayName: 'Input',
189
+ name: 'parameters',
190
+ values: [
191
+ {
192
+ displayName: 'Parameter Name or ID',
193
+ name: 'name',
194
+ type: 'options',
195
+ typeOptions: { loadOptionsMethod: 'getToolParameters' },
196
+ default: '',
197
+ description: 'Which input of the action to set. The list is read from the action chosen above, and marks the required ones. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
198
+ },
199
+ {
200
+ displayName: 'Value',
201
+ name: 'value',
202
+ type: 'string',
203
+ default: '',
204
+ description: 'The value to send. Numbers, true, false, null, and JSON objects or arrays are recognised automatically. Everything else is sent as text.',
205
+ },
206
+ ],
207
+ },
208
+ ],
209
+ },
163
210
  {
164
211
  displayName: 'Arguments (JSON)',
165
212
  name: 'arguments',
166
213
  type: 'json',
167
214
  default: '{}',
168
- description: 'The inputs for this action, as JSON. Run Get Action Schemas to see which fields it expects.',
169
- displayOptions: { show: { resource: ['action'], operation: ['execute'] } },
215
+ description: 'The full arguments object, exactly as the action expects it',
216
+ displayOptions: { show: { resource: ['action'], operation: ['execute'], inputMode: ['json'] } },
170
217
  },
171
218
  {
172
- displayName: 'App (Toolkit)',
173
- name: 'toolkit',
174
- type: 'string',
175
- default: '',
176
- description: 'Optional. Restrict the action to this toolkit, for example gmail.',
219
+ displayName: 'Save Large Responses in the Workbench',
220
+ name: 'syncToWorkbench',
221
+ type: 'boolean',
222
+ default: false,
223
+ description: 'Whether Composio should keep the full response in its workbench and return only a preview, which helps when an action returns something big',
177
224
  displayOptions: { show: { resource: ['action'], operation: ['execute'] } },
178
225
  },
179
226
  {
@@ -204,20 +251,90 @@ const fields = [
204
251
  class Composio {
205
252
  constructor() {
206
253
  this.description = {
207
- displayName: 'Composio',
254
+ displayName: 'Composio Connect',
208
255
  name: 'composio',
209
256
  icon: { light: 'file:composio.svg', dark: 'file:composio.dark.svg' },
210
257
  group: ['transform'],
211
258
  version: [1],
212
259
  subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
213
260
  description: 'Connect your own apps through Composio and run their actions in any workflow',
214
- defaults: { name: 'Composio' },
261
+ defaults: { name: 'Composio Connect' },
215
262
  usableAsTool: true,
216
263
  inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
217
264
  outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
218
265
  credentials: [{ name: 'composioApi', required: true }],
219
266
  properties: [resourceOptions, ...operationOptions, ...fields],
220
267
  };
268
+ this.methods = {
269
+ loadOptions: {
270
+ async searchToolkits(filter) {
271
+ const search = (filter !== null && filter !== void 0 ? filter : '').trim();
272
+ const data = await (0, GenericFunctions_1.searchCatalog)(this, search === '' ? 'popular apps and their actions' : search);
273
+ const toolkits = (0, catalog_1.extractToolkits)(data);
274
+ if (toolkits.length === 0) {
275
+ return [
276
+ {
277
+ name: 'No App Matched. Keep Typing, for Example Gmail, Slack or Sheets.',
278
+ value: '',
279
+ description: 'No match',
280
+ },
281
+ ];
282
+ }
283
+ return toolkits.map((entry) => ({
284
+ name: `${entry.toolkit}${entry.connected ? ' (connected)' : ''}`,
285
+ value: entry.toolkit,
286
+ description: entry.description === '' ? 'Composio app' : entry.description.slice(0, 120),
287
+ }));
288
+ },
289
+ async searchActions(filter) {
290
+ const search = (filter !== null && filter !== void 0 ? filter : '').trim();
291
+ const data = await (0, GenericFunctions_1.searchCatalog)(this, search === '' ? 'common actions' : search);
292
+ const tools = (0, catalog_1.extractTools)(data);
293
+ if (tools.length === 0) {
294
+ return [
295
+ {
296
+ name: 'No Action Matched. Describe What You Want to Do, for Example Send an Email.',
297
+ value: '',
298
+ description: 'No match',
299
+ },
300
+ ];
301
+ }
302
+ return tools.map((tool) => ({
303
+ name: tool.slug,
304
+ value: tool.slug,
305
+ description: `${tool.toolkit}${tool.description === '' ? '' : ' — ' + tool.description.slice(0, 110)}`,
306
+ }));
307
+ },
308
+ async getToolParameters() {
309
+ const toolSlug = this.getCurrentNodeParameter('toolSlug') || '';
310
+ if (toolSlug === '') {
311
+ return [
312
+ {
313
+ name: 'Choose an Action Above First',
314
+ value: '',
315
+ description: 'An action is needed before its inputs can be listed',
316
+ },
317
+ ];
318
+ }
319
+ const data = await (0, GenericFunctions_1.fetchToolSchemas)(this, [toolSlug]);
320
+ const parameters = (0, catalog_1.extractParameters)(data, toolSlug);
321
+ if (parameters.length === 0) {
322
+ return [
323
+ {
324
+ name: 'This Action Reports No Inputs, or Its Schema Could Not Be Read. Use Using JSON if You Know the Fields.',
325
+ value: '',
326
+ description: 'No inputs found',
327
+ },
328
+ ];
329
+ }
330
+ return parameters.map((parameter) => ({
331
+ name: `${parameter.name}${parameter.required ? ' (required)' : ''}`,
332
+ value: parameter.name,
333
+ description: `${parameter.type}${parameter.description === '' ? '' : ' — ' + parameter.description.slice(0, 110)}`,
334
+ }));
335
+ },
336
+ },
337
+ };
221
338
  }
222
339
  async execute() {
223
340
  const items = this.getInputData();
@@ -229,90 +346,90 @@ class Composio {
229
346
  const operation = this.getNodeParameter('operation', i);
230
347
  const nodeUserId = this.getNodeParameter('userId', i, '') || undefined;
231
348
  const userId = nodeUserId || (credentials.userId || undefined);
232
- const resolvedUserId = userId || undefined;
233
- let tool;
234
- let args;
349
+ const push = (payload) => {
350
+ returnData.push({ json: payload, pairedItem: { item: i } });
351
+ };
235
352
  if (resource === 'connection') {
236
- const toolkit = this.getNodeParameter('toolkit', i);
237
- if (operation === 'connect') {
238
- tool = metatools_1.META_TOOLS.manageConnections;
239
- args = (0, GenericFunctions_1.buildToolArguments)({
240
- action: metatools_1.CONNECTION_ACTIONS.create,
241
- toolkit,
242
- user_id: resolvedUserId,
243
- });
244
- }
245
- else if (operation === 'wait') {
246
- const timeout = this.getNodeParameter('timeout', i, 120);
247
- tool = metatools_1.META_TOOLS.waitForConnections;
248
- args = (0, GenericFunctions_1.buildToolArguments)({
249
- timeout,
250
- user_id: resolvedUserId,
251
- });
252
- }
253
- else if (operation === 'remove') {
254
- tool = metatools_1.META_TOOLS.manageConnections;
255
- args = (0, GenericFunctions_1.buildToolArguments)({
256
- action: metatools_1.CONNECTION_ACTIONS.remove,
257
- toolkit,
258
- user_id: resolvedUserId,
259
- });
260
- }
261
- else {
262
- tool = metatools_1.META_TOOLS.manageConnections;
263
- args = (0, GenericFunctions_1.buildToolArguments)({
264
- action: metatools_1.CONNECTION_ACTIONS.list,
265
- user_id: resolvedUserId,
353
+ if (operation === 'check') {
354
+ const toolkits = this.getNodeParameter('toolkits', i, []).filter((toolkit) => toolkit !== '');
355
+ const outcome = await (0, GenericFunctions_1.manageConnections)(this, toolkits);
356
+ push({
357
+ tool: outcome.tool,
358
+ data: outcome.data,
359
+ raw: outcome.text,
360
+ isError: outcome.isError,
266
361
  });
362
+ continue;
267
363
  }
364
+ const toolkit = this.getNodeParameter('toolkit', i);
365
+ const outcome = await (0, GenericFunctions_1.manageConnections)(this, [toolkit], operation === 'reconnect');
366
+ push({
367
+ tool: outcome.tool,
368
+ data: outcome.data,
369
+ raw: outcome.text,
370
+ isError: outcome.isError,
371
+ });
372
+ continue;
268
373
  }
269
- else if (resource === 'action') {
374
+ let tool;
375
+ let args;
376
+ if (resource === 'action') {
270
377
  if (operation === 'search') {
271
- const query = this.getNodeParameter('query', i);
272
378
  tool = metatools_1.META_TOOLS.searchTools;
273
- args = (0, GenericFunctions_1.buildToolArguments)({ query, user_id: resolvedUserId });
379
+ args = {
380
+ queries: [{ use_case: this.getNodeParameter('useCase', i) }],
381
+ session: { generate_id: true },
382
+ };
274
383
  }
275
384
  else if (operation === 'schemas') {
276
- const raw = this.getNodeParameter('toolSlugs', i);
277
- const toolSlugs = raw
278
- .split(',')
279
- .map((slug) => slug.trim())
280
- .filter((slug) => slug !== '');
281
385
  tool = metatools_1.META_TOOLS.getToolSchemas;
282
- args = (0, GenericFunctions_1.buildToolArguments)({ tool_slugs: toolSlugs, user_id: resolvedUserId });
386
+ args = {
387
+ tool_slugs: this.getNodeParameter('toolSlugs', i, []).filter((slug) => slug !== ''),
388
+ include: ['input_schema'],
389
+ };
283
390
  }
284
391
  else {
285
- const toolSlug = this.getNodeParameter('toolSlug', i);
286
- const rawArguments = this.getNodeParameter('arguments', i, '{}');
287
- const toolkit = this.getNodeParameter('toolkit', i, '') || undefined;
288
- const parsedArguments = typeof rawArguments === 'string'
289
- ? JSON.parse(rawArguments === '' ? '{}' : rawArguments)
290
- : rawArguments;
392
+ const inputMode = this.getNodeParameter('inputMode', i, 'fields');
393
+ let toolArguments;
394
+ if (inputMode === 'json') {
395
+ const raw = this.getNodeParameter('arguments', i, '{}');
396
+ toolArguments =
397
+ typeof raw === 'string'
398
+ ? JSON.parse(raw === '' ? '{}' : raw)
399
+ : raw;
400
+ }
401
+ else {
402
+ const rows = this.getNodeParameter('parameterRows.parameters', i, []);
403
+ toolArguments = (0, catalog_1.buildArgumentsFromRows)(rows);
404
+ }
291
405
  tool = metatools_1.META_TOOLS.multiExecuteTool;
292
- args = (0, GenericFunctions_1.buildToolArguments)({
293
- tools: [{ tool_slug: toolSlug, arguments: parsedArguments }],
294
- toolkits: toolkit === undefined ? undefined : [toolkit],
295
- user_id: resolvedUserId,
296
- });
406
+ args = {
407
+ tools: [
408
+ {
409
+ tool_slug: this.getNodeParameter('toolSlug', i),
410
+ arguments: toolArguments,
411
+ },
412
+ ],
413
+ sync_response_to_workbench: this.getNodeParameter('syncToWorkbench', i, false),
414
+ };
297
415
  }
298
416
  }
299
417
  else {
300
418
  tool = this.getNodeParameter('metaTool', i);
301
419
  const rawMetaArguments = this.getNodeParameter('metaArguments', i, '{}');
302
- const parsedMetaArguments = typeof rawMetaArguments === 'string'
420
+ const parsed = typeof rawMetaArguments === 'string'
303
421
  ? JSON.parse(rawMetaArguments === '' ? '{}' : rawMetaArguments)
304
422
  : rawMetaArguments;
305
- args = (0, GenericFunctions_1.buildToolArguments)({ ...parsedMetaArguments, user_id: resolvedUserId });
423
+ args = { ...parsed };
306
424
  }
425
+ if (userId !== undefined)
426
+ args.user_id = userId;
307
427
  const outcome = await (0, GenericFunctions_1.callMetaTool)(this, tool, args);
308
- returnData.push({
309
- json: {
310
- tool: outcome.tool,
311
- data: outcome.data,
312
- raw: outcome.text,
313
- isError: outcome.isError,
314
- },
315
- pairedItem: { item: i },
428
+ push({
429
+ tool: outcome.tool,
430
+ data: outcome.data,
431
+ raw: outcome.text,
432
+ isError: outcome.isError,
316
433
  });
317
434
  }
318
435
  catch (error) {