n8n-nodes-mindrouter 0.3.0 → 0.5.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
@@ -28,6 +28,14 @@ npm install n8n-nodes-mindrouter
28
28
 
29
29
  A sub-node for the **AI Agent** and **LLM Chain** nodes: click the **Chat Model** socket and pick *MindRouter Chat Model*. Choose any model from your account (loaded live) and set temperature, max tokens, top-p, penalties, and timeout. Everything the agent does then runs through the MindRouter gateway — guardrails, spend limits, and usage tracking included.
30
30
 
31
+ ### MindRouter Embeddings
32
+
33
+ A sub-node for **Vector Store** and retriever nodes: click the **Embeddings** socket and pick *MindRouter Embeddings*. Your whole RAG pipeline — indexing and retrieval included — then runs through the MindRouter gateway on the same key and bill as the chat model. Supports batch size, dimension truncation, and newline stripping.
34
+
35
+ ### MindRouter PII Guard
36
+
37
+ Detect, mask, tokenize, or restore PII and credentials in any text — Indonesian NIK/NPWP/phone numbers, credit cards, emails, API keys and JWTs, plus (optionally, via NER) names and locations in Indonesian and English. **Tokenize** replaces values with placeholders and hands you the reversal map, so you can send sanitized text to any external service and restore the original values afterwards. Usable as an AI Agent tool.
38
+
31
39
  ### MindRouter
32
40
 
