@loopthink/n8n-nodes-loopthink 0.15.0 → 0.17.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/README.md +32 -31
- package/dist/nodes/Loopthink/result.operation.js +10 -4
- package/dist/nodes/Loopthink/result.operation.js.map +1 -1
- package/dist/nodes/LoopthinkRunner/LoopthinkRunner.node.js +2 -89
- package/dist/nodes/LoopthinkRunner/LoopthinkRunner.node.js.map +1 -1
- package/dist/nodes/LoopthinkRunner/socket.js +2 -2
- package/dist/nodes/LoopthinkRunner/socket.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ is not an option.
|
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
Claude ──► loopthink ──► queue ──┐
|
|
13
|
-
│ n8n
|
|
13
|
+
│ pushed to n8n (outbound only)
|
|
14
14
|
▼
|
|
15
15
|
[loopthink Runner] ──► your internal API
|
|
16
16
|
│
|
|
@@ -44,21 +44,16 @@ over Postgres, and over an HTTP API.
|
|
|
44
44
|
**loopthink** is what the workflow does with the rest: *Build Index Page* and
|
|
45
45
|
*Send Result*.
|
|
46
46
|
|
|
47
|
-
###
|
|
47
|
+
### How work arrives
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
| Latency added | half the poll interval | none |
|
|
53
|
-
| Needs | plain HTTPS | a network that allows WebSocket upgrades |
|
|
54
|
-
| Cost to loopthink | ~$6 per runner/month | ~$0.04 |
|
|
49
|
+
The Runner holds **one outbound WebSocket** and is pushed each call the moment it
|
|
50
|
+
is queued. There is nothing to configure: no poll interval, so no latency added
|
|
51
|
+
to a tool call, and no request paid for while there is nothing to do.
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
attempts, that is the answer, and Polling is the fallback. Corporate proxies and
|
|
61
|
-
TLS inspection are the usual reason an upgrade fails.
|
|
53
|
+
What it needs is a network that allows an outbound WebSocket upgrade to the
|
|
54
|
+
loopthink queue host. If one cannot be established, the log says so after three
|
|
55
|
+
attempts, which is the symptom of a proxy or of TLS inspection breaking the
|
|
56
|
+
upgrade. Where that cannot be changed, the Docker runner is the way in.
|
|
62
57
|
|
|
63
58
|
## Install
|
|
64
59
|
|
|
@@ -74,11 +69,12 @@ Self-hosted n8n only, as community nodes generally are.
|
|
|
74
69
|
3. In n8n, create a **loopthink Runner API** credential (the node's
|
|
75
70
|
**Authentication** slot) and paste the four values. Saving it runs a
|
|
76
71
|
connection check against the queue, so a wrong value shows up right away.
|
|
77
|
-
4. Add
|
|
72
|
+
4. Add the **loopthink Runner** node to a workflow and **activate** it.
|
|
78
73
|
|
|
79
|
-
That last step matters:
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
That last step matters: the connection to loopthink lives for as long as the
|
|
75
|
+
workflow is active, and *Test workflow* in the editor only holds it while the tab
|
|
76
|
+
waits. A runner that reads as offline in loopthink is almost always a workflow
|
|
77
|
+
nobody activated.
|
|
82
78
|
|
|
83
79
|
### Credentials for your own systems
|
|
84
80
|
|
|
@@ -93,18 +89,21 @@ were never anywhere near us to begin with.
|
|
|
93
89
|
|
|
94
90
|
## How it behaves
|
|
95
91
|
|
|
96
|
-
- **
|
|
97
|
-
created while there is nothing to do. A Schedule trigger at one
|
|
98
|
-
produce ~2.6 million executions a month, over 99% of them finding
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
- **A connection, not a schedule.** The socket lives inside the node, so no
|
|
93
|
+
execution is created while there is nothing to do. A Schedule trigger at one
|
|
94
|
+
second would produce ~2.6 million executions a month, over 99% of them finding
|
|
95
|
+
nothing.
|
|
96
|
+
- **No latency of its own.** A call is pushed as it is queued, so nothing is
|
|
97
|
+
added to it by waiting for the next look.
|
|
98
|
+
- **Bursts are handled without waiting.** Each pushed call is emitted as it
|
|
99
|
+
arrives, however many arrive together.
|
|
103
100
|
- **Executions are your audit trail.** One per tool call, with the tool name, the
|
|
104
101
|
URL, the status and how many masking rules applied. Turn it off with
|
|
105
102
|
*Emit Results* if you would rather keep the list clean.
|
|
106
|
-
- **Transient failures do not stop it.** A
|
|
107
|
-
|
|
103
|
+
- **Transient failures do not stop it.** A dropped socket is logged and
|
|
104
|
+
reconnected with a backoff; API Gateway closes every connection eventually
|
|
105
|
+
anyway, so that is ordinary operation rather than a fault. Stopping would leave
|
|
106
|
+
the runner silently dead until someone noticed.
|
|
108
107
|
|
|
109
108
|
## The loopthink node
|
|
110
109
|
|
|
@@ -164,11 +163,15 @@ See [examples/](examples) for three sources worked through.
|
|
|
164
163
|
|
|
165
164
|
### When an answer is too large
|
|
166
165
|
|
|
167
|
-
One answer carries
|
|
166
|
+
One answer carries 5 MB. Past that **Send Result** does not send the rows at
|
|
168
167
|
all: it answers with a sentence naming the size and the row count and saying to
|
|
169
168
|
narrow the filters or pass a smaller limit, which a model acts on by splitting
|
|
170
169
|
its question and asking again.
|
|
171
170
|
|
|
171
|
+
Five megabytes is what fits through the way back to the client, not what a model
|
|
172
|
+
reads comfortably: it is around a million tokens. A tool that answers with the
|
|
173
|
+
columns a question needs, rather than every column it has, stays far below it.
|
|
174
|
+
|
|
172
175
|
Measured before the POST on purpose. Sent anyway, an oversized body is rejected
|
|
173
176
|
by whichever layer notices first, the call stays unanswered, and the caller waits
|
|
174
177
|
out its timeout to learn nothing at all.
|
|
@@ -189,9 +192,7 @@ for HTTP tools **without** a scope, or it will silently do nothing.
|
|
|
189
192
|
## Scope
|
|
190
193
|
|
|
191
194
|
HTTP tools are executed by the node; anything else is a workflow tool your own
|
|
192
|
-
branch answers.
|
|
193
|
-
nothing but latency and cost. HubSpot and Salesforce importers are not executed
|
|
194
|
-
here.
|
|
195
|
+
branch answers. HubSpot and Salesforce importers are not executed here.
|
|
195
196
|
|
|
196
197
|
## Develop
|
|
197
198
|
|
|
@@ -16,12 +16,18 @@ function pagePayload(ctx, respondWith, items) {
|
|
|
16
16
|
const capped = Number.isFinite(limit) && limit > 0;
|
|
17
17
|
return { items: capped ? rows.slice(0, limit) : rows, truncated: capped && rows.length > limit };
|
|
18
18
|
}
|
|
19
|
-
const MAX_RESULT_BYTES =
|
|
19
|
+
const MAX_RESULT_BYTES = 5 * 1024 * 1024;
|
|
20
20
|
const RESULT_TOO_LARGE_STATUS = 413;
|
|
21
|
+
function formatBytes(n) {
|
|
22
|
+
if (n >= 1024 * 1024) {
|
|
23
|
+
const mb = n / (1024 * 1024);
|
|
24
|
+
return `${Number.isInteger(mb) ? mb : mb.toFixed(1)} MB`;
|
|
25
|
+
}
|
|
26
|
+
return `${Math.round(n / 1024)} KB`;
|
|
27
|
+
}
|
|
21
28
|
function tooLargeMessage(bytes, rows) {
|
|
22
|
-
const kb = (n) => `${Math.round(n / 1024)} KB`;
|
|
23
29
|
const counted = rows === undefined ? '' : `${rows.toLocaleString('en-US')} rows, `;
|
|
24
|
-
return (`The result was too large to return: ${counted}${
|
|
30
|
+
return (`The result was too large to return: ${counted}${formatBytes(bytes)}, and one answer can carry ${formatBytes(MAX_RESULT_BYTES)}. ` +
|
|
25
31
|
'Nothing was returned. Ask for a smaller slice and combine the answers: narrow the filters, for instance a ' +
|
|
26
32
|
'shorter date range or a more specific match, or pass a smaller limit if this tool takes one.');
|
|
27
33
|
}
|
|
@@ -54,7 +60,7 @@ exports.resultFields = [
|
|
|
54
60
|
displayName: 'Respond With',
|
|
55
61
|
name: 'respondWith',
|
|
56
62
|
type: 'options',
|
|
57
|
-
default: '
|
|
63
|
+
default: 'list',
|
|
58
64
|
options: [
|
|
59
65
|
{
|
|
60
66
|
name: 'One Object',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.operation.js","sourceRoot":"","sources":["../../../nodes/Loopthink/result.operation.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"result.operation.js","sourceRoot":"","sources":["../../../nodes/Loopthink/result.operation.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,6 @@ class LoopthinkRunner {
|
|
|
12
12
|
icon: 'file:loopthink-mark.svg',
|
|
13
13
|
group: ['trigger'],
|
|
14
14
|
version: 1,
|
|
15
|
-
subtitle: '={{$parameter["transport"] === "websocket" ? "pushed" : $parameter["pollInterval"] + "s poll"}}',
|
|
16
15
|
description: 'Claims loopthink MCP tool calls and executes the HTTP ones inside your network',
|
|
17
16
|
defaults: { name: 'loopthink Runner' },
|
|
18
17
|
inputs: ['main'],
|
|
@@ -21,34 +20,6 @@ class LoopthinkRunner {
|
|
|
21
20
|
{ name: 'loopthinkRunnerApi', required: true, displayName: 'Authentication' },
|
|
22
21
|
],
|
|
23
22
|
properties: [
|
|
24
|
-
{
|
|
25
|
-
displayName: 'Transport',
|
|
26
|
-
name: 'transport',
|
|
27
|
-
type: 'options',
|
|
28
|
-
noDataExpression: true,
|
|
29
|
-
default: 'polling',
|
|
30
|
-
options: [
|
|
31
|
-
{
|
|
32
|
-
name: 'Polling',
|
|
33
|
-
value: 'polling',
|
|
34
|
-
description: 'Asks for work over plain HTTPS. Works through any proxy.',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: 'WebSocket',
|
|
38
|
-
value: 'websocket',
|
|
39
|
-
description: 'Holds one outbound connection and is pushed work the moment it is queued. No poll latency, far cheaper, but the network has to allow WebSocket upgrades.',
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
displayName: 'Poll Interval (Seconds)',
|
|
45
|
-
displayOptions: { show: { transport: ['polling'] } },
|
|
46
|
-
name: 'pollInterval',
|
|
47
|
-
type: 'number',
|
|
48
|
-
typeOptions: { minValue: 10, maxValue: 300 },
|
|
49
|
-
default: 10,
|
|
50
|
-
description: 'How often to check for work. This is the latency added to every tool call, and every poll is a request you pay for. Ten seconds is the floor; use WebSocket if the wait matters, because a pushed call has none of it.',
|
|
51
|
-
},
|
|
52
23
|
{
|
|
53
24
|
displayName: 'Every claimed call leaves here. Route it with a Switch on <code>{{$json.tool}}</code>: a workflow tool goes to the branch that answers it, an HTTP tool to a branch with <b>loopthink → Execute Request</b>. Every branch ends in <b>loopthink → Send Result</b>. Give the Switch a fallback, or a tool nobody answers leaves the caller waiting out its timeout.',
|
|
54
25
|
name: 'workflowNotice',
|
|
@@ -60,8 +31,6 @@ class LoopthinkRunner {
|
|
|
60
31
|
}
|
|
61
32
|
async trigger() {
|
|
62
33
|
const credentials = await this.getCredentials('loopthinkRunnerApi');
|
|
63
|
-
const pollInterval = Math.max(10, this.getNodeParameter('pollInterval', 10)) * 1000;
|
|
64
|
-
const transport = this.getNodeParameter('transport', 'polling');
|
|
65
34
|
const queueUrl = String(credentials.queueUrl || '').replace(/\/+$/, '');
|
|
66
35
|
const workspaceId = String(credentials.workspaceId);
|
|
67
36
|
const groupId = String(credentials.groupId);
|
|
@@ -76,67 +45,11 @@ class LoopthinkRunner {
|
|
|
76
45
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${label} is missing from the loopthink Runner credential`, { description: 'Open the credential and fill it in. The values are shown when the runner is created in loopthink.' });
|
|
77
46
|
}
|
|
78
47
|
}
|
|
79
|
-
const base = `${queueUrl}/group/${workspaceId}/${groupId}/runner`;
|
|
80
|
-
let stopped = false;
|
|
81
|
-
let timer;
|
|
82
|
-
const claim = async () => {
|
|
83
|
-
const response = await this.helpers.httpRequest({
|
|
84
|
-
method: 'POST',
|
|
85
|
-
url: `${base}/next`,
|
|
86
|
-
headers: { Authorization: `Bearer ${secret}` },
|
|
87
|
-
json: true,
|
|
88
|
-
returnFullResponse: true,
|
|
89
|
-
ignoreHttpStatusErrors: true,
|
|
90
|
-
});
|
|
91
|
-
if (response.statusCode === 204)
|
|
92
|
-
return null;
|
|
93
|
-
if (response.statusCode !== 200) {
|
|
94
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), response.body, {
|
|
95
|
-
message: `loopthink returned ${response.statusCode} when claiming work`,
|
|
96
|
-
description: 'Check the runner secret and the queue URL on the credential. A 401 means the secret was revoked or belongs to another server.',
|
|
97
|
-
httpCode: String(response.statusCode),
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return response.body;
|
|
101
|
-
};
|
|
102
48
|
const handle = async (job) => {
|
|
103
49
|
(0, workflowTools_1.emitJob)(this, job);
|
|
104
50
|
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return { closeFunction: async () => socket.close() };
|
|
108
|
-
}
|
|
109
|
-
const tick = async () => {
|
|
110
|
-
if (stopped)
|
|
111
|
-
return;
|
|
112
|
-
try {
|
|
113
|
-
const job = await claim();
|
|
114
|
-
if (job) {
|
|
115
|
-
await handle(job);
|
|
116
|
-
if (!stopped) {
|
|
117
|
-
setImmediate(() => void tick());
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
this.logger.warn(`loopthink Runner: ${error.message}`);
|
|
124
|
-
}
|
|
125
|
-
if (!stopped) {
|
|
126
|
-
timer = setTimeout(() => void tick(), pollInterval);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
void tick();
|
|
130
|
-
return {
|
|
131
|
-
closeFunction: async () => {
|
|
132
|
-
stopped = true;
|
|
133
|
-
if (timer)
|
|
134
|
-
clearTimeout(timer);
|
|
135
|
-
},
|
|
136
|
-
manualTriggerFunction: async () => {
|
|
137
|
-
await tick();
|
|
138
|
-
},
|
|
139
|
-
};
|
|
51
|
+
const socket = await (0, socket_1.startSocket)(this, { queueUrl, workspaceId, groupId, secret, handle });
|
|
52
|
+
return { closeFunction: async () => socket.close() };
|
|
140
53
|
}
|
|
141
54
|
}
|
|
142
55
|
exports.LoopthinkRunner = LoopthinkRunner;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoopthinkRunner.node.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/LoopthinkRunner.node.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"LoopthinkRunner.node.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/LoopthinkRunner.node.ts"],"names":[],"mappings":";;;AAMA,+CAAkD;AAElD,qCAAuC;AACvC,mDAA8E;AAwB9E,MAAa,eAAe;IAA5B;QACC,gBAAW,GAAyB;YACnC,WAAW,EAAE,kBAAkB;YAC/B,IAAI,EAAE,iBAAiB;YAKvB,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,CAAC,SAAS,CAAC;YAClB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,gFAAgF;YAC7F,QAAQ,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE;YAKtC,MAAM,EAAE,CAAC,MAAM,CAAC;YAGhB,OAAO,EAAE,8BAAc;YACvB,WAAW,EAAE;gBACZ,EAAE,IAAI,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE;aAC7E;YACD,UAAU,EAAE;gBACX;oBAGC,WAAW,EACV,mWAAmW;oBACpW,IAAI,EAAE,gBAAgB;oBACtB,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,EAAE;iBACX;aACD;SACD,CAAC;IAmCH,CAAC;IAjCA,KAAK,CAAC,OAAO;QACZ,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;QAEpE,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAI1C,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI;YAC5B,CAAC,WAAW,EAAE,QAAQ,CAAC;YACvB,CAAC,cAAc,EAAE,WAAW,CAAC;YAC7B,CAAC,UAAU,EAAE,OAAO,CAAC;YACrB,CAAC,QAAQ,EAAE,MAAM,CAAC;SACT,EAAE,CAAC;YACZ,IAAI,CAAC,KAAK,EAAE,CAAC;gBACZ,MAAM,IAAI,iCAAkB,CAC3B,IAAI,CAAC,OAAO,EAAE,EACd,GAAG,KAAK,kDAAkD,EAC1D,EAAE,WAAW,EAAE,mGAAmG,EAAE,CACpH,CAAC;YACH,CAAC;QACF,CAAC;QAGD,MAAM,MAAM,GAAG,KAAK,EAAE,GAAkB,EAAiB,EAAE;YAC1D,IAAA,uBAAO,EAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACpB,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAW,EAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3F,OAAO,EAAE,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;IACtD,CAAC;CACD;AAtED,0CAsEC"}
|
|
@@ -18,7 +18,7 @@ async function startSocket(ctx, { queueUrl, workspaceId, groupId, secret, handle
|
|
|
18
18
|
}));
|
|
19
19
|
if (!ping?.wsUrl) {
|
|
20
20
|
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'This loopthink deployment has no WebSocket transport', {
|
|
21
|
-
description: '
|
|
21
|
+
description: 'Ask for the transport to be enabled on this deployment; the runner needs it to receive work.',
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
const wsUrl = `${ping.wsUrl}` +
|
|
@@ -68,7 +68,7 @@ async function startSocket(ctx, { queueUrl, workspaceId, groupId, secret, handle
|
|
|
68
68
|
if (!everConnected && failedAttempts === 3) {
|
|
69
69
|
ctx.logger.error('loopthink Runner: could not establish a WebSocket connection after three attempts. ' +
|
|
70
70
|
'This network most likely does not allow WebSocket upgrades (a proxy, or TLS inspection breaking them). ' +
|
|
71
|
-
'
|
|
71
|
+
'Allow an outbound WebSocket upgrade to the loopthink queue host, or run the Docker runner instead.');
|
|
72
72
|
}
|
|
73
73
|
reconnectTimer = setTimeout(connect, backoff);
|
|
74
74
|
backoff = Math.min(backoff * 2, RECONNECT_MAX_MS);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/socket.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/socket.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loopthink/n8n-nodes-loopthink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
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 — no inbound connection required.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|