@loopthink/n8n-nodes-loopthink 0.12.0 → 0.14.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 CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Run [loopthink](https://www.loopthink.ai) MCP tools from inside your own network.
4
4
 
5
- The node reaches out to loopthink, claims tool calls, executes them against your
6
- internal HTTP APIs, masks the results locally and sends them back. **Nothing has
5
+ The node reaches out to loopthink, claims tool calls, hands them to your
6
+ workflow, masks whatever it answers with and sends that back. **Nothing has
7
7
  to reach your n8n from the internet**, which is the whole point: it exists for
8
8
  environments where the Docker runner cannot be deployed because inbound traffic
9
9
  is not an option.
@@ -24,15 +24,16 @@ Claude ──► loopthink ──► queue ──┐
24
24
  (an n8n Data Table, a Sheet, a database, any node-only integration), so it sends
25
25
  the validated arguments instead.
26
26
 
27
- Both leave the Runner on the same output. A Switch on `{{$json.tool}}` decides
28
- what happens: a workflow tool goes to the branch that answers it, an HTTP tool to
29
- a branch with **loopthink → Execute Request**, and every branch ends in
27
+ Every call leaves the Runner on the same output. A Switch on `{{$json.tool}}`
28
+ sends each tool to the branch that answers it, and every branch ends in
30
29
  **loopthink → Send Result**. Give the Switch a fallback, or a tool nobody answers
31
30
  leaves the caller waiting out its timeout.
32
31
 
33
- One branch covers every HTTP tool of a server, because the call is already
34
- resolved: point Execute Request at `{{ $json.request }}` and it runs whichever
35
- one arrived.
32
+ What a branch does is your decision, because a tool carries what it needs. A
33
+ parameter the author pinned down arrives alongside the ones the model filled: a
34
+ table name, a statement, a path. So a Postgres branch reads
35
+ `{{ $json.params.statement }}`, and an HTTP branch reads
36
+ `{{ $json.params.path }}` and issues the call with an n8n credential.
36
37
 
37
38
  See [examples/](examples) for three working workflows: over an n8n Data Table,
38
39
  over Postgres, and over an HTTP API.
@@ -79,30 +80,16 @@ That last step matters: while you are only running *Test workflow* in the editor
79
80
  the node listens for a short window and then stops. It polls continuously only
80
81
  once the workflow is active.
81
82
 
82
- ### Secrets for your own systems
83
+ ### Credentials for your own systems
83
84
 
84
- In loopthink you configure the *shape* of a request, which header and which URL,
85
- and write `{{secret.NAME}}` where a value belongs:
85
+ They stay in n8n, on whichever node makes the call, and loopthink never holds
86
+ them. A tool says what to reach, never what to reach it with: the path or the
87
+ statement travels as a fixed parameter, and the API key or database password is
88
+ picked from n8n's own credential store by the node that needs it.
86
89
 
87
- ```
88
- Header X-API-Key: {{secret.CRM_API_KEY}}
89
- ```
90
-
91
- Then add a **loopthink Target Secrets** credential in n8n (the node's **Secrets**
92
- slot) with a `CRM_API_KEY` entry. The runner fills the placeholder in on the way out. loopthink sends the
93
- request to make, never the key to make it with. The secret is never stored there
94
- and never travels.
95
-
96
- Two behaviours worth knowing:
97
-
98
- - **A missing secret refuses the call.** The literal placeholder is never sent;
99
- it would earn a 401 and leave `{{secret.CRM_API_KEY}}` in the target's access
100
- log. The error names what is missing.
101
- - **Execution records show the unresolved form.** A substituted URL can contain a
102
- secret, and n8n stores execution data.
103
-
104
- A placeholder in a **URL** ends up in the target's access log and in any proxy in
105
- between. Sometimes an API leaves no choice, but prefer a header where you have one.
90
+ The node used to substitute `{{secret.NAME}}` into a resolved request, and that
91
+ went with the HTTP kind. n8n's credentials do the same job better, because they
92
+ were never anywhere near us to begin with.
106
93
 
107
94
  ## How it behaves
108
95
 
@@ -138,16 +125,12 @@ past the last id it was given by passing `id_after`. Both are ordinary filters i
138
125
  already understands, so nothing opaque travels back and forth and the model can
139
126
  also bisect a large range instead of only walking forward.
140
127
 
141
- **Execute Request** issues an HTTP tool's call and fills in `{{secret.NAME}}` from
142
- the **Secrets** credential on the way out. That substitution is the reason this
143
- is not a plain HTTP Request node: handed the placeholder, a standard node sends
144
- it verbatim and it lands in the target's access log.
145
-
146
- **`$json.q`** is what fills a Data Table node's condition rows, and it arrives
147
- ready to use. That node cannot take its conditions from an expression: they are
148
- rows in the editor, fixed when the workflow is built, and the list as a whole
149
- cannot be computed, because n8n walks a string handed to a multi-value collection
150
- character by character and quietly produces one empty condition per character.
128
+ **`$json.params`** is what fills a Data Table node's condition rows, and it
129
+ arrives ready to use. That node cannot take its conditions from an expression:
130
+ they are rows in the editor, fixed when the workflow is built, and the list as a
131
+ whole cannot be computed, because n8n walks a string handed to a multi-value
132
+ collection character by character and quietly produces one empty condition per
133
+ character.
151
134
 
152
135
  So every row has to hold a value on every call, including the ones the model left
153
136
  empty. The platform sends them, keyed by the tool's parameter name: a bound so
@@ -156,14 +139,20 @@ match. One key, one plain value:
156
139
 
157
140
  | Tool parameter | Column | Comparison | Value |
158
141
  |---|---|---|---|
159
- | `created_after` | `createdAt` | Or Later | `{{ $json.q.created_after }}` |
160
- | `created_before` | `createdAt` | Or Earlier | `{{ $json.q.created_before }}` |
161
- | `status` | `status` | Contains | `{{ $json.q.status }}` |
162
- | `id_after` | `id` | Greater Than | `{{ $json.q.id_after }}` |
142
+ | `created_after` | `createdAt` | Or Later | `{{ $json.params.created_after }}` |
143
+ | `created_before` | `createdAt` | Or Earlier | `{{ $json.params.created_before }}` |
144
+ | `status` | `status` | Contains | `{{ $json.params.status }}` |
145
+ | `id_after` | `id` | Greater Than | `{{ $json.params.id_after }}` |
163
146
 
164
147
  The comparison is chosen once from the dropdown and never changes, so it is not
165
- in the data. Set **Limit** to `{{ $json.q.fetch }}`, which is one more than the
166
- limit, and **Order** to `{{ $json.q.order }}`.
148
+ in the data.
149
+
150
+ Every key under `params` is a parameter the tool declared, under the name its
151
+ author typed. Nothing is added on the way. A tool that wants a cap declares
152
+ `limit` like any other parameter, and then the reading node's **Limit** is
153
+ `{{ $json.params.limit + 1 }}` — one more than the answer carries, which is where
154
+ `truncated` comes from. A tool that declares `sort` gets `ASC` or `DESC` under
155
+ that name, ready for **Order**.
167
156
 
168
157
  `id_after` is what lets a model read on, and it is an ordinary parameter like any
169
158
  other. Compare it with **Greater Than** rather than Or Later, so that it means
@@ -171,10 +160,19 @@ what it says and the boundary row is not returned twice.
171
160
 
172
161
  ![The Data Table node reading from it](https://raw.githubusercontent.com/loopthink/n8n-nodes-loopthink/main/docs/read-bookings.png)
173
162
 
174
- Paging is by **cursor**, not offset, so the source does the work: `id < c` with a
175
- Limit is one comparison it expresses natively and it hands back only the page.
176
163
  See [examples/](examples) for three sources worked through.
177
164
 
165
+ ### When an answer is too large
166
+
167
+ One answer carries 300 KB. Past that **Send Result** does not send the rows at
168
+ all: it answers with a sentence naming the size and the row count and saying to
169
+ narrow the filters or pass a smaller limit, which a model acts on by splitting
170
+ its question and asking again.
171
+
172
+ Measured before the POST on purpose. Sent anyway, an oversized body is rejected
173
+ by whichever layer notices first, the call stays unanswered, and the caller waits
174
+ out its timeout to learn nothing at all.
175
+
178
176
  ## Masking
179
177
 
180
178
  Masking runs **here**, before anything travels back. Only masked data ever passes
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Loopthink = void 0;
4
- const execute_operation_1 = require("./execute.operation");
5
4
  const result_operation_1 = require("./result.operation");
6
5
  class Loopthink {
7
6
  constructor() {
@@ -23,12 +22,6 @@ class Loopthink {
23
22
  displayName: 'Authentication',
24
23
  displayOptions: { show: { operation: ['result'] } },
25
24
  },
26
- {
27
- name: 'loopthinkTargetApi',
28
- required: false,
29
- displayName: 'Secrets',
30
- displayOptions: { show: { operation: ['execute'] } },
31
- },
32
25
  ],
33
26
  properties: [
34
27
  {
@@ -44,29 +37,16 @@ class Loopthink {
44
37
  description: 'Mask the answer and send it back to loopthink',
45
38
  action: 'Send the result back to loopthink',
46
39
  },
47
- {
48
- name: 'Execute Request',
49
- value: 'execute',
50
- description: 'Issue the HTTP call loopthink resolved, filling in your secrets',
51
- action: 'Execute the resolved HTTP request',
52
- },
53
40
  ],
54
41
  },
55
42
  ...result_operation_1.resultFields.map((field) => ({
56
43
  ...field,
57
44
  displayOptions: { ...field.displayOptions, show: { ...field.displayOptions?.show, operation: ['result'] } },
58
45
  })),
59
- ...execute_operation_1.executeFields.map((field) => ({
60
- ...field,
61
- displayOptions: { ...field.displayOptions, show: { ...field.displayOptions?.show, operation: ['execute'] } },
62
- })),
63
46
  ],
64
47
  };
65
48
  }
