@linkorb/n8n-nodes-nebula 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Joost Faassen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,438 @@
1
+ # n8n-nodes-nebula
2
+
3
+ A custom n8n node for implementing **Nebula HITL (Human-in-the-Loop)** workflows with your own configurable REST API backend.
4
+
5
+ ![Nebula HITL Request Node](docs/node-screenshot.png)
6
+
7
+ ## Description
8
+
9
+ This node allows you to create human approval/input requests in your n8n workflows. Unlike other HITL solutions that are tied to specific cloud services, this node is designed to work with **your own backend**, giving you full control over how requests are created, stored, displayed, and responded to.
10
+
11
+ ### Features
12
+
13
+ - ⏸️ **True Wait Functionality**: Workflow execution pauses until a human responds
14
+ - πŸ” **Configurable Backend**: Connect to your own REST API endpoint
15
+ - πŸ“ **Multiple Response Types**: Ok, Yes/No, Text, or Custom options
16
+ - 🏷️ **Rich Metadata**: Support for priority, assignee, tags, and custom data
17
+ - πŸ”„ **Webhook-based Responses**: Automatically resume workflow when human responds
18
+ - ⏰ **Configurable Timeout**: Set how long to wait for a response
19
+ - πŸ“Š **Full Context**: Pass workflow and execution context to your backend
20
+
21
+ ### How It Works
22
+
23
+ 1. **Request Creation**: The node POSTs a request to your backend with all details including a webhook URL
24
+ 2. **Workflow Pauses**: The n8n execution enters a "waiting" state - no resources are consumed while waiting
25
+ 3. **Human Action**: Your backend displays the request to a human for action
26
+ 4. **Webhook Callback**: When the human responds, your backend POSTs to the webhook URL
27
+ 5. **Workflow Resumes**: n8n receives the webhook, resumes the workflow with the response data as output
28
+
29
+ ## Installation
30
+
31
+ ### Prerequisites
32
+
33
+ - n8n version 1.0.0 or later
34
+ - Node.js 18.10 or later
35
+ - pnpm (recommended) or npm
36
+
37
+ ### Install via npm (Community Nodes)
38
+
39
+ In your n8n instance, go to **Settings β†’ Community Nodes** and install:
40
+
41
+ ```
42
+ n8n-nodes-nebula
43
+ ```
44
+
45
+ ### Manual Installation (Self-hosted n8n)
46
+
47
+ 1. **Clone or download this repository:**
48
+
49
+ ```bash
50
+ cd ~/.n8n/custom
51
+ git clone https://github.com/linkorb/n8n-nodes-nebula.git
52
+ cd n8n-nodes-nebula
53
+ ```
54
+
55
+ 2. **Install dependencies:**
56
+
57
+ ```bash
58
+ pnpm install
59
+ ```
60
+
61
+ 3. **Build the node:**
62
+
63
+ ```bash
64
+ pnpm build
65
+ ```
66
+
67
+ 4. **Restart n8n:**
68
+
69
+ ```bash
70
+ # If running n8n directly
71
+ n8n start
72
+
73
+ # If running via PM2
74
+ pm2 restart n8n
75
+
76
+ # If running via Docker
77
+ docker restart n8n
78
+ ```
79
+
80
+ ### Installation with Docker
81
+
82
+ If you're running n8n in Docker, you can mount the custom nodes:
83
+
84
+ ```yaml
85
+ version: '3.8'
86
+ services:
87
+ n8n:
88
+ image: n8nio/n8n
89
+ volumes:
90
+ - ~/.n8n:/home/node/.n8n
91
+ - ./n8n-nodes-nebula:/home/node/.n8n/custom/n8n-nodes-nebula
92
+ environment:
93
+ - N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
94
+ ```
95
+
96
+ ## Configuration
97
+
98
+ ### Setting up Credentials
99
+
100
+ 1. In n8n, go to **Credentials β†’ Add Credential**
101
+ 2. Search for "Nebula API"
102
+ 3. Fill in the required fields:
103
+
104
+ | Field | Description | Example |
105
+ |-------|-------------|---------|
106
+ | **Base URL** | Your Nebula API backend base URL | `https://api.mycompany.com/nebula` |
107
+ | **Username** | Authentication username | `n8n-service` |
108
+ | **Password** | Authentication password | `your-secure-password` |
109
+ | **Metadata** | Additional JSON data for all requests | `{"tenantId": "abc123"}` |
110
+
111
+ > **Note:** The webhook callback URL is automatically determined from your n8n instance configuration. You don't need to specify it manually.
112
+
113
+ ## Usage
114
+
115
+ ### Basic Workflow
116
+
117
+ 1. Add the **Nebula HITL Request** node to your workflow
118
+ 2. Connect it to your workflow flow
119
+ 3. Configure the node:
120
+ - **Title**: A short description of what needs human attention
121
+ - **Message**: Detailed markdown-formatted message
122
+ - **Response Type**: Choose from Ok, Yes/No, Text, or Form
123
+
124
+ ### Response Types
125
+
126
+ | Type | Description | Response Value |
127
+ |------|-------------|----------------|
128
+ | **Ok** | Simple acknowledgement | `"ok"` |
129
+ | **Yes/No** | Binary choice | `"yes"` or `"no"` |
130
+ | **Text** | Free-form text input | User's text input |
131
+ | **Form (survey.json)** | Custom form in survey.json format | Form submission data |
132
+
133
+ ### Form JSON Example
134
+
135
+ The Form response type uses [survey.json](https://surveyjs.io/form-library/documentation/design-survey/create-a-simple-survey) format:
136
+
137
+ ```json
138
+ {
139
+ "elements": [
140
+ {
141
+ "type": "radiogroup",
142
+ "name": "decision",
143
+ "title": "Please select an option",
144
+ "choices": ["Approve", "Reject", "Need More Info"]
145
+ },
146
+ {
147
+ "type": "text",
148
+ "name": "comment",
149
+ "title": "Additional comments"
150
+ }
151
+ ]
152
+ }
153
+ ```
154
+
155
+ ### Node Options
156
+
157
+ | Option | Description | Default |
158
+ |--------|-------------|---------|
159
+ | **Priority** | Request priority level | `normal` |
160
+ | **Timeout (Minutes)** | Auto-timeout for requests | `0` (no timeout) |
161
+ | **Assignee** | Email/ID to assign request to | (none) |
162
+ | **Tags** | Comma-separated tags | (none) |
163
+
164
+ ### Additional Data
165
+
166
+ You can pass additional JSON data that will be included in the request to your backend:
167
+
168
+ ```json
169
+ {
170
+ "orderId": "12345",
171
+ "customerName": "John Doe",
172
+ "amount": 150.00
173
+ }
174
+ ```
175
+
176
+ ## Backend API Requirements
177
+
178
+ Your backend API needs to implement the following endpoint:
179
+
180
+ ### POST /requests
181
+
182
+ Creates a new HITL request.
183
+
184
+ **Request Body:**
185
+
186
+ ```json
187
+ {
188
+ "requestId": "uuid-v4-string",
189
+ "title": "Approval Required",
190
+ "message": "Please review this order...",
191
+ "responseType": "yesno",
192
+ "form": null,
193
+ "webhookUrl": "https://your-n8n.com/webhook-waiting/xxx/nebula-hitl-response",
194
+ "priority": "normal",
195
+ "timeoutMinutes": 0,
196
+ "assignee": "john@example.com",
197
+ "tags": ["urgent", "finance"],
198
+ "metadata": {"tenantId": "abc123"},
199
+ "additionalData": {"orderId": "12345"},
200
+ "inputData": {},
201
+ "workflowId": "workflow-id",
202
+ "executionId": "execution-id",
203
+ "createdAt": "2024-01-15T10:30:00Z"
204
+ }
205
+ ```
206
+
207
+ **Response:**
208
+
209
+ ```json
210
+ {
211
+ "success": true,
212
+ "requestId": "uuid-v4-string",
213
+ "status": "pending"
214
+ }
215
+ ```
216
+
217
+ ### Webhook Callback (IMPORTANT - This Resumes the Workflow!)
218
+
219
+ When a human responds, your backend **MUST** call the `webhookUrl` provided in the request payload. This is what resumes the waiting n8n workflow execution.
220
+
221
+ **POST to webhookUrl:**
222
+
223
+ ```json
224
+ {
225
+ "requestId": "uuid-v4-string",
226
+ "response": "approved",
227
+ "responseValue": "approved",
228
+ "respondedBy": "john@example.com",
229
+ "respondedAt": "2024-01-15T11:45:00Z",
230
+ "comment": "Looks good!",
231
+ "data": {
232
+ "anyAdditionalData": "you want to pass"
233
+ }
234
+ }
235
+ ```
236
+
237
+ **What happens:**
238
+ 1. n8n receives the webhook POST
239
+ 2. The waiting workflow execution resumes
240
+ 3. The Nebula HITL Request node outputs the webhook payload data
241
+ 4. Subsequent nodes in your workflow can access `$json.response`, `$json.respondedBy`, etc.
242
+
243
+ **The webhook URL format:** `https://your-n8n-instance.com/webhook-waiting/{executionId}/nebula-hitl-response`
244
+
245
+ The `webhookUrl` is automatically constructed and included in the request payload sent to your backend. Your backend simply needs to POST to this URL when a human responds.
246
+
247
+ ### Health Check (Optional)
248
+
249
+ For credential testing, implement:
250
+
251
+ **GET /health**
252
+
253
+ ```json
254
+ {
255
+ "status": "ok"
256
+ }
257
+ ```
258
+
259
+ ## Example Backend Implementation
260
+
261
+ Here's a minimal Express.js backend example:
262
+
263
+ ```javascript
264
+ const express = require('express');
265
+ const app = express();
266
+
267
+ app.use(express.json());
268
+
269
+ // Store requests in memory (use a database in production)
270
+ const requests = new Map();
271
+
272
+ // Create request
273
+ app.post('/requests', (req, res) => {
274
+ const { requestId, webhookUrl, ...data } = req.body;
275
+
276
+ requests.set(requestId, {
277
+ ...data,
278
+ requestId,
279
+ webhookUrl,
280
+ status: 'pending'
281
+ });
282
+
283
+ console.log('New Nebula HITL request:', requestId, data.title);
284
+
285
+ res.json({ success: true, requestId, status: 'pending' });
286
+ });
287
+
288
+ // Respond to request (called by your UI)
289
+ app.post('/requests/:requestId/respond', async (req, res) => {
290
+ const requestId = req.params.requestId;
291
+ const { response, respondedBy } = req.body;
292
+
293
+ const request = requests.get(requestId);
294
+ if (!request) {
295
+ return res.status(404).json({ error: 'Request not found' });
296
+ }
297
+
298
+ // Call n8n webhook to resume workflow
299
+ await fetch(request.webhookUrl, {
300
+ method: 'POST',
301
+ headers: { 'Content-Type': 'application/json' },
302
+ body: JSON.stringify({
303
+ requestId,
304
+ response,
305
+ responseValue: response,
306
+ respondedBy,
307
+ respondedAt: new Date().toISOString()
308
+ })
309
+ });
310
+
311
+ requests.delete(requestId);
312
+
313
+ res.json({ success: true });
314
+ });
315
+
316
+ // Health check
317
+ app.get('/health', (req, res) => {
318
+ res.json({ status: 'ok' });
319
+ });
320
+
321
+ app.listen(3000, () => {
322
+ console.log('Nebula HITL Backend running on port 3000');
323
+ });
324
+ ```
325
+
326
+ ## Workflow Example
327
+
328
+ ```
329
+ [Trigger] β†’ [Process Data] β†’ [Nebula HITL Request] β†’ [If Response == "approved"] β†’ [Continue]
330
+ ↓
331
+ [Handle Rejection]
332
+ ```
333
+
334
+ ### Accessing Response Data
335
+
336
+ After the Nebula HITL Request node resumes (when the webhook is called), the node outputs the webhook payload data. You can access:
337
+
338
+ ```javascript
339
+ // In a Code node or expression
340
+ const requestId = $json.requestId; // The original request ID
341
+ const response = $json.response; // The response value (e.g., "approved", "yes", "rejected")
342
+ const responseValue = $json.responseValue; // Same as response (for compatibility)
343
+ const respondedBy = $json.respondedBy; // Who responded (email/ID)
344
+ const respondedAt = $json.respondedAt; // ISO timestamp when they responded
345
+ const comment = $json.comment; // Optional comment from responder
346
+ const data = $json.data; // Any additional data from your backend
347
+ ```
348
+
349
+ **Example: Using in an IF node condition:**
350
+ ```
351
+ {{ $json.response }} equals "approved"
352
+ ```
353
+
354
+ **Example: Send notification with response:**
355
+ ```
356
+ The request was {{ $json.response }} by {{ $json.respondedBy }} at {{ $json.respondedAt }}
357
+ ```
358
+
359
+ ## Development
360
+
361
+ ### Project Structure
362
+
363
+ ```
364
+ n8n-nodes-nebula/
365
+ β”œβ”€β”€ credentials/
366
+ β”‚ └── NebulaApi.credentials.ts
367
+ β”œβ”€β”€ nodes/
368
+ β”‚ └── NebulaHitlRequest/
369
+ β”‚ β”œβ”€β”€ NebulaHitlRequest.node.ts
370
+ β”‚ β”œβ”€β”€ NebulaHitlRequest.node.json
371
+ β”‚ └── nebulaHitlRequest.svg
372
+ β”œβ”€β”€ package.json
373
+ β”œβ”€β”€ tsconfig.json
374
+ └── README.md
375
+ ```
376
+
377
+ ### Building from Source
378
+
379
+ ```bash
380
+ # Install dependencies
381
+ pnpm install
382
+
383
+ # Build
384
+ pnpm build
385
+
386
+ # Watch mode (for development)
387
+ pnpm dev
388
+
389
+ # Lint
390
+ pnpm lint
391
+
392
+ # Format
393
+ pnpm format
394
+ ```
395
+
396
+ ## Troubleshooting
397
+
398
+ ### Node not appearing in n8n
399
+
400
+ 1. Ensure the build completed successfully (`pnpm build`)
401
+ 2. Check that the `dist` folder contains compiled JS files
402
+ 3. Verify the custom nodes path in n8n configuration
403
+ 4. Restart n8n completely
404
+
405
+ ### Webhook not being called
406
+
407
+ 1. Ensure your n8n instance is publicly accessible or your backend can reach it
408
+ 2. Check the webhookUrl in your backend logs
409
+ 3. Verify the requestId matches in both the request and response
410
+
411
+ ### Authentication errors
412
+
413
+ 1. Verify credentials in n8n are correct
414
+ 2. Check your backend's authentication logic
415
+ 3. Look at the backend logs for more details
416
+
417
+ ## Contributing
418
+
419
+ Contributions are welcome! Please:
420
+
421
+ 1. Fork the repository
422
+ 2. Create a feature branch
423
+ 3. Make your changes
424
+ 4. Submit a pull request
425
+
426
+ ## License
427
+
428
+ MIT License - see [LICENSE](LICENSE) file for details.
429
+
430
+ ## Related Projects
431
+
432
+ - [n8n](https://n8n.io) - Fair-code workflow automation
433
+ - [n8n documentation on creating nodes](https://docs.n8n.io/integrations/creating-nodes/)
434
+
435
+ ## Support
436
+
437
+ - Create an issue on GitHub for bug reports
438
+ - Discussions for questions and feature requests
@@ -0,0 +1,11 @@
1
+ import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class NebulaApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ icon: "fa:circle-nodes";
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ test: ICredentialTestRequest;
10
+ }
11
+ //# sourceMappingURL=NebulaApi.credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NebulaApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/NebulaApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,eAAe,EAChB,MAAM,cAAc,CAAC;AAEtB,qBAAa,SAAU,YAAW,eAAe;IAC/C,IAAI,SAAe;IACnB,WAAW,SAAgB;IAC3B,gBAAgB,SAAiD;IAIjE,IAAI,EAAG,iBAAiB,CAAU;IAElC,UAAU,EAAE,eAAe,EAAE,CAqC3B;IAEF,YAAY,EAAE,oBAAoB,CAQhC;IAEF,IAAI,EAAE,sBAAsB,CAM1B;CACH"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NebulaApi = void 0;
4
+ class NebulaApi {
5
+ name = 'nebulaApi';
6
+ displayName = 'Nebula API';
7
+ documentationUrl = 'https://github.com/linkorb/n8n-nodes-nebula';
8
+ // Note: Custom SVG icons don't work with N8N_CUSTOM_EXTENSIONS due to n8n bug
9
+ // See: https://github.com/n8n-io/n8n/issues/21360
10
+ // Using Font Awesome as fallback. Install via npm for custom SVG icons.
11
+ icon = 'fa:circle-nodes';
12
+ properties = [
13
+ {
14
+ displayName: 'Base URL',
15
+ name: 'baseUrl',
16
+ type: 'string',
17
+ default: '',
18
+ placeholder: 'https://api.example.com',
19
+ description: 'The base URL of your Nebula API backend',
20
+ required: true,
21
+ },
22
+ {
23
+ displayName: 'Username',
24
+ name: 'username',
25
+ type: 'string',
26
+ default: '',
27
+ description: 'Username for authentication with the API',
28
+ required: true,
29
+ },
30
+ {
31
+ displayName: 'Password',
32
+ name: 'password',
33
+ type: 'string',
34
+ typeOptions: {
35
+ password: true,
36
+ },
37
+ default: '',
38
+ description: 'Password for authentication with the API',
39
+ required: true,
40
+ },
41
+ {
42
+ displayName: 'Metadata',
43
+ name: 'metadata',
44
+ type: 'json',
45
+ default: '{}',
46
+ description: 'Additional JSON metadata to include with all requests (e.g., tenant ID, environment)',
47
+ placeholder: '{"tenantId": "abc123", "environment": "production"}',
48
+ },
49
+ ];
50
+ authenticate = {
51
+ type: 'generic',
52
+ properties: {
53
+ auth: {
54
+ username: '={{$credentials.username}}',
55
+ password: '={{$credentials.password}}',
56
+ },
57
+ },
58
+ };
59
+ test = {
60
+ request: {
61
+ baseURL: '={{$credentials.baseUrl}}',
62
+ url: '/health',
63
+ method: 'GET',
64
+ },
65
+ };
66
+ }
67
+ exports.NebulaApi = NebulaApi;
68
+ //# sourceMappingURL=NebulaApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NebulaApi.credentials.js","sourceRoot":"","sources":["../../credentials/NebulaApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,SAAS;IACpB,IAAI,GAAG,WAAW,CAAC;IACnB,WAAW,GAAG,YAAY,CAAC;IAC3B,gBAAgB,GAAG,6CAA6C,CAAC;IACjE,8EAA8E;IAC9E,kDAAkD;IAClD,wEAAwE;IACxE,IAAI,GAAG,iBAA0B,CAAC;IAElC,UAAU,GAAsB;QAC9B;YACE,WAAW,EAAE,UAAU;YACvB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,yBAAyB;YACtC,WAAW,EAAE,yCAAyC;YACtD,QAAQ,EAAE,IAAI;SACf;QACD;YACE,WAAW,EAAE,UAAU;YACvB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,0CAA0C;YACvD,QAAQ,EAAE,IAAI;SACf;QACD;YACE,WAAW,EAAE,UAAU;YACvB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE;gBACX,QAAQ,EAAE,IAAI;aACf;YACD,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,0CAA0C;YACvD,QAAQ,EAAE,IAAI;SACf;QACD;YACE,WAAW,EAAE,UAAU;YACvB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,sFAAsF;YACnG,WAAW,EAAE,qDAAqD;SACnE;KACF,CAAC;IAEF,YAAY,GAAyB;QACnC,IAAI,EAAE,SAAS;QACf,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,QAAQ,EAAE,4BAA4B;gBACtC,QAAQ,EAAE,4BAA4B;aACvC;SACF;KACF,CAAC;IAEF,IAAI,GAA2B;QAC7B,OAAO,EAAE;YACP,OAAO,EAAE,2BAA2B;YACpC,GAAG,EAAE,SAAS;YACd,MAAM,EAAE,KAAK;SACd;KACF,CAAC;CACH;AAjED,8BAiEC"}
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
2
+ <rect width="60" height="60" rx="8" fill="#1a1a2e"/>
3
+ <g transform="translate(10, 10)">
4
+ <path d="M20 2C10.059 2 2 10.059 2 20s8.059 18 18 18c4.641 0 8.886-1.76 12.082-4.646l4.95 4.95v-3.608h4.085L35.355 29.1A17.91 17.91 0 0038 20c0-9.941-8.059-18-18-18z" fill="#F58220"/>
5
+ <circle cx="20" cy="20" r="9" fill="#1a1a2e"/>
6
+ <path d="M30 28l10 10v-6h-4v-6h-6z" fill="#F58220"/>
7
+ </g>
8
+ <circle cx="10" cy="10" r="1.2" fill="#fff" opacity="0.5"/>
9
+ <circle cx="50" cy="8" r="0.8" fill="#fff" opacity="0.4"/>
10
+ <circle cx="52" cy="50" r="1" fill="#fff" opacity="0.5"/>
11
+ </svg>
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import { IExecuteFunctions, INodeType, INodeTypeDescription, INodeExecutionData, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';
2
+ export declare class NebulaHitlRequest implements INodeType {
3
+ description: INodeTypeDescription;
4
+ webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
5
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
6
+ }
7
+ //# sourceMappingURL=NebulaHitlRequest.node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NebulaHitlRequest.node.d.ts","sourceRoot":"","sources":["../../../nodes/NebulaHitlRequest/NebulaHitlRequest.node.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EAGrB,MAAM,cAAc,CAAC;AAGtB,qBAAa,iBAAkB,YAAW,SAAS;IACjD,WAAW,EAAE,oBAAoB,CA+L/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAsC/D,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAmLxE"}
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NebulaHitlRequest = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const uuid_1 = require("uuid");
6
+ class NebulaHitlRequest {
7
+ description = {
8
+ displayName: 'Nebula HITL Request',
9
+ name: 'nebulaHitlRequest',
10
+ // Note: Custom SVG icons don't work with N8N_CUSTOM_EXTENSIONS due to n8n bug
11
+ // See: https://github.com/n8n-io/n8n/issues/21360
12
+ // Using Font Awesome as fallback. Install via npm for custom SVG icons.
13
+ icon: 'fa:user-clock',
14
+ group: ['transform'],
15
+ version: 1,
16
+ subtitle: '={{$parameter["operation"]}}',
17
+ description: 'Send Human-in-the-Loop requests via Nebula and wait for responses',
18
+ defaults: {
19
+ name: 'Nebula HITL Request',
20
+ },
21
+ inputs: ['main'],
22
+ outputs: ['main'],
23
+ credentials: [
24
+ {
25
+ name: 'nebulaApi',
26
+ required: true,
27
+ },
28
+ ],
29
+ webhooks: [
30
+ {
31
+ name: 'default',
32
+ httpMethod: 'POST',
33
+ responseMode: 'onReceived',
34
+ path: 'nebula-hitl-response',
35
+ restartWebhook: true,
36
+ },
37
+ ],
38
+ properties: [
39
+ {
40
+ displayName: 'Operation',
41
+ name: 'operation',
42
+ type: 'options',
43
+ noDataExpression: true,
44
+ options: [
45
+ {
46
+ name: 'HITL Request',
47
+ value: 'hitlRequest',
48
+ description: 'Create a Human-in-the-Loop request and wait for response',
49
+ action: 'Create a HITL request and wait for response',
50
+ },
51
+ ],
52
+ default: 'hitlRequest',
53
+ },
54
+ {
55
+ displayName: 'Title',
56
+ name: 'title',
57
+ type: 'string',
58
+ default: '',
59
+ placeholder: 'Approval Required',
60
+ description: 'A short title for the HITL request',
61
+ required: true,
62
+ displayOptions: {
63
+ show: {
64
+ operation: ['hitlRequest'],
65
+ },
66
+ },
67
+ },
68
+ {
69
+ displayName: 'Message',
70
+ name: 'message',
71
+ type: 'string',
72
+ typeOptions: {
73
+ rows: 6,
74
+ },
75
+ default: '',
76
+ placeholder: 'Please review the following information and provide your approval...',
77
+ description: 'A detailed message in Markdown format',
78
+ required: true,
79
+ displayOptions: {
80
+ show: {
81
+ operation: ['hitlRequest'],
82
+ },
83
+ },
84
+ },
85
+ {
86
+ displayName: 'Response Type',
87
+ name: 'responseType',
88
+ type: 'options',
89
+ options: [
90
+ {
91
+ name: 'Ok',
92
+ value: 'ok',
93
+ description: 'Simple acknowledgement with an OK button',
94
+ },
95
+ {
96
+ name: 'Yes/No',
97
+ value: 'yesno',
98
+ description: 'Binary choice between Yes and No',
99
+ },
100
+ {
101
+ name: 'Text',
102
+ value: 'text',
103
+ description: 'Free-form text input',
104
+ },
105
+ {
106
+ name: 'Form (survey.json)',
107
+ value: 'form',
108
+ description: 'Custom form defined as survey.json JSON format',
109
+ },
110
+ ],
111
+ default: 'ok',
112
+ description: 'The type of response expected from the human',
113
+ displayOptions: {
114
+ show: {
115
+ operation: ['hitlRequest'],
116
+ },
117
+ },
118
+ },
119
+ {
120
+ displayName: 'Form JSON',
121
+ name: 'formJson',
122
+ type: 'json',
123
+ default: '{\n "elements": [\n {\n "type": "radiogroup",\n "name": "decision",\n "title": "Please select an option",\n "choices": ["Approve", "Reject", "Need More Info"]\n }\n ]\n}',
124
+ placeholder: '{"elements": [{"type": "text", "name": "comment", "title": "Your comment"}]}',
125
+ description: 'Form definition in survey.json JSON format',
126
+ displayOptions: {
127
+ show: {
128
+ operation: ['hitlRequest'],
129
+ responseType: ['form'],
130
+ },
131
+ },
132
+ },
133
+ {
134
+ displayName: 'Additional Data',
135
+ name: 'additionalData',
136
+ type: 'json',
137
+ default: '{}',
138
+ description: 'Additional JSON data to include with the request',
139
+ displayOptions: {
140
+ show: {
141
+ operation: ['hitlRequest'],
142
+ },
143
+ },
144
+ },
145
+ {
146
+ displayName: 'Options',
147
+ name: 'options',
148
+ type: 'collection',
149
+ placeholder: 'Add Option',
150
+ default: {},
151
+ displayOptions: {
152
+ show: {
153
+ operation: ['hitlRequest'],
154
+ },
155
+ },
156
+ options: [
157
+ {
158
+ displayName: 'Priority',
159
+ name: 'priority',
160
+ type: 'options',
161
+ options: [
162
+ { name: 'Low', value: 'low' },
163
+ { name: 'Normal', value: 'normal' },
164
+ { name: 'High', value: 'high' },
165
+ { name: 'Urgent', value: 'urgent' },
166
+ ],
167
+ default: 'normal',
168
+ description: 'Priority level of the request',
169
+ },
170
+ {
171
+ displayName: 'Timeout (Minutes)',
172
+ name: 'timeoutMinutes',
173
+ type: 'number',
174
+ default: 0,
175
+ description: 'Timeout in minutes after which the workflow continues with a timeout response (0 = wait indefinitely)',
176
+ typeOptions: {
177
+ minValue: 0,
178
+ },
179
+ },
180
+ {
181
+ displayName: 'Assignee',
182
+ name: 'assignee',
183
+ type: 'string',
184
+ default: '',
185
+ description: 'Email or ID of the person to assign this request to',
186
+ },
187
+ {
188
+ displayName: 'Tags',
189
+ name: 'tags',
190
+ type: 'string',
191
+ default: '',
192
+ description: 'Comma-separated list of tags',
193
+ },
194
+ ],
195
+ },
196
+ ],
197
+ };
198
+ async webhook() {
199
+ const bodyData = this.getBodyData();
200
+ // Validate the incoming webhook has required data
201
+ if (!bodyData.requestId) {
202
+ return {
203
+ webhookResponse: {
204
+ status: 400,
205
+ body: { error: 'Missing requestId in webhook payload' },
206
+ },
207
+ };
208
+ }
209
+ // Return the response data to continue the workflow
210
+ // The webhook data becomes the output of the node
211
+ return {
212
+ webhookResponse: {
213
+ status: 200,
214
+ body: { success: true, message: 'Response received, workflow will continue' },
215
+ },
216
+ workflowData: [
217
+ [
218
+ {
219
+ json: {
220
+ requestId: bodyData.requestId,
221
+ response: bodyData.response,
222
+ responseValue: bodyData.responseValue,
223
+ respondedBy: bodyData.respondedBy,
224
+ respondedAt: bodyData.respondedAt || new Date().toISOString(),
225
+ comment: bodyData.comment,
226
+ data: bodyData.data || {},
227
+ },
228
+ },
229
+ ],
230
+ ],
231
+ };
232
+ }
233
+ async execute() {
234
+ const items = this.getInputData();
235
+ // Check if we're resuming from a webhook (execution was waiting)
236
+ const waitingWebhookData = this.getInputData();
237
+ // If we have webhook response data (resuming), pass it through
238
+ // This happens when the webhook is called and the execution resumes
239
+ if (waitingWebhookData.length > 0 &&
240
+ waitingWebhookData[0].json.requestId &&
241
+ waitingWebhookData[0].json.response !== undefined) {
242
+ // We're resuming from webhook - return the webhook data as output
243
+ return [waitingWebhookData];
244
+ }
245
+ const operation = this.getNodeParameter('operation', 0);
246
+ if (operation === 'hitlRequest') {
247
+ // Process only the first item for the request
248
+ // (HITL requests typically handle one request at a time)
249
+ const itemIndex = 0;
250
+ try {
251
+ const credentials = await this.getCredentials('nebulaApi');
252
+ const baseUrl = credentials.baseUrl.replace(/\/$/, ''); // Remove trailing slash
253
+ // Parse metadata from credentials
254
+ let metadata = {};
255
+ try {
256
+ metadata = credentials.metadata ? JSON.parse(credentials.metadata) : {};
257
+ }
258
+ catch {
259
+ // Use empty object if parsing fails
260
+ }
261
+ // Get parameters
262
+ const title = this.getNodeParameter('title', itemIndex);
263
+ const message = this.getNodeParameter('message', itemIndex);
264
+ const responseType = this.getNodeParameter('responseType', itemIndex);
265
+ const additionalDataStr = this.getNodeParameter('additionalData', itemIndex, '{}');
266
+ const options = this.getNodeParameter('options', itemIndex, {});
267
+ // Get form JSON if response type is form
268
+ let form = {};
269
+ if (responseType === 'form') {
270
+ const formJsonStr = this.getNodeParameter('formJson', itemIndex, '{}');
271
+ try {
272
+ form = JSON.parse(formJsonStr);
273
+ }
274
+ catch {
275
+ // Use empty object if parsing fails
276
+ }
277
+ }
278
+ // Generate unique request ID
279
+ const requestId = (0, uuid_1.v4)();
280
+ // Construct the webhook URL using n8n's built-in instance base URL
281
+ // The webhook path is defined in the node description as 'nebula-hitl-response'
282
+ const n8nBaseUrl = this.getInstanceBaseUrl().replace(/\/$/, '');
283
+ const executionId = this.getExecutionId();
284
+ // n8n webhook URL format for waiting executions: {baseUrl}/webhook-waiting/{executionId}/nebula-hitl-response
285
+ const webhookUrl = `${n8nBaseUrl}/webhook-waiting/${executionId}/nebula-hitl-response`;
286
+ // Parse additional data
287
+ let parsedAdditionalData = {};
288
+ try {
289
+ parsedAdditionalData = JSON.parse(additionalDataStr);
290
+ }
291
+ catch {
292
+ // If parsing fails, use empty object
293
+ }
294
+ // Store incoming item data for reference
295
+ const inputItemData = items[itemIndex]?.json || {};
296
+ // Get workflow info
297
+ const workflow = this.getWorkflow();
298
+ // Build request payload
299
+ const payload = {
300
+ requestId,
301
+ title,
302
+ message,
303
+ responseType,
304
+ form: responseType === 'form' ? form : undefined,
305
+ webhookUrl, // The URL the backend should POST to when responding
306
+ priority: options.priority || 'normal',
307
+ timeoutMinutes: options.timeoutMinutes || 0,
308
+ assignee: options.assignee || undefined,
309
+ tags: options.tags ? options.tags.split(',').map((t) => t.trim()) : [],
310
+ metadata,
311
+ additionalData: parsedAdditionalData,
312
+ inputData: inputItemData,
313
+ workflowId: workflow.id,
314
+ workflowName: workflow.name,
315
+ executionId,
316
+ createdAt: new Date().toISOString(),
317
+ };
318
+ // Make HTTP POST request to create the HITL request
319
+ try {
320
+ await this.helpers.httpRequest({
321
+ method: 'POST',
322
+ url: `${baseUrl}/requests`,
323
+ body: payload,
324
+ json: true,
325
+ auth: {
326
+ username: credentials.username,
327
+ password: credentials.password,
328
+ },
329
+ headers: {
330
+ 'Content-Type': 'application/json',
331
+ },
332
+ });
333
+ }
334
+ catch (error) {
335
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), { error: error.message }, {
336
+ message: 'Failed to create HITL request on the Nebula server',
337
+ });
338
+ }
339
+ // Calculate the wait timeout
340
+ const timeoutMinutes = options.timeoutMinutes || 0;
341
+ let waitTill;
342
+ if (timeoutMinutes > 0) {
343
+ // Wait until the specified timeout
344
+ waitTill = new Date(Date.now() + timeoutMinutes * 60 * 1000);
345
+ }
346
+ else {
347
+ // Wait indefinitely (max date - n8n will handle this)
348
+ // Set to 1 year from now as "indefinite"
349
+ waitTill = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000);
350
+ }
351
+ // Store request info in static data for potential reference
352
+ const staticData = this.getWorkflowStaticData('node');
353
+ staticData.currentRequest = {
354
+ requestId,
355
+ title,
356
+ webhookUrl,
357
+ createdAt: payload.createdAt,
358
+ waitTill: waitTill.toISOString(),
359
+ };
360
+ // PUT THE EXECUTION TO WAIT!
361
+ // This is the key - it pauses the workflow until the webhook is called
362
+ // or the timeout is reached
363
+ await this.putExecutionToWait(waitTill);
364
+ // This code is reached when the execution resumes (either from webhook or timeout)
365
+ // However, in practice, when webhook is called, n8n handles the resume differently
366
+ // Return empty - the webhook handler will provide the actual output
367
+ return [[]];
368
+ }
369
+ catch (error) {
370
+ if (this.continueOnFail()) {
371
+ return [
372
+ [
373
+ {
374
+ json: {
375
+ error: error.message,
376
+ },
377
+ pairedItem: { item: itemIndex },
378
+ },
379
+ ],
380
+ ];
381
+ }
382
+ throw error;
383
+ }
384
+ }
385
+ // Default return for unknown operations
386
+ return [[]];
387
+ }
388
+ }
389
+ exports.NebulaHitlRequest = NebulaHitlRequest;
390
+ //# sourceMappingURL=NebulaHitlRequest.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NebulaHitlRequest.node.js","sourceRoot":"","sources":["../../../nodes/NebulaHitlRequest/NebulaHitlRequest.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ {
2
+ "node": "n8n-nodes-nebula.nebulaHitlRequest",
3
+ "nodeVersion": "1.0",
4
+ "codexVersion": "1.0",
5
+ "categories": ["Utility", "Core Nodes"],
6
+ "resources": {
7
+ "primaryDocumentation": [
8
+ {
9
+ "url": "https://github.com/linkorb/n8n-nodes-nebula"
10
+ }
11
+ ]
12
+ },
13
+ "alias": [
14
+ "nebula",
15
+ "hitl",
16
+ "human",
17
+ "approval",
18
+ "wait",
19
+ "human in the loop",
20
+ "manual",
21
+ "confirm",
22
+ "review"
23
+ ],
24
+ "subcategories": {
25
+ "Core Nodes": ["Flow"]
26
+ }
27
+ }
28
+
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
2
+ <rect width="60" height="60" rx="8" fill="#5C6BC0"/>
3
+ <circle cx="30" cy="18" r="7" fill="#fff"/>
4
+ <path d="M18 42c0-8.284 5.373-15 12-15s12 6.716 12 15v3H18v-3z" fill="#fff"/>
5
+ <circle cx="44" cy="18" r="8" fill="#FF7043"/>
6
+ <path d="M44 12v8M44 22v2" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/>
7
+ </svg>
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@linkorb/n8n-nodes-nebula",
3
+ "version": "1.0.0",
4
+ "description": "n8n nodes for Nebula - Human-in-the-Loop (HITL) requests with configurable REST API backend",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "nebula",
9
+ "human-in-the-loop",
10
+ "hitl",
11
+ "approval",
12
+ "nebula-hitl"
13
+ ],
14
+ "license": "MIT",
15
+ "homepage": "https://github.com/linkorb/n8n-nodes-nebula",
16
+ "author": {
17
+ "name": "Joost Faassen"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/linkorb/n8n-nodes-nebula.git"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "engines": {
27
+ "node": ">=18.10",
28
+ "pnpm": ">=9.1"
29
+ },
30
+ "packageManager": "pnpm@9.1.4",
31
+ "main": "dist/index.js",
32
+ "types": "dist/index.d.ts",
33
+ "scripts": {
34
+ "build": "tsc && gulp build:icons",
35
+ "dev": "tsc --watch",
36
+ "format": "prettier nodes credentials --write",
37
+ "lint": "eslint nodes credentials package.json",
38
+ "lintfix": "eslint nodes credentials package.json --fix",
39
+ "prepublishOnly": "npm run build"
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "n8n": {
45
+ "n8nNodesApiVersion": 1,
46
+ "credentials": [
47
+ "dist/credentials/NebulaApi.credentials.js"
48
+ ],
49
+ "nodes": [
50
+ "dist/nodes/NebulaHitlRequest/NebulaHitlRequest.node.js"
51
+ ]
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22.10.1",
55
+ "@types/uuid": "^10.0.0",
56
+ "@typescript-eslint/parser": "^8.18.0",
57
+ "eslint": "^9.16.0",
58
+ "gulp": "^5.0.0",
59
+ "n8n-workflow": "^1.70.0",
60
+ "prettier": "^3.4.2",
61
+ "typescript": "^5.7.2"
62
+ },
63
+ "dependencies": {
64
+ "uuid": "^11.0.3"
65
+ },
66
+ "peerDependencies": {
67
+ "n8n-workflow": "*"
68
+ }
69
+ }