33
41
  A regular node for direct calls.
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MindRouterEmbeddings = void 0;
4
+ const openai_1 = require("@langchain/openai");
5
+ const n8n_workflow_1 = require("n8n-workflow");
6
+ const api_1 = require("../shared/api");
7
+ class MindRouterEmbeddings {
8
+ constructor() {
9
+ this.description = {
10
+ displayName: 'MindRouter Embeddings',
11
+ name: 'embeddingsMindRouter',
12
+ icon: 'file:mindrouter.svg',
13
+ group: ['transform'],
14
+ version: 1,
15
+ description: 'Use MindRouter embedding models with Vector Stores',
16
+ defaults: {
17
+ name: 'MindRouter Embeddings',
18
+ },
19
+ codex: {
20
+ categories: ['AI'],
21
+ subcategories: {
22
+ AI: ['Embeddings'],
23
+ },
24
+ resources: {
25
+ primaryDocumentation: [
26
+ {
27
+ url: 'https://mindrouter.io/docs',
28
+ },
29
+ ],
30
+ },
31
+ },
32
+ inputs: [],
33
+ outputs: [n8n_workflow_1.NodeConnectionTypes.AiEmbedding],
34
+ outputNames: ['Embeddings'],
35
+ credentials: [
36
+ {
37
+ name: 'mindRouterApi',
38
+ required: true,
39
+ },
40
+ ],
41
+ properties: [
42
+ {
43
+ displayName: 'Model Name or ID',
44
+ name: 'model',
45
+ type: 'options',
46
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
47
+ typeOptions: {
48
+ loadOptionsMethod: 'getEmbeddingModels',
49
+ },
50
+ required: true,
51
+ default: '',
52
+ },
53
+ {
54
+ displayName: 'Options',
55
+ name: 'options',
56
+ type: 'collection',
57
+ placeholder: 'Add option',
58
+ default: {},
59
+ options: [
60
+ {
61
+ displayName: 'Batch Size',
62
+ name: 'batchSize',
63
+ type: 'number',
64
+ typeOptions: { minValue: 1, maxValue: 2048 },
65
+ default: 512,
66
+ description: 'How many texts to embed per API request',
67
+ },
68
+ {
69
+ displayName: 'Dimensions',
70
+ name: 'dimensions',
71
+ type: 'number',
72
+ typeOptions: { minValue: 1 },
73
+ default: 0,
74
+ description: 'Shorten vectors to this many dimensions (0 = model default). Only models that support truncation honour it.',
75
+ },
76
+ {
77
+ displayName: 'Strip New Lines',
78
+ name: 'stripNewLines',
79
+ type: 'boolean',
80
+ default: true,
81
+ description: 'Whether to replace newlines with spaces before embedding',
82
+ },
83
+ ],
84
+ },
85
+ ],
86
+ };
87
+ this.methods = {
88
+ loadOptions: {
89
+ async getEmbeddingModels() {
90
+ var _a;
91
+ const credentials = await this.getCredentials('mindRouterApi');
92
+ const baseUrl = (0, api_1.normalizeBaseUrl)(credentials.baseUrl);
93
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'mindRouterApi', {
94
+ method: 'GET',
95
+ url: `${baseUrl}/models`,
96
+ json: true,
97
+ }));
98
+ const models = (_a = response.data) !== null && _a !== void 0 ? _a : [];
99
+ const embedding = models.filter((m) => m.category === 'embedding');
100
+ return (embedding.length > 0 ? embedding : models)
101
+ .map((m) => ({ name: m.display_name || m.id, value: m.id }))
102
+ .sort((a, b) => a.name.localeCompare(b.name));
103
+ },
104
+ },
105
+ };
106
+ }
107
+ async supplyData(itemIndex) {
108
+ const credentials = await this.getCredentials('mindRouterApi');
109
+ const modelName = this.getNodeParameter('model', itemIndex);
110
+ const options = this.getNodeParameter('options', itemIndex, {});
111
+ const embeddings = new openai_1.OpenAIEmbeddings({
112
+ apiKey: String(credentials.apiKey),
113
+ model: modelName,
114
+ batchSize: options.batchSize,
115
+ dimensions: options.dimensions ? options.dimensions : undefined,
116
+ stripNewLines: options.stripNewLines,
117
+ configuration: {
118
+ baseURL: (0, api_1.normalizeBaseUrl)(credentials.baseUrl),
119
+ },
120
+ });
121
+ return {
122
+ response: embeddings,
123
+ };
124
+ }
125
+ }
126
+ exports.MindRouterEmbeddings = MindRouterEmbeddings;
@@ -0,0 +1,28 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" role="img" aria-label="MindRouter">
2
+ <rect width="60" height="60" rx="12" fill="#101826"/>
3
+ <g transform="translate(8 12)">
4
+ <defs>
5
+ <mask id="mh">
6
+ <rect width="44" height="36" fill="#fff"/>
7
+ <circle cx="22" cy="18.5" r="5.4" fill="#000"/>
8
+ </mask>
9
+ </defs>
10
+ <g mask="url(#mh)">
11
+ <path d="M4 32 L12 7 L22 19 Z" fill="#FFFFFF"/>
12
+ <path d="M22 19 L31 10 L38 32 Z" fill="#FFFFFF"/>
13
+ </g>
14
+ <circle cx="22" cy="18.5" r="5.4" fill="none" stroke="#FFFFFF" stroke-width="2"/>
15
+ <g transform="translate(18 14.5) scale(0.5)">
16
+ <g fill="none" stroke="#14B8B0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
17
+ <path d="M3.8 8 H8"/>
18
+ <path d="M8 8 V5 H12"/>
19
+ <path d="M8 8 V11 H12"/>
20
+ </g>
21
+ <g fill="#14B8B0">
22
+ <circle cx="3.8" cy="8" r="1.5"/>
23
+ <circle cx="12.2" cy="5" r="1.3"/>
24
+ <circle cx="12.2" cy="11" r="1.3"/>
25
+ </g>
26
+ </g>
27
+ </g>
28
+ </svg>
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MindRouterPiiGuard = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const api_1 = require("../shared/api");
6
+ class MindRouterPiiGuard {
7
+ constructor() {
8
+ this.description = {
9
+ displayName: 'MindRouter PII Guard',
10
+ name: 'mindRouterPiiGuard',
11
+ icon: 'file:mindrouter.svg',
12
+ group: ['transform'],
13
+ version: 1,
14
+ subtitle: '={{$parameter["operation"]}}',
15
+ description: 'Detect, mask, or tokenize PII and credentials in text',
16
+ defaults: {
17
+ name: 'MindRouter PII Guard',
18
+ },
19
+ usableAsTool: true,
20
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
21
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
22
+ credentials: [
23
+ {
24
+ name: 'mindRouterApi',
25
+ required: true,
26
+ },
27
+ ],
28
+ properties: [
29
+ {
30
+ displayName: 'Operation',
31
+ name: 'operation',
32
+ type: 'options',
33
+ noDataExpression: true,
34
+ options: [
35
+ {
36
+ name: 'Mask',
37
+ value: 'mask',
38
+ action: 'Mask PII irreversibly',
39
+ description: 'Replace all detected PII and credentials with redaction markers. Use when the text leaves for good (logs, tickets, datasets).',
40
+ },
41
+ {
42
+ name: 'Scan',
43
+ value: 'scan',
44
+ action: 'Scan for PII',
45
+ description: 'Detect PII without changing the text; returns type counts and findings',
46
+ },
47
+ {
48
+ name: 'Tokenize',
49
+ value: 'tokenize',
50
+ action: 'Tokenize PII reversibly',
51
+ description: 'Replace PII with placeholders and return the placeholder→value map, so the values can be restored later',
52
+ },
53
+ {
54
+ name: 'Restore',
55
+ value: 'restore',
56
+ action: 'Restore tokenized PII',
57
+ description: 'Put original values back using the map returned by Tokenize',
58
+ },
59
+ ],
60
+ default: 'mask',
61
+ },
62
+ {
63
+ displayName: 'Text',
64
+ name: 'text',
65
+ type: 'string',
66
+ typeOptions: { rows: 4 },
67
+ required: true,
68
+ default: '',
69
+ description: 'The text to process',
70
+ },
71
+ {
72
+ displayName: 'Tokens Map',
73
+ name: 'tokens',
74
+ type: 'json',
75
+ displayOptions: {
76
+ show: { operation: ['restore'] },
77
+ },
78
+ required: true,
79
+ default: '',
80
+ description: 'The placeholder→value map returned by the Tokenize operation',
81
+ },
82
+ {
83
+ displayName: 'Use NER',
84
+ name: 'useNer',
85
+ type: 'boolean',
86
+ displayOptions: {
87
+ show: { operation: ['mask', 'scan', 'tokenize'] },
88
+ },
89
+ default: false,
90
+ description: 'Whether to also detect names and locations (Indonesian and English) with the NER engine. Slower; only available when the deployment has NER enabled.',
91
+ },
92
+ ],
93
+ };
94
+ }
95
+ async execute() {
96
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
97
+ const items = this.getInputData();
98
+ const returnData = [];
99
+ const operation = this.getNodeParameter('operation', 0);
100
+ const credentials = await this.getCredentials('mindRouterApi');
101
+ const mcpUrl = `${(0, api_1.normalizeBaseUrl)(credentials.baseUrl)}/mcp`;
102
+ const toolNames = {
103
+ mask: 'pii_mask',
104
+ scan: 'pii_scan',
105
+ tokenize: 'pii_tokenize',
106
+ restore: 'pii_restore',
107
+ };
108
+ for (let i = 0; i < items.length; i++) {
109
+ try {
110
+ const text = this.getNodeParameter('text', i);
111
+ const args = { text };
112
+ if (operation === 'restore') {
113
+ const raw = this.getNodeParameter('tokens', i);
114
+ const tokens = typeof raw === 'string' ? JSON.parse(raw) : raw;
115
+ if (tokens === null || typeof tokens !== 'object' || Array.isArray(tokens)) {
116
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Tokens Map must be a JSON object', {
117
+ itemIndex: i,
118
+ });
119
+ }
120
+ args.tokens = tokens;
121
+ }
122
+ else if (this.getNodeParameter('useNer', i, false)) {
123
+ args.use_ner = true;
124
+ }
125
+ const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'mindRouterApi', {
126
+ method: 'POST',
127
+ url: mcpUrl,
128
+ body: {
129
+ jsonrpc: '2.0',
130
+ id: i + 1,
131
+ method: 'tools/call',
132
+ params: { name: toolNames[operation], arguments: args },
133
+ },
134
+ json: true,
135
+ }));
136
+ if ((_a = response.error) === null || _a === void 0 ? void 0 : _a.message) {
137
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), response.error.message, { itemIndex: i });
138
+ }
139
+ if ((_b = response.result) === null || _b === void 0 ? void 0 : _b.isError) {
140
+ const msg = (_e = (_d = (_c = response.result.content) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : 'PII tool call failed';
141
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), msg, { itemIndex: i });
142
+ }
143
+ let payload = (_f = response.result) === null || _f === void 0 ? void 0 : _f.structuredContent;
144
+ if (!payload && ((_j = (_h = (_g = response.result) === null || _g === void 0 ? void 0 : _g.content) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.text)) {
145
+ payload = JSON.parse(response.result.content[0].text);
146
+ }
147
+ returnData.push({ json: payload !== null && payload !== void 0 ? payload : {}, pairedItem: { item: i } });
148
+ }
149
+ catch (error) {
150
+ if (this.continueOnFail()) {
151
+ returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
152
+ continue;
153
+ }
154
+ throw error;
155
+ }
156
+ }
157
+ return [returnData];
158
+ }
159
+ }
160
+ exports.MindRouterPiiGuard = MindRouterPiiGuard;
@@ -0,0 +1,28 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" role="img" aria-label="MindRouter">
2
+ <rect width="60" height="60" rx="12" fill="#101826"/>
3
+ <g transform="translate(8 12)">
4
+ <defs>
5
+ <mask id="mh">
6
+ <rect width="44" height="36" fill="#fff"/>
7
+ <circle cx="22" cy="18.5" r="5.4" fill="#000"/>
8
+ </mask>
9
+ </defs>
10
+ <g mask="url(#mh)">
11
+ <path d="M4 32 L12 7 L22 19 Z" fill="#FFFFFF"/>
12
+ <path d="M22 19 L31 10 L38 32 Z" fill="#FFFFFF"/>
13
+ </g>
14
+ <circle cx="22" cy="18.5" r="5.4" fill="none" stroke="#FFFFFF" stroke-width="2"/>
15
+ <g transform="translate(18 14.5) scale(0.5)">
16
+ <g fill="none" stroke="#14B8B0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
17
+ <path d="M3.8 8 H8"/>
18
+ <path d="M8 8 V5 H12"/>
19
+ <path d="M8 8 V11 H12"/>
20
+ </g>
21
+ <g fill="#14B8B0">
22
+ <circle cx="3.8" cy="8" r="1.5"/>
23
+ <circle cx="12.2" cy="5" r="1.3"/>
24
+ <circle cx="12.2" cy="11" r="1.3"/>
25
+ </g>
26
+ </g>
27
+ </g>
28
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-mindrouter",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "n8n community node for MindRouter — unified gateway to AI models with Indonesian PII guardrails",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -39,7 +39,9 @@
39
39
  ],
40
40
  "nodes": [
41
41
  "dist/nodes/MindRouter/MindRouter.node.js",
42
- "dist/nodes/MindRouterChatModel/MindRouterChatModel.node.js"
42
+ "dist/nodes/MindRouterChatModel/MindRouterChatModel.node.js",
43
+ "dist/nodes/MindRouterEmbeddings/MindRouterEmbeddings.node.js",
44
+ "dist/nodes/MindRouterPiiGuard/MindRouterPiiGuard.node.js"
43
45
  ]
44
46
  },
45
47
  "devDependencies": {