66
49
  async execute() {
67
- const operation = this.getNodeParameter('operation', 0);
68
- if (operation === 'execute')
69
- return execute_operation_1.executeRequest.call(this);
70
50
  return result_operation_1.executeResult.call(this);
71
51
  }
72
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Loopthink.node.js","sourceRoot":"","sources":["../../../nodes/Loopthink/Loopthink.node.ts"],"names":[],"mappings":";;;AAOA,2DAAoE;AACpE,yDAAiE;AAmBjE,MAAa,SAAS;IAAtB;QACC,gBAAW,GAAyB;YACnC,WAAW,EAAE,WAAW;YACxB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,CAAC,WAAW,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE,8BAA8B;YACxC,WAAW,EAAE,wCAAwC;YACrD,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;YAC/B,MAAM,EAAE,CAAC,MAAM,CAAC;YAChB,OAAO,EAAE,CAAC,MAAM,CAAC;YACjB,WAAW,EAAE;gBACZ;oBACC,IAAI,EAAE,oBAAoB;oBAC1B,QAAQ,EAAE,IAAI;oBACd,WAAW,EAAE,gBAAgB;oBAC7B,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE;iBACnD;gBACD;oBAIC,IAAI,EAAE,oBAAoB;oBAC1B,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,SAAS;oBACtB,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE;iBACpD;aACD;YACD,UAAU,EAAE;gBACX;oBACC,WAAW,EAAE,WAAW;oBACxB,IAAI,EAAE,WAAW;oBACjB,IAAI,EAAE,SAAS;oBACf,gBAAgB,EAAE,IAAI;oBACtB,OAAO,EAAE,QAAQ;oBACjB,OAAO,EAAE;wBACR;4BACC,IAAI,EAAE,aAAa;4BACnB,KAAK,EAAE,QAAQ;4BACf,WAAW,EAAE,+CAA+C;4BAC5D,MAAM,EAAE,mCAAmC;yBAC3C;wBACD;4BACC,IAAI,EAAE,iBAAiB;4BACvB,KAAK,EAAE,SAAS;4BAChB,WAAW,EAAE,iEAAiE;4BAC9E,MAAM,EAAE,mCAAmC;yBAC3C;qBACD;iBACD;gBACD,GAAG,+BAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBAC/B,GAAG,KAAK;oBACR,cAAc,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE;iBAC3G,CAAC,CAAC;gBACH,GAAG,iCAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBAChC,GAAG,KAAK;oBACR,cAAc,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE;iBAC5G,CAAC,CAAC;aACH;SACD,CAAC;IAOH,CAAC;IALA,KAAK,CAAC,OAAO;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,CAAyB,CAAC;QAChF,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,kCAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO,gCAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;CACD;AAnED,8BAmEC"}
1
+ {"version":3,"file":"Loopthink.node.js","sourceRoot":"","sources":["../../../nodes/Loopthink/Loopthink.node.ts"],"names":[],"mappings":";;;AAOA,yDAAiE;AAmBjE,MAAa,SAAS;IAAtB;QACC,gBAAW,GAAyB;YACnC,WAAW,EAAE,WAAW;YACxB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,CAAC,WAAW,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE,8BAA8B;YACxC,WAAW,EAAE,wCAAwC;YACrD,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;YAC/B,MAAM,EAAE,CAAC,MAAM,CAAC;YAChB,OAAO,EAAE,CAAC,MAAM,CAAC;YACjB,WAAW,EAAE;gBACZ;oBACC,IAAI,EAAE,oBAAoB;oBAC1B,QAAQ,EAAE,IAAI;oBACd,WAAW,EAAE,gBAAgB;oBAC7B,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE;iBACnD;aACD;YACD,UAAU,EAAE;gBACX;oBACC,WAAW,EAAE,WAAW;oBACxB,IAAI,EAAE,WAAW;oBACjB,IAAI,EAAE,SAAS;oBACf,gBAAgB,EAAE,IAAI;oBACtB,OAAO,EAAE,QAAQ;oBACjB,OAAO,EAAE;wBACR;4BACC,IAAI,EAAE,aAAa;4BACnB,KAAK,EAAE,QAAQ;4BACf,WAAW,EAAE,+CAA+C;4BAC5D,MAAM,EAAE,mCAAmC;yBAC3C;qBACD;iBACD;gBACD,GAAG,+BAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBAC/B,GAAG,KAAK;oBACR,cAAc,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE;iBAC3G,CAAC,CAAC;aACH;SACD,CAAC;IAKH,CAAC;IAHA,KAAK,CAAC,OAAO;QACZ,OAAO,gCAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;CACD;AA9CD,8BA8CC"}
@@ -7,13 +7,29 @@ const masking_1 = require("../LoopthinkRunner/masking");
7
7
  function pagePayload(ctx, respondWith, items) {
8
8
  if (respondWith === 'object')
9
9
  return (items[0]?.json ?? {});
10
- const rows = items.map((item) => item.json);
10
+ const rows = items
11
+ .map((item) => item.json)
12
+ .filter((row) => row && Object.keys(row).length > 0);
11
13
  if (respondWith === 'list')
12
14
  return rows;
13
15
  const limit = Number(ctx.getNodeParameter('pageSize', 0));
14
16
  const capped = Number.isFinite(limit) && limit > 0;
15
17
  return { items: capped ? rows.slice(0, limit) : rows, truncated: capped && rows.length > limit };
16
18
  }
19
+ const MAX_RESULT_BYTES = 300 * 1024;
20
+ function tooLargeMessage(bytes, rows) {
21
+ const kb = (n) => `${Math.round(n / 1024)} KB`;
22
+ const counted = rows === undefined ? '' : `${rows.toLocaleString('en-US')} rows, `;
23
+ return (`The result was too large to return: ${counted}${kb(bytes)}, and one answer can carry ${kb(MAX_RESULT_BYTES)}. ` +
24
+ 'Nothing was returned. Ask for a smaller slice and combine the answers: narrow the filters, for instance a ' +
25
+ 'shorter date range or a more specific match, or pass a smaller limit if this tool takes one.');
26
+ }
27
+ function countRows(payload) {
28
+ if (Array.isArray(payload))
29
+ return payload.length;
30
+ const items = payload?.items;
31
+ return Array.isArray(items) ? items.length : undefined;
32
+ }
17
33
  function parseRequest(value) {
18
34
  if (typeof value === 'string') {
19
35
  try {
@@ -65,9 +81,9 @@ exports.resultFields = [
65
81
  displayName: 'Limit',
66
82
  name: 'pageSize',
67
83
  type: 'number',
68
- default: "={{ $('loopthink Runner').first().json.q.limit }}",
84
+ default: "={{ $('loopthink Runner').first().json.params.limit }}",
69
85
  displayOptions: { show: { respondWith: ['page'] } },
70
- description: 'How many rows to answer with. Read one more than this in the branch: if the extra row arrives, the answer is marked truncated.',
86
+ description: 'How many rows to answer with. Set the reading node\'s own limit to {{ $json.params.limit + 1 }}: if that extra row arrives, the answer is marked truncated.',
71
87
  },
72
88
  {
73
89
  displayName: 'Status',
@@ -108,10 +124,12 @@ async function executeResult() {
108
124
  }
109
125
  else {
110
126
  const payload = pagePayload(this, respondWith, items);
111
- body = {
112
- status: this.getNodeParameter('status', 0),
113
- data: (0, masking_1.applyMasking)(payload, (0, masking_1.rulesForScope)(request.masking ?? [], request.scope ?? undefined)),
114
- };
127
+ const data = (0, masking_1.applyMasking)(payload, (0, masking_1.rulesForScope)(request.masking ?? [], request.scope ?? undefined));
128
+ const size = Buffer.byteLength(JSON.stringify(data ?? null), 'utf8');
129
+ body =
130
+ size > MAX_RESULT_BYTES
131
+ ? { error: tooLargeMessage(size, countRows(data)) }
132
+ : { status: this.getNodeParameter('status', 0), data };
115
133
  }
116
134
  const response = await this.helpers.httpRequest({
117
135
  method: 'POST',
@@ -1 +1 @@
1
- {"version":3,"file":"result.operation.js","sourceRoot":"","sources":["../../../nodes/Loopthink/result.operation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"result.operation.js","sourceRoot":"","sources":["../../../nodes/Loopthink/result.operation.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,6 @@ export interface HttpPayload {
11
11
  export interface ParamsPayload {
12
12
  kind: 'params';
13
13
  params: IDataObject;
14
- q?: IDataObject;
15
14
  }
16
15
  export interface StatementPayload {
17
16
  kind: 'statement';
@@ -19,12 +19,9 @@ function emitJob(ctx, job) {
19
19
  {
20
20
  requestId: job.requestId,
21
21
  tool: job.tool,
22
- request: job.request,
23
22
  params: isParamsRequest(job.request) || isStatementRequest(job.request)
24
23
  ? job.request.params
25
24
  : {},
26
- statement: isStatementRequest(job.request) ? job.request.statement : null,
27
- q: isParamsRequest(job.request) ? (job.request.q ?? {}) : {},
28
25
  masking: job.masking ?? [],
29
26
  scope: job.scope ?? null,
30
27
  leaseUntil: job.leaseUntil,
@@ -1 +1 @@
1
- {"version":3,"file":"workflowTools.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/workflowTools.ts"],"names":[],"mappings":";;;AAiFA,0CAEC;AAED,gDAIC;AAyBD,0BAqBC;AAtDD,SAAgB,eAAe,CAAC,OAAiC;IAChE,OAAQ,OAAyB,EAAE,IAAI,KAAK,QAAQ,CAAC;AACtD,CAAC;AAED,SAAgB,kBAAkB,CACjC,OAAiC;IAEjC,OAAQ,OAA4B,EAAE,IAAI,KAAK,WAAW,CAAC;AAC5D,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;gBAI9C,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;oBACtE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM;oBACpB,CAAC,CAAC,EAAE;gBACL,SAAS,EAAE,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;gBACzE,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC5D,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"}
1
+ {"version":3,"file":"workflowTools.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/workflowTools.ts"],"names":[],"mappings":";;;AAiFA,0CAEC;AAED,gDAIC;AAyBD,0BAkBC;AAnDD,SAAgB,eAAe,CAAC,OAAiC;IAChE,OAAQ,OAAyB,EAAE,IAAI,KAAK,QAAQ,CAAC;AACtD,CAAC;AAED,SAAgB,kBAAkB,CACjC,OAAiC;IAEjC,OAAQ,OAA4B,EAAE,IAAI,KAAK,WAAW,CAAC;AAC5D,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;gBAId,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;oBACtE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM;oBACpB,CAAC,CAAC,EAAE;gBACL,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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopthink/n8n-nodes-loopthink",
3
- "version": "0.12.0",
3
+ "version": "0.14.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 \u2014 no inbound connection required.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -42,8 +42,7 @@
42
42
  "n8n": {
43
43
  "n8nNodesApiVersion": 1,
44
44
  "credentials": [
45
- "dist/credentials/LoopthinkRunnerApi.credentials.js",
46
- "dist/credentials/LoopthinkTargetApi.credentials.js"
45
+ "dist/credentials/LoopthinkRunnerApi.credentials.js"
47
46
  ],
48
47
  "nodes": [
49
48
  "dist/nodes/LoopthinkRunner/LoopthinkRunner.node.js",
@@ -1,7 +0,0 @@
1
- import type { ICredentialType, INodeProperties } from 'n8n-workflow';
2
- export declare class LoopthinkTargetApi implements ICredentialType {
3
- name: string;
4
- displayName: string;
5
- documentationUrl: string;
6
- properties: INodeProperties[];
7
- }
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LoopthinkTargetApi = void 0;
4
- class LoopthinkTargetApi {
5
- constructor() {
6
- this.name = 'loopthinkTargetApi';
7
- this.displayName = 'Loopthink Target Secrets API';
8
- this.documentationUrl = 'https://www.loopthink.ai/docs/n8n';
9
- this.properties = [
10
- {
11
- displayName: 'Base URL',
12
- name: 'baseUrl',
13
- type: 'string',
14
- default: '',
15
- placeholder: 'http://pms.internal:8080',
16
- description: 'Where the tool paths point. An n8n server does not tell loopthink this, because the host is reachable from here and from nowhere else, so it arrives as a path and is joined with this. Needed only if the server has HTTP tools.',
17
- },
18
- {
19
- displayName: 'Secrets',
20
- name: 'secrets',
21
- placeholder: 'Add secret',
22
- type: 'fixedCollection',
23
- typeOptions: { multipleValues: true },
24
- default: {},
25
- description: 'Each entry matches a {{secret.NAME}} placeholder configured in loopthink. A call whose placeholder is missing here is refused rather than sent, because an unresolved placeholder would reach the target system and end up in its logs.',
26
- options: [
27
- {
28
- name: 'secret',
29
- displayName: 'Secret',
30
- values: [
31
- {
32
- displayName: 'Name',
33
- name: 'name',
34
- type: 'string',
35
- default: '',
36
- placeholder: 'CRM_API_KEY',
37
- description: 'The NAME in {{secret.NAME}}, exactly as written in loopthink',
38
- },
39
- {
40
- displayName: 'Value',
41
- name: 'value',
42
- type: 'string',
43
- typeOptions: { password: true },
44
- default: '',
45
- },
46
- ],
47
- },
48
- ],
49
- },
50
- ];
51
- }
52
- }
53
- exports.LoopthinkTargetApi = LoopthinkTargetApi;
54
- //# sourceMappingURL=LoopthinkTargetApi.credentials.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LoopthinkTargetApi.credentials.js","sourceRoot":"","sources":["../../credentials/LoopthinkTargetApi.credentials.ts"],"names":[],"mappings":";;;AAiBA,MAAa,kBAAkB;IAA/B;QACC,SAAI,GAAG,oBAAoB,CAAC;QAE5B,gBAAW,GAAG,8BAA8B,CAAC;QAM7C,qBAAgB,GAAG,mCAAmC,CAAC;QAEvD,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,0BAA0B;gBACvC,WAAW,EACV,mOAAmO;aACpO;YACD;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,SAAS;gBACf,WAAW,EAAE,YAAY;gBACzB,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;gBACrC,OAAO,EAAE,EAAE;gBACX,WAAW,EACV,yOAAyO;gBAC1O,OAAO,EAAE;oBACR;wBACC,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,QAAQ;wBACrB,MAAM,EAAE;4BACP;gCACC,WAAW,EAAE,MAAM;gCACnB,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,QAAQ;gCACd,OAAO,EAAE,EAAE;gCACX,WAAW,EAAE,aAAa;gCAC1B,WAAW,EAAE,8DAA8D;6BAC3E;4BACD;gCACC,WAAW,EAAE,OAAO;gCACpB,IAAI,EAAE,OAAO;gCACb,IAAI,EAAE,QAAQ;gCACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gCAC/B,OAAO,EAAE,EAAE;6BACX;yBACD;qBACD;iBACD;aACD;SACD,CAAC;IACH,CAAC;CAAA;AAvDD,gDAuDC"}
@@ -1,3 +0,0 @@
1
- import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
2
- export declare const executeFields: INodeProperties[];
3
- export declare function executeRequest(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
@@ -1,100 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.executeFields = void 0;
4
- exports.executeRequest = executeRequest;
5
- const n8n_workflow_1 = require("n8n-workflow");
6
- const secrets_1 = require("../LoopthinkRunner/secrets");
7
- exports.executeFields = [
8
- {
9
- displayName: 'Request',
10
- name: 'httpRequest',
11
- type: 'json',
12
- default: "={{ $json.request }}",
13
- description: 'The resolved call from the Runner. Leave this as it is unless the branch reshapes the item first.',
14
- },
15
- {
16
- displayName: 'Timeout (Seconds)',
17
- name: 'requestTimeout',
18
- type: 'number',
19
- typeOptions: { minValue: 1, maxValue: 120 },
20
- default: 30,
21
- description: 'How long to wait for the internal API before giving up',
22
- },
23
- {
24
- displayName: 'Keys for your own systems stay in this n8n. Write <code>{{secret.NAME}}</code> in loopthink where a value belongs and add a matching entry under <b>Secrets</b>. A call whose placeholder has no entry is refused rather than sent, because the literal placeholder would reach the target and sit in its log.',
25
- name: 'executeNotice',
26
- type: 'notice',
27
- default: '',
28
- },
29
- ];
30
- function parseRequest(value) {
31
- if (typeof value === 'string') {
32
- try {
33
- return JSON.parse(value);
34
- }
35
- catch {
36
- return {};
37
- }
38
- }
39
- return (value ?? {});
40
- }
41
- async function executeRequest() {
42
- const timeout = this.getNodeParameter('requestTimeout', 0, 30) * 1000;
43
- let secrets = {};
44
- let baseUrl = '';
45
- try {
46
- const credential = await this.getCredentials('loopthinkTargetApi');
47
- secrets = (0, secrets_1.secretsFromCredential)(credential);
48
- baseUrl = String(credential.baseUrl || '').replace(/\/+$/, '');
49
- }
50
- catch {
51
- }
52
- const out = [];
53
- for (let i = 0; i < this.getInputData().length; i += 1) {
54
- const request = parseRequest(this.getNodeParameter('httpRequest', i));
55
- if (!request.url && request.path) {
56
- if (!baseUrl) {
57
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'This call has a path but no base URL', {
58
- description: 'loopthink sent the path only, so the base URL comes from here. Add it to the Secrets credential on this node.',
59
- });
60
- }
61
- request.url = `${baseUrl}/${request.path.replace(/^\/+/, '')}`;
62
- }
63
- if (!request.url) {
64
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No url or path in the Request parameter', {
65
- description: 'This operation runs the HTTP call loopthink resolved. A Data Table or custom tool has neither, so route those to their own branch instead.',
66
- });
67
- }
68
- try {
69
- const resolved = (0, secrets_1.substituteSecrets)(request, secrets);
70
- const url = new URL(resolved.url);
71
- Object.entries(resolved.query || {}).forEach(([key, value]) => url.searchParams.set(key, String(value)));
72
- const response = await this.helpers.httpRequest({
73
- method: (resolved.method || 'GET'),
74
- url: url.toString(),
75
- headers: resolved.headers || {},
76
- body: resolved.body ?? undefined,
77
- json: true,
78
- timeout,
79
- returnFullResponse: true,
80
- ignoreHttpStatusErrors: true,
81
- });
82
- out.push({
83
- json: {
84
- status: response.statusCode,
85
- data: response.body,
86
- url: request.url,
87
- method: request.method ?? 'GET',
88
- },
89
- });
90
- }
91
- catch (error) {
92
- if (error instanceof secrets_1.MissingSecretError) {
93
- this.logger.warn(`loopthink: ${error.message}`);
94
- }
95
- out.push({ json: { error: error.message, url: request.url } });
96
- }
97
- }
98
- return [out];
99
- }
100
- //# sourceMappingURL=execute.operation.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"execute.operation.js","sourceRoot":"","sources":["../../../nodes/Loopthink/execute.operation.ts"],"names":[],"mappings":"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"}
@@ -1,7 +0,0 @@
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>;
@@ -1,63 +0,0 @@
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
@@ -1 +0,0 @@
1
- {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../nodes/LoopthinkRunner/secrets.ts"],"names":[],"mappings":";;;AAyBA,kDAaC;AAaD,8CAcC;AAGD,sDAYC;AApED,MAAM,WAAW,GAAG,iDAAiD,CAAC;AAEtE,MAAa,kBAAmB,SAAQ,KAAK;IAC5C,YAA4B,KAAe;QAC1C,KAAK,CACJ,iBAAiB,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,8BAA8B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC7F,4CAA4C,CAC7C,CAAC;QAJyB,UAAK,GAAL,KAAK,CAAU;QAK1C,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IAClC,CAAC;CACD;AARD,gDAQC;AAGD,SAAgB,mBAAmB,CAAC,KAAc,EAAE,QAAqB,IAAI,GAAG,EAAE;IACjF,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QACpD,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,KAAgC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa,EAAE,OAA+B;IACvE,OAAO,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3E,CAAC;AASD,SAAgB,iBAAiB,CAAI,KAAQ,EAAE,OAA+B;IAC7E,MAAM,OAAO,GAAG,CAAC,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjF,IAAI,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE1D,MAAM,IAAI,GAAG,CAAC,CAAU,EAAW,EAAE;QACpC,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,gBAAgB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAA4B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvG,CAAC;QACD,OAAO,CAAC,CAAC;IACV,CAAC,CAAC;IAEF,OAAO,IAAI,CAAC,KAAK,CAAM,CAAC;AACzB,CAAC;AAGD,SAAgB,qBAAqB,CAAC,UAAmB;IACxD,MAAM,OAAO,GAAI,UAAkB,EAAE,OAAO,EAAE,MAAM,CAAC;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAG9C,IAAI,IAAI,IAAI,KAAK,EAAE,KAAK;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}