@ferchy/n8n-nodes-aimc-toolkit 0.1.4

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.
@@ -0,0 +1,502 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AimcCode = void 0;
37
+ const n8n_workflow_1 = require("n8n-workflow");
38
+ const vm_1 = require("vm");
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const os = __importStar(require("os"));
42
+ const child_process_1 = require("child_process");
43
+ const util_1 = require("util");
44
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
45
+ const optionalRequire = (moduleName) => () => {
46
+ try {
47
+ return require(moduleName);
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ };
53
+ const libraryLoaders = {
54
+ _: () => require('lodash'),
55
+ lodash: () => require('lodash'),
56
+ axios: () => require('axios'),
57
+ cheerio: () => require('cheerio'),
58
+ dayjs: () => require('dayjs'),
59
+ moment: () => require('moment-timezone'),
60
+ dateFns: () => require('date-fns'),
61
+ dateFnsTz: () => require('date-fns-tz'),
62
+ joi: () => require('joi'),
63
+ Joi: () => require('joi'),
64
+ validator: () => require('validator'),
65
+ uuid: () => require('uuid'),
66
+ Ajv: () => require('ajv'),
67
+ yup: () => require('yup'),
68
+ zod: () => require('zod'),
69
+ z: () => require('zod'),
70
+ xml2js: () => require('xml2js'),
71
+ XMLParser: () => require('fast-xml-parser').XMLParser,
72
+ YAML: () => require('yaml'),
73
+ papaparse: () => require('papaparse'),
74
+ Papa: () => require('papaparse'),
75
+ Handlebars: () => require('handlebars'),
76
+ htmlToText: () => require('html-to-text'),
77
+ marked: () => require('marked'),
78
+ slug: () => {
79
+ const slugLib = require('slug');
80
+ return slugLib.default || slugLib;
81
+ },
82
+ pluralize: () => require('pluralize'),
83
+ qs: () => require('qs'),
84
+ FormData: () => require('form-data'),
85
+ ini: () => require('ini'),
86
+ toml: () => require('toml'),
87
+ nanoid: () => {
88
+ const nanoidLib = require('nanoid');
89
+ return nanoidLib.nanoid || nanoidLib;
90
+ },
91
+ bytes: () => require('bytes'),
92
+ ms: () => require('ms'),
93
+ phoneNumber: () => require('libphonenumber-js'),
94
+ iban: () => require('iban'),
95
+ fuzzy: () => require('fuse.js'),
96
+ stringSimilarity: () => require('string-similarity'),
97
+ pRetry: () => require('p-retry'),
98
+ jsonDiff: () => require('json-diff-ts'),
99
+ cronParser: () => require('cron-parser'),
100
+ franc: () => require('franc-min'),
101
+ compromise: () => require('compromise'),
102
+ protobuf: () => require('protobufjs'),
103
+ protobufjs: () => require('protobufjs'),
104
+ knex: () => require('knex'),
105
+ QRCode: () => require('qrcode'),
106
+ qrcode: () => require('qrcode'),
107
+ ytdl: () => require('@distube/ytdl-core'),
108
+ httpProxyAgent: () => require('http-proxy-agent'),
109
+ socksProxyAgent: () => require('socks-proxy-agent'),
110
+ bufferutil: optionalRequire('bufferutil'),
111
+ utf8Validate: optionalRequire('utf-8-validate'),
112
+ };
113
+ function attachLibraries(target, cache) {
114
+ for (const [name, loader] of Object.entries(libraryLoaders)) {
115
+ Object.defineProperty(target, name, {
116
+ enumerable: true,
117
+ get() {
118
+ if (!(name in cache)) {
119
+ cache[name] = loader();
120
+ }
121
+ return cache[name];
122
+ },
123
+ });
124
+ }
125
+ }
126
+ function normalizeResult(result, fallbackItems) {
127
+ const isItem = (value) => !!value && typeof value === 'object' && 'json' in value;
128
+ const toJson = (value) => value && typeof value === 'object' ? JSON.parse(JSON.stringify(value)) : value;
129
+ if (result === undefined) {
130
+ return fallbackItems;
131
+ }
132
+ if (Array.isArray(result)) {
133
+ if (result.every(isItem)) {
134
+ return result;
135
+ }
136
+ return result.map((entry) => ({
137
+ json: entry && typeof entry === 'object' ? toJson(entry) : { data: entry },
138
+ }));
139
+ }
140
+ if (isItem(result)) {
141
+ return [result];
142
+ }
143
+ return [
144
+ {
145
+ json: result && typeof result === 'object' ? toJson(result) : { data: result },
146
+ },
147
+ ];
148
+ }
149
+ function buildSandbox(params) {
150
+ const cache = {};
151
+ const sandbox = {
152
+ items: params.items,
153
+ item: params.item,
154
+ $input: {
155
+ all: () => params.items,
156
+ first: () => params.items[0],
157
+ last: () => params.items[params.items.length - 1],
158
+ item: () => params.item,
159
+ json: () => params.mode === 'runOnceForEachItem' && params.item
160
+ ? params.item.json
161
+ : params.items.map((entry) => entry.json),
162
+ },
163
+ params: params.nodeParams,
164
+ utils: {
165
+ now: () => new Date().toISOString(),
166
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
167
+ isEmpty: (value) => value === null || value === undefined || value === '' ||
168
+ (Array.isArray(value) && value.length === 0),
169
+ safeJson: (value) => {
170
+ try {
171
+ return JSON.parse(JSON.stringify(value));
172
+ }
173
+ catch {
174
+ return value;
175
+ }
176
+ },
177
+ toArray: (value) => (Array.isArray(value) ? value : [value]),
178
+ availableLibraries: () => Object.keys(libraryLoaders).sort(),
179
+ },
180
+ console: {
181
+ log: (...args) => console.log('[AIMC Code]', ...args),
182
+ warn: (...args) => console.warn('[AIMC Code]', ...args),
183
+ error: (...args) => console.error('[AIMC Code]', ...args),
184
+ },
185
+ Buffer,
186
+ Date,
187
+ Math,
188
+ JSON,
189
+ Promise,
190
+ setTimeout,
191
+ clearTimeout,
192
+ };
193
+ attachLibraries(sandbox, cache);
194
+ const libs = {};
195
+ attachLibraries(libs, cache);
196
+ sandbox.libs = libs;
197
+ return sandbox;
198
+ }
199
+ function indentCode(code, spaces = 4) {
200
+ const pad = ' '.repeat(spaces);
201
+ return code
202
+ .split('\n')
203
+ .map((line) => (line.trim().length ? `${pad}${line}` : line))
204
+ .join('\n');
205
+ }
206
+ async function createTempDir(prefix) {
207
+ return fs.promises.mkdtemp(path.join(os.tmpdir(), prefix));
208
+ }
209
+ async function writeJsonFile(filePath, data) {
210
+ await fs.promises.writeFile(filePath, JSON.stringify(data), 'utf8');
211
+ }
212
+ async function runPython(params) {
213
+ const tempDir = await createTempDir('aimc-python-');
214
+ try {
215
+ const inputPath = path.join(tempDir, 'input.json');
216
+ const paramsPath = path.join(tempDir, 'params.json');
217
+ await writeJsonFile(inputPath, params.items.map((item) => item.json));
218
+ await writeJsonFile(paramsPath, params.nodeParams);
219
+ const wrapper = `
220
+ import json
221
+ import os
222
+ import sys
223
+ import traceback
224
+ import subprocess
225
+
226
+ def _load_json(path):
227
+ if not path:
228
+ return None
229
+ with open(path, 'r', encoding='utf-8') as handle:
230
+ return json.load(handle)
231
+
232
+ def _auto_install(packages):
233
+ if not packages:
234
+ return
235
+ pkg_list = [p.strip() for p in packages.split(',') if p.strip()]
236
+ if not pkg_list:
237
+ return
238
+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--user'] + pkg_list)
239
+
240
+ items = _load_json(os.environ.get('AIMC_INPUT_PATH')) or []
241
+ item = items[0] if items else None
242
+ params = _load_json(os.environ.get('AIMC_PARAMS_PATH')) or {}
243
+ result = None
244
+
245
+ if os.environ.get('AIMC_PIP_AUTOINSTALL') == '1':
246
+ _auto_install(os.environ.get('AIMC_PIP_PACKAGES', ''))
247
+
248
+ try:
249
+ ${indentCode(params.code, 4)}
250
+ if 'result' not in locals():
251
+ result = items
252
+ print('__AIMC_RESULT__' + json.dumps(result, default=str))
253
+ except Exception:
254
+ traceback.print_exc()
255
+ sys.exit(1)
256
+ `;
257
+ const scriptPath = path.join(tempDir, 'script.py');
258
+ await fs.promises.writeFile(scriptPath, wrapper, 'utf8');
259
+ const env = {
260
+ ...process.env,
261
+ AIMC_INPUT_PATH: inputPath,
262
+ AIMC_PARAMS_PATH: paramsPath,
263
+ AIMC_MODE: params.mode,
264
+ AIMC_PIP_AUTOINSTALL: params.autoInstall ? '1' : '0',
265
+ AIMC_PIP_PACKAGES: params.packages,
266
+ };
267
+ const { stdout, stderr } = await execFileAsync(params.pythonPath, [scriptPath], {
268
+ env,
269
+ timeout: params.timeoutMs,
270
+ maxBuffer: 10 * 1024 * 1024,
271
+ });
272
+ const combined = `${stdout}\n${stderr}`.trim();
273
+ const marker = '__AIMC_RESULT__';
274
+ const markerIndex = combined.lastIndexOf(marker);
275
+ if (markerIndex === -1) {
276
+ throw new Error('Python did not return a result. Set `result` in your code.');
277
+ }
278
+ const jsonPayload = combined.slice(markerIndex + marker.length).trim();
279
+ return JSON.parse(jsonPayload);
280
+ }
281
+ finally {
282
+ await fs.promises.rm(tempDir, { recursive: true, force: true });
283
+ }
284
+ }
285
+ class AimcCode {
286
+ constructor() {
287
+ this.description = {
288
+ displayName: 'AIMC Code',
289
+ name: 'aimcCode',
290
+ icon: 'file:aimc-code.svg',
291
+ group: ['transform'],
292
+ version: 1,
293
+ description: 'Execute JavaScript with a curated toolkit of libraries.',
294
+ defaults: {
295
+ name: 'AIMC Code',
296
+ },
297
+ inputs: ['main'],
298
+ outputs: ['main'],
299
+ properties: [
300
+ {
301
+ displayName: 'Language',
302
+ name: 'language',
303
+ type: 'options',
304
+ options: [
305
+ { name: 'JavaScript', value: 'javascript' },
306
+ { name: 'Python', value: 'python' },
307
+ ],
308
+ default: 'javascript',
309
+ },
310
+ {
311
+ displayName: 'Mode',
312
+ name: 'mode',
313
+ type: 'options',
314
+ options: [
315
+ {
316
+ name: 'Run Once for All Items',
317
+ value: 'runOnceForAllItems',
318
+ },
319
+ {
320
+ name: 'Run Once for Each Item',
321
+ value: 'runOnceForEachItem',
322
+ },
323
+ ],
324
+ default: 'runOnceForAllItems',
325
+ },
326
+ {
327
+ displayName: 'Timeout (Seconds)',
328
+ name: 'timeoutSeconds',
329
+ type: 'number',
330
+ default: 30,
331
+ typeOptions: {
332
+ minValue: 1,
333
+ maxValue: 300,
334
+ },
335
+ },
336
+ {
337
+ displayName: 'JavaScript Code',
338
+ name: 'code',
339
+ type: 'string',
340
+ typeOptions: {
341
+ editor: 'jsEditor',
342
+ },
343
+ displayOptions: {
344
+ show: {
345
+ language: ['javascript'],
346
+ },
347
+ },
348
+ default: `// AIMC Code node\n// Use libs.<name> or direct globals like axios, _, zod\n\n// Example: transform all items\nconst data = $input.all().map((entry) => entry.json);\nconst ids = data.map((row) => nanoid());\n\nreturn data.map((row, index) => ({\n ...row,\n id: ids[index],\n processedAt: utils.now(),\n}));\n`,
349
+ description: 'Write JavaScript to transform data.',
350
+ noDataExpression: true,
351
+ },
352
+ {
353
+ displayName: 'Python Code',
354
+ name: 'pythonCode',
355
+ type: 'string',
356
+ typeOptions: {
357
+ editor: 'jsEditor',
358
+ editorLanguage: 'python',
359
+ },
360
+ displayOptions: {
361
+ show: {
362
+ language: ['python'],
363
+ },
364
+ },
365
+ default: `# AIMC Python\n# Access items (list of dicts), item (first item), and params (node params)\n# Set a variable named result to return output\n\nresult = {\n \"count\": len(items),\n \"first\": items[0] if items else None\n}\n`,
366
+ description: 'Write Python to transform data. Set a variable named result.',
367
+ noDataExpression: true,
368
+ },
369
+ {
370
+ displayName: 'Python Path',
371
+ name: 'pythonPath',
372
+ type: 'string',
373
+ default: 'python3',
374
+ displayOptions: {
375
+ show: {
376
+ language: ['python'],
377
+ },
378
+ },
379
+ description: 'Path to Python executable (e.g. python3 or /usr/bin/python3).',
380
+ },
381
+ {
382
+ displayName: 'Python Packages (comma separated)',
383
+ name: 'pythonPackages',
384
+ type: 'string',
385
+ default: 'requests,pandas,numpy,beautifulsoup4,lxml,python-dateutil,pytz,pydantic,pyyaml,xmltodict',
386
+ displayOptions: {
387
+ show: {
388
+ language: ['python'],
389
+ },
390
+ },
391
+ description: 'Optional packages to install when auto-install is enabled.',
392
+ },
393
+ {
394
+ displayName: 'Auto-install Python Packages',
395
+ name: 'pythonAutoInstall',
396
+ type: 'boolean',
397
+ default: false,
398
+ displayOptions: {
399
+ show: {
400
+ language: ['python'],
401
+ },
402
+ },
403
+ description: 'Install missing packages using pip before execution.',
404
+ },
405
+ ],
406
+ };
407
+ }
408
+ async execute() {
409
+ const items = this.getInputData();
410
+ const language = this.getNodeParameter('language', 0, 'javascript');
411
+ const mode = this.getNodeParameter('mode', 0, 'runOnceForAllItems');
412
+ const timeoutSeconds = this.getNodeParameter('timeoutSeconds', 0, 30);
413
+ const timeoutMs = Math.max(1, timeoutSeconds) * 1000;
414
+ const nodeParams = this.getNode().parameters;
415
+ if (language === 'python') {
416
+ const pythonCode = this.getNodeParameter('pythonCode', 0, '');
417
+ if (!pythonCode.trim()) {
418
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No Python code provided.');
419
+ }
420
+ const pythonPath = this.getNodeParameter('pythonPath', 0, 'python3');
421
+ const pythonPackages = this.getNodeParameter('pythonPackages', 0, '');
422
+ const autoInstall = this.getNodeParameter('pythonAutoInstall', 0, false);
423
+ try {
424
+ if (mode === 'runOnceForEachItem') {
425
+ const results = [];
426
+ for (let index = 0; index < items.length; index++) {
427
+ const item = items[index];
428
+ const result = await runPython({
429
+ pythonPath,
430
+ code: pythonCode,
431
+ items: [item],
432
+ mode,
433
+ timeoutMs,
434
+ nodeParams,
435
+ autoInstall,
436
+ packages: pythonPackages,
437
+ });
438
+ const normalized = normalizeResult(result, [item]);
439
+ results.push(...normalized);
440
+ }
441
+ return [results];
442
+ }
443
+ const result = await runPython({
444
+ pythonPath,
445
+ code: pythonCode,
446
+ items,
447
+ mode,
448
+ timeoutMs,
449
+ nodeParams,
450
+ autoInstall,
451
+ packages: pythonPackages,
452
+ });
453
+ const normalized = normalizeResult(result, items);
454
+ return [normalized];
455
+ }
456
+ catch (error) {
457
+ const message = error instanceof Error ? error.message : 'Unknown error';
458
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Python execution failed: ${message}`);
459
+ }
460
+ }
461
+ const code = this.getNodeParameter('code', 0, '');
462
+ if (!code.trim()) {
463
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No code provided.');
464
+ }
465
+ const runCode = async (sandbox) => {
466
+ const context = (0, vm_1.createContext)(sandbox);
467
+ const wrapped = `(async () => {\n${code}\n})()`;
468
+ return (0, vm_1.runInContext)(wrapped, context, { timeout: timeoutMs });
469
+ };
470
+ try {
471
+ if (mode === 'runOnceForEachItem') {
472
+ const results = [];
473
+ for (let index = 0; index < items.length; index++) {
474
+ const item = items[index];
475
+ const sandbox = buildSandbox({
476
+ items: [item],
477
+ item,
478
+ mode,
479
+ nodeParams,
480
+ });
481
+ const result = await runCode(sandbox);
482
+ const normalized = normalizeResult(result, [item]);
483
+ results.push(...normalized);
484
+ }
485
+ return [results];
486
+ }
487
+ const sandbox = buildSandbox({
488
+ items,
489
+ mode,
490
+ nodeParams,
491
+ });
492
+ const result = await runCode(sandbox);
493
+ const normalized = normalizeResult(result, items);
494
+ return [normalized];
495
+ }
496
+ catch (error) {
497
+ const message = error instanceof Error ? error.message : 'Unknown error';
498
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Code execution failed: ${message}`);
499
+ }
500
+ }
501
+ }
502
+ exports.AimcCode = AimcCode;
@@ -0,0 +1,31 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
2
+ <defs>
3
+ <linearGradient id="aimc-eye-bg" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0%" stop-color="#0A1021"/>
5
+ <stop offset="100%" stop-color="#0D1B3A"/>
6
+ </linearGradient>
7
+ <radialGradient id="aimc-eye-core" cx="50%" cy="50%" r="50%">
8
+ <stop offset="0%" stop-color="#7DD3FC"/>
9
+ <stop offset="65%" stop-color="#38BDF8"/>
10
+ <stop offset="100%" stop-color="#0EA5E9"/>
11
+ </radialGradient>
12
+ </defs>
13
+ <rect x="6" y="6" width="52" height="52" rx="10" fill="url(#aimc-eye-bg)"/>
14
+ <path d="M12 32C18 22 26 18 32 18C38 18 46 22 52 32C46 42 38 46 32 46C26 46 18 42 12 32Z" fill="none" stroke="#38BDF8" stroke-width="2.5"/>
15
+ <circle cx="32" cy="32" r="9.5" fill="url(#aimc-eye-core)" opacity="0.9"/>
16
+ <circle cx="32" cy="32" r="3.5" fill="#0B1223"/>
17
+ <g stroke="#1E3A8A" stroke-width="1.5" stroke-linecap="round" opacity="0.8">
18
+ <path d="M20 14V26"/>
19
+ <path d="M24 12V24"/>
20
+ <path d="M28 10V22"/>
21
+ <path d="M36 10V22"/>
22
+ <path d="M40 12V24"/>
23
+ <path d="M44 14V26"/>
24
+ <path d="M20 38V50"/>
25
+ <path d="M24 40V52"/>
26
+ <path d="M28 42V54"/>
27
+ <path d="M36 42V54"/>
28
+ <path d="M40 40V52"/>
29
+ <path d="M44 38V50"/>
30
+ </g>
31
+ </svg>
@@ -0,0 +1,5 @@
1
+ import { INodeExecutionData, INodeType, INodeTypeDescription, IExecuteFunctions } from 'n8n-workflow';
2
+ export declare class AimcMedia implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }