@loopthink/n8n-nodes-loopthink 0.9.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 +21 -0
- package/README.md +259 -0
- package/dist/credentials/LoopthinkRunnerApi.credentials.d.ts +8 -0
- package/dist/credentials/LoopthinkRunnerApi.credentials.js +53 -0
- package/dist/credentials/LoopthinkRunnerApi.credentials.js.map +1 -0
- package/dist/credentials/LoopthinkTargetApi.credentials.d.ts +7 -0
- package/dist/credentials/LoopthinkTargetApi.credentials.js +46 -0
- package/dist/credentials/LoopthinkTargetApi.credentials.js.map +1 -0
- package/dist/nodes/Loopthink/Loopthink.node.d.ts +5 -0
- package/dist/nodes/Loopthink/Loopthink.node.js +87 -0
- package/dist/nodes/Loopthink/Loopthink.node.js.map +1 -0
- package/dist/nodes/Loopthink/execute.operation.d.ts +3 -0
- package/dist/nodes/Loopthink/execute.operation.js +89 -0
- package/dist/nodes/Loopthink/execute.operation.js.map +1 -0
- package/dist/nodes/Loopthink/loopthink-mark.svg +1 -0
- package/dist/nodes/Loopthink/prepare.operation.d.ts +3 -0
- package/dist/nodes/Loopthink/prepare.operation.js +144 -0
- package/dist/nodes/Loopthink/prepare.operation.js.map +1 -0
- package/dist/nodes/Loopthink/query.d.ts +29 -0
- package/dist/nodes/Loopthink/query.js +63 -0
- package/dist/nodes/Loopthink/query.js.map +1 -0
- package/dist/nodes/Loopthink/result.operation.d.ts +3 -0
- package/dist/nodes/Loopthink/result.operation.js +137 -0
- package/dist/nodes/Loopthink/result.operation.js.map +1 -0
- package/dist/nodes/LoopthinkRunner/LoopthinkRunner.node.d.ts +5 -0
- package/dist/nodes/LoopthinkRunner/LoopthinkRunner.node.js +143 -0
- package/dist/nodes/LoopthinkRunner/LoopthinkRunner.node.js.map +1 -0
- package/dist/nodes/LoopthinkRunner/loopthink-mark.svg +1 -0
- package/dist/nodes/LoopthinkRunner/masking.d.ts +10 -0
- package/dist/nodes/LoopthinkRunner/masking.js +95 -0
- package/dist/nodes/LoopthinkRunner/masking.js.map +1 -0
- package/dist/nodes/LoopthinkRunner/secrets.d.ts +7 -0
- package/dist/nodes/LoopthinkRunner/secrets.js +63 -0
- package/dist/nodes/LoopthinkRunner/secrets.js.map +1 -0
- package/dist/nodes/LoopthinkRunner/socket.d.ts +13 -0
- package/dist/nodes/LoopthinkRunner/socket.js +94 -0
- package/dist/nodes/LoopthinkRunner/socket.js.map +1 -0
- package/dist/nodes/LoopthinkRunner/workflowTools.d.ts +26 -0
- package/dist/nodes/LoopthinkRunner/workflowTools.js +27 -0
- package/dist/nodes/LoopthinkRunner/workflowTools.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rulesForScope = rulesForScope;
|
|
4
|
+
exports.applyMasking = applyMasking;
|
|
5
|
+
const PATTERN_REGEX = {
|
|
6
|
+
email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
7
|
+
iban: /\b[A-Z]{2}\d{2}[A-Z0-9]{10,30}\b/g,
|
|
8
|
+
phone: /(?:\+\d{1,3}[\s-]?)?(?:\(?\d{2,4}\)?[\s-]?){2,5}\d{2,4}/g,
|
|
9
|
+
};
|
|
10
|
+
function pseudonymize(value) {
|
|
11
|
+
let hash = 0;
|
|
12
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
13
|
+
hash = (hash * 31 + value.charCodeAt(i)) | 0;
|
|
14
|
+
}
|
|
15
|
+
return `pseudo_${(hash >>> 0).toString(36)}`;
|
|
16
|
+
}
|
|
17
|
+
function maskValue(value) {
|
|
18
|
+
if (value.length <= 2)
|
|
19
|
+
return '*'.repeat(value.length);
|
|
20
|
+
return `${value[0]}${'*'.repeat(Math.max(1, value.length - 2))}${value[value.length - 1]}`;
|
|
21
|
+
}
|
|
22
|
+
function applyStrategyToString(value, strategy) {
|
|
23
|
+
switch (strategy) {
|
|
24
|
+
case 'remove':
|
|
25
|
+
return undefined;
|
|
26
|
+
case 'pseudonymize':
|
|
27
|
+
return pseudonymize(value);
|
|
28
|
+
case 'mask':
|
|
29
|
+
default:
|
|
30
|
+
return maskValue(value);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function applyPatternToString(value, rule) {
|
|
34
|
+
const regex = PATTERN_REGEX[rule.pattern];
|
|
35
|
+
if (!regex)
|
|
36
|
+
return value;
|
|
37
|
+
return value.replace(regex, (match) => {
|
|
38
|
+
const replaced = applyStrategyToString(match, rule.strategy);
|
|
39
|
+
return replaced === undefined ? '' : replaced;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function normalizeFieldRules(rules) {
|
|
43
|
+
const map = new Map();
|
|
44
|
+
rules.filter((r) => r.field).forEach((r) => map.set(String(r.field).toLowerCase(), r));
|
|
45
|
+
return map;
|
|
46
|
+
}
|
|
47
|
+
function normalizeScope(value) {
|
|
48
|
+
return value
|
|
49
|
+
.toLowerCase()
|
|
50
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
51
|
+
.replace(/^_+|_+$/g, '');
|
|
52
|
+
}
|
|
53
|
+
function rulesForScope(rules, scope) {
|
|
54
|
+
if (!rules || rules.length === 0)
|
|
55
|
+
return [];
|
|
56
|
+
const normalized = scope ? normalizeScope(scope) : undefined;
|
|
57
|
+
return rules.filter((r) => !r.scope || (normalized !== undefined && normalizeScope(r.scope) === normalized));
|
|
58
|
+
}
|
|
59
|
+
function applyMasking(data, rules) {
|
|
60
|
+
if (!rules || rules.length === 0)
|
|
61
|
+
return data;
|
|
62
|
+
const fieldRules = normalizeFieldRules(rules);
|
|
63
|
+
const patternRules = rules.filter((r) => r.pattern);
|
|
64
|
+
const walk = (value, keyName) => {
|
|
65
|
+
if (value === null || value === undefined)
|
|
66
|
+
return value;
|
|
67
|
+
if (Array.isArray(value))
|
|
68
|
+
return value.map((item) => walk(item, keyName));
|
|
69
|
+
if (typeof value === 'object') {
|
|
70
|
+
const out = {};
|
|
71
|
+
Object.keys(value).forEach((k) => {
|
|
72
|
+
const rule = fieldRules.get(k.toLowerCase());
|
|
73
|
+
if (rule) {
|
|
74
|
+
if (rule.strategy === 'remove')
|
|
75
|
+
return;
|
|
76
|
+
if (value[k] === null || value[k] === undefined) {
|
|
77
|
+
out[k] = value[k];
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
out[k] = applyStrategyToString(String(value[k]), rule.strategy);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
out[k] = walk(value[k], k);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
if (typeof value === 'string') {
|
|
89
|
+
return patternRules.reduce((acc, rule) => applyPatternToString(acc, rule), value);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
};
|
|
93
|
+
return walk(data);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=masking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"masking.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/masking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare class MissingSecretError extends Error {
|
|
2
|
+
readonly names: string[];
|
|
3
|
+
constructor(names: string[]);
|
|
4
|
+
}
|
|
5
|
+
export declare function collectPlaceholders(value: unknown, found?: Set<string>): Set<string>;
|
|
6
|
+
export declare function substituteSecrets<T>(value: T, secrets: Record<string, string>): T;
|
|
7
|
+
export declare function secretsFromCredential(credential: unknown): Record<string, string>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MissingSecretError = void 0;
|
|
4
|
+
exports.collectPlaceholders = collectPlaceholders;
|
|
5
|
+
exports.substituteSecrets = substituteSecrets;
|
|
6
|
+
exports.secretsFromCredential = secretsFromCredential;
|
|
7
|
+
const PLACEHOLDER = /\{\{\s*secret\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
|
|
8
|
+
class MissingSecretError extends Error {
|
|
9
|
+
constructor(names) {
|
|
10
|
+
super(`Missing secret${names.length > 1 ? 's' : ''} in the runner credential: ${names.join(', ')}. ` +
|
|
11
|
+
'Add them under "loopthink Target Secrets".');
|
|
12
|
+
this.names = names;
|
|
13
|
+
this.name = 'MissingSecretError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.MissingSecretError = MissingSecretError;
|
|
17
|
+
function collectPlaceholders(value, found = new Set()) {
|
|
18
|
+
if (typeof value === 'string') {
|
|
19
|
+
for (const match of value.matchAll(PLACEHOLDER))
|
|
20
|
+
found.add(match[1]);
|
|
21
|
+
return found;
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
value.forEach((v) => collectPlaceholders(v, found));
|
|
25
|
+
return found;
|
|
26
|
+
}
|
|
27
|
+
if (value && typeof value === 'object') {
|
|
28
|
+
Object.values(value).forEach((v) => collectPlaceholders(v, found));
|
|
29
|
+
}
|
|
30
|
+
return found;
|
|
31
|
+
}
|
|
32
|
+
function substituteString(value, secrets) {
|
|
33
|
+
return value.replace(PLACEHOLDER, (_full, name) => secrets[name]);
|
|
34
|
+
}
|
|
35
|
+
function substituteSecrets(value, secrets) {
|
|
36
|
+
const missing = [...collectPlaceholders(value)].filter((name) => !secrets[name]);
|
|
37
|
+
if (missing.length)
|
|
38
|
+
throw new MissingSecretError(missing);
|
|
39
|
+
const walk = (v) => {
|
|
40
|
+
if (typeof v === 'string')
|
|
41
|
+
return substituteString(v, secrets);
|
|
42
|
+
if (Array.isArray(v))
|
|
43
|
+
return v.map(walk);
|
|
44
|
+
if (v && typeof v === 'object') {
|
|
45
|
+
return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));
|
|
46
|
+
}
|
|
47
|
+
return v;
|
|
48
|
+
};
|
|
49
|
+
return walk(value);
|
|
50
|
+
}
|
|
51
|
+
function secretsFromCredential(credential) {
|
|
52
|
+
const entries = credential?.secrets?.secret;
|
|
53
|
+
if (!Array.isArray(entries))
|
|
54
|
+
return {};
|
|
55
|
+
const out = {};
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const name = String(entry?.name || '').trim();
|
|
58
|
+
if (name && entry?.value)
|
|
59
|
+
out[name] = String(entry.value);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ITriggerFunctions } from 'n8n-workflow';
|
|
2
|
+
import type { QueuedRequest } from './workflowTools';
|
|
3
|
+
export interface SocketOptions {
|
|
4
|
+
queueUrl: string;
|
|
5
|
+
workspaceId: string;
|
|
6
|
+
groupId: string;
|
|
7
|
+
secret: string;
|
|
8
|
+
handle: (job: QueuedRequest) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface SocketTransport {
|
|
11
|
+
close: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function startSocket(ctx: ITriggerFunctions, { queueUrl, workspaceId, groupId, secret, handle }: SocketOptions): Promise<SocketTransport>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startSocket = startSocket;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const ws_1 = __importDefault(require("ws"));
|
|
9
|
+
const PING_INTERVAL_MS = 60 * 1000;
|
|
10
|
+
const RECONNECT_MIN_MS = 1000;
|
|
11
|
+
const RECONNECT_MAX_MS = 60000;
|
|
12
|
+
async function startSocket(ctx, { queueUrl, workspaceId, groupId, secret, handle }) {
|
|
13
|
+
const ping = (await ctx.helpers.httpRequest({
|
|
14
|
+
method: 'GET',
|
|
15
|
+
url: `${queueUrl}/group/${workspaceId}/${groupId}/runner/ping`,
|
|
16
|
+
headers: { Authorization: `Bearer ${secret}` },
|
|
17
|
+
json: true,
|
|
18
|
+
}));
|
|
19
|
+
if (!ping?.wsUrl) {
|
|
20
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'This loopthink deployment has no WebSocket transport', {
|
|
21
|
+
description: 'Switch Transport to Polling, or ask for the transport to be enabled.',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const wsUrl = `${ping.wsUrl}` +
|
|
25
|
+
`?workspaceId=${encodeURIComponent(workspaceId)}` +
|
|
26
|
+
`&groupId=${encodeURIComponent(groupId)}` +
|
|
27
|
+
`&secret=${encodeURIComponent(secret)}`;
|
|
28
|
+
let socket;
|
|
29
|
+
let pinger;
|
|
30
|
+
let reconnectTimer;
|
|
31
|
+
let backoff = RECONNECT_MIN_MS;
|
|
32
|
+
let stopped = false;
|
|
33
|
+
let everConnected = false;
|
|
34
|
+
let failedAttempts = 0;
|
|
35
|
+
const connect = () => {
|
|
36
|
+
if (stopped)
|
|
37
|
+
return;
|
|
38
|
+
socket = new ws_1.default(wsUrl);
|
|
39
|
+
socket.on('open', () => {
|
|
40
|
+
backoff = RECONNECT_MIN_MS;
|
|
41
|
+
everConnected = true;
|
|
42
|
+
failedAttempts = 0;
|
|
43
|
+
ctx.logger.info('loopthink Runner: WebSocket connected');
|
|
44
|
+
pinger = setInterval(() => {
|
|
45
|
+
if (socket?.readyState === ws_1.default.OPEN) {
|
|
46
|
+
socket.send(JSON.stringify({ type: 'ping' }));
|
|
47
|
+
}
|
|
48
|
+
}, PING_INTERVAL_MS);
|
|
49
|
+
});
|
|
50
|
+
socket.on('message', (raw) => {
|
|
51
|
+
let message;
|
|
52
|
+
try {
|
|
53
|
+
message = JSON.parse(raw.toString());
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (message?.type !== 'request' || !message.request)
|
|
59
|
+
return;
|
|
60
|
+
void handle(message.request).catch((e) => ctx.logger.warn(`loopthink Runner: ${e.message}`));
|
|
61
|
+
});
|
|
62
|
+
const scheduleReconnect = () => {
|
|
63
|
+
if (pinger)
|
|
64
|
+
clearInterval(pinger);
|
|
65
|
+
if (stopped)
|
|
66
|
+
return;
|
|
67
|
+
failedAttempts += 1;
|
|
68
|
+
if (!everConnected && failedAttempts === 3) {
|
|
69
|
+
ctx.logger.error('loopthink Runner: could not establish a WebSocket connection after three attempts. ' +
|
|
70
|
+
'This network most likely does not allow WebSocket upgrades (a proxy, or TLS inspection breaking them). ' +
|
|
71
|
+
'Switch Transport to Polling. It needs nothing but plain HTTPS.');
|
|
72
|
+
}
|
|
73
|
+
reconnectTimer = setTimeout(connect, backoff);
|
|
74
|
+
backoff = Math.min(backoff * 2, RECONNECT_MAX_MS);
|
|
75
|
+
};
|
|
76
|
+
socket.on('close', scheduleReconnect);
|
|
77
|
+
socket.on('error', (e) => {
|
|
78
|
+
ctx.logger.warn(`loopthink Runner: ${e.message}`);
|
|
79
|
+
socket?.close();
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
connect();
|
|
83
|
+
return {
|
|
84
|
+
close: () => {
|
|
85
|
+
stopped = true;
|
|
86
|
+
if (pinger)
|
|
87
|
+
clearInterval(pinger);
|
|
88
|
+
if (reconnectTimer)
|
|
89
|
+
clearTimeout(reconnectTimer);
|
|
90
|
+
socket?.close();
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=socket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/socket.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IDataObject, INodeOutputConfiguration, ITriggerFunctions } from 'n8n-workflow';
|
|
2
|
+
import type { MaskingRule } from './masking';
|
|
3
|
+
export interface HttpPayload {
|
|
4
|
+
kind?: 'http';
|
|
5
|
+
method: string;
|
|
6
|
+
url: string;
|
|
7
|
+
query?: Record<string, string>;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
body?: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface ParamsPayload {
|
|
12
|
+
kind: 'params';
|
|
13
|
+
params: IDataObject;
|
|
14
|
+
}
|
|
15
|
+
export interface QueuedRequest {
|
|
16
|
+
requestId: string;
|
|
17
|
+
tool: string;
|
|
18
|
+
request: HttpPayload | ParamsPayload;
|
|
19
|
+
masking: MaskingRule[];
|
|
20
|
+
scope?: string;
|
|
21
|
+
credentialRef?: string;
|
|
22
|
+
leaseUntil: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function isParamsRequest(request: QueuedRequest['request']): request is ParamsPayload;
|
|
25
|
+
export declare const RUNNER_OUTPUTS: INodeOutputConfiguration[];
|
|
26
|
+
export declare function emitJob(ctx: ITriggerFunctions, job: QueuedRequest): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RUNNER_OUTPUTS = void 0;
|
|
4
|
+
exports.isParamsRequest = isParamsRequest;
|
|
5
|
+
exports.emitJob = emitJob;
|
|
6
|
+
function isParamsRequest(request) {
|
|
7
|
+
return request?.kind === 'params';
|
|
8
|
+
}
|
|
9
|
+
exports.RUNNER_OUTPUTS = [
|
|
10
|
+
{ type: 'main' },
|
|
11
|
+
];
|
|
12
|
+
function emitJob(ctx, job) {
|
|
13
|
+
ctx.emit([
|
|
14
|
+
ctx.helpers.returnJsonArray([
|
|
15
|
+
{
|
|
16
|
+
requestId: job.requestId,
|
|
17
|
+
tool: job.tool,
|
|
18
|
+
request: job.request,
|
|
19
|
+
params: isParamsRequest(job.request) ? job.request.params : {},
|
|
20
|
+
masking: job.masking ?? [],
|
|
21
|
+
scope: job.scope ?? null,
|
|
22
|
+
leaseUntil: job.leaseUntil,
|
|
23
|
+
},
|
|
24
|
+
]),
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=workflowTools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowTools.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/workflowTools.ts"],"names":[],"mappings":";;;AAsDA,0CAEC;AAyBD,0BAcC;AAzCD,SAAgB,eAAe,CAAC,OAAiC;IAChE,OAAQ,OAAyB,EAAE,IAAI,KAAK,QAAQ,CAAC;AACtD,CAAC;AAWY,QAAA,cAAc,GAA+B;IAGzD,EAAE,IAAI,EAAE,MAA0C,EAAE;CACpD,CAAC;AAUF,SAAgB,OAAO,CAAC,GAAsB,EAAE,GAAkB;IACjE,GAAG,CAAC,IAAI,CAAC;QACR,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC;YAC3B;gBACC,SAAS,EAAE,GAAG,CAAC,SAAS;gBACxB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAiC;gBAC9C,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;gBAC9D,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE;gBAC1B,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,IAAI;gBACxB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC1B;SACD,CAAC;KACF,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loopthink/n8n-nodes-loopthink",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Run loopthink MCP tools from inside your own network. The node reaches out to loopthink, claims tool calls, executes them against your internal HTTP APIs and returns masked results \u2014 no inbound connection required.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"loopthink",
|
|
8
|
+
"mcp",
|
|
9
|
+
"runner"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://www.loopthink.ai",
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "loopthink",
|
|
15
|
+
"email": "info@loopthink.ai"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/loopthink/n8n-nodes-loopthink.git"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20.15"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "npx rimraf dist && tsc && gulp build:icons",
|
|
26
|
+
"prepublishOnly": "npm run lint && npm test && npm run build",
|
|
27
|
+
"dev": "npm run build && concurrently -n tsc,icons \"tsc --watch --preserveWatchOutput\" \"gulp watch:icons\"",
|
|
28
|
+
"format": "prettier nodes credentials --write",
|
|
29
|
+
"lint": "eslint nodes credentials package.json",
|
|
30
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
31
|
+
"test": "mocha -r ts-node/register 'nodes/**/*.spec.ts' --exit",
|
|
32
|
+
"docker:up": "npm run build && docker compose up -d && echo \"n8n \u2192 http://127.0.0.1:${LOOPTHINK_N8N_PORT:-5680} | echo target (from inside n8n) \u2192 http://echo:8080\"",
|
|
33
|
+
"docker:down": "docker compose down -v",
|
|
34
|
+
"docker:logs": "docker compose logs -f n8n",
|
|
35
|
+
"docker:restart": "npm run build && docker compose up -d --force-recreate n8n"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"n8n": {
|
|
43
|
+
"n8nNodesApiVersion": 1,
|
|
44
|
+
"credentials": [
|
|
45
|
+
"dist/credentials/LoopthinkRunnerApi.credentials.js",
|
|
46
|
+
"dist/credentials/LoopthinkTargetApi.credentials.js"
|
|
47
|
+
],
|
|
48
|
+
"nodes": [
|
|
49
|
+
"dist/nodes/LoopthinkRunner/LoopthinkRunner.node.js",
|
|
50
|
+
"dist/nodes/Loopthink/Loopthink.node.js"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/chai": "^4.3.11",
|
|
55
|
+
"@types/mocha": "^10.0.6",
|
|
56
|
+
"@types/ws": "^8.18.1",
|
|
57
|
+
"@typescript-eslint/parser": "^7.15.0",
|
|
58
|
+
"chai": "^4.4.1",
|
|
59
|
+
"concurrently": "^10.0.4",
|
|
60
|
+
"eslint": "^8.57.0",
|
|
61
|
+
"eslint-plugin-n8n-nodes-base": "^1.16.1",
|
|
62
|
+
"gulp": "^5.0.0",
|
|
63
|
+
"mocha": "^10.4.0",
|
|
64
|
+
"n8n-workflow": "*",
|
|
65
|
+
"prettier": "^3.3.2",
|
|
66
|
+
"rimraf": "^5.0.7",
|
|
67
|
+
"ts-node": "^10.9.2",
|
|
68
|
+
"typescript": "^5.5.3"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"n8n-workflow": "*"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"ws": "^8.21.3"
|
|
75
|
+
}
|
|
76
|
+
}
|