n8n-nodes-mindrouter 0.1.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
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# n8n-nodes-mindrouter
|
|
2
|
+
|
|
3
|
+
Official [n8n](https://n8n.io) community node for [MindRouter](https://mindrouter.io) — a unified gateway to AI models with built-in Indonesian PII guardrails, spend controls, and team management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
In n8n: **Settings → Community Nodes → Install**, enter:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
n8n-nodes-mindrouter
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Self-hosted (npm):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install n8n-nodes-mindrouter
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Credentials
|
|
20
|
+
|
|
21
|
+
1. Create an API key in your [MindRouter dashboard](https://mindrouter.io) under **API Keys**.
|
|
22
|
+
2. In n8n, add a **MindRouter API** credential and paste the key.
|
|
23
|
+
3. Leave the base URL as `https://api.mindrouter.io/v1` unless you run a self-hosted gateway.
|
|
24
|
+
|
|
25
|
+
## Operations
|
|
26
|
+
|
|
27
|
+
### Chat
|
|
28
|
+
|
|
29
|
+
Send a prompt (or a full OpenAI-format `messages` array) to any model your account can use. The model dropdown is loaded live from your account, so it always matches your plan and provider settings. Supports temperature, max output tokens, and a simplified output (`text`, `model`, `usage`) or the raw API response.
|
|
30
|
+
|
|
31
|
+
The node is usable as a tool by n8n **AI Agent** nodes.
|
|
32
|
+
|
|
33
|
+
### List Models
|
|
34
|
+
|
|
35
|
+
Returns the models available to your account, including pricing metadata.
|
|
36
|
+
|
|
37
|
+
## Compatibility
|
|
38
|
+
|
|
39
|
+
Requires n8n 1.x and Node.js 18.10+.
|
|
40
|
+
|
|
41
|
+
## Resources
|
|
42
|
+
|
|
43
|
+
- [MindRouter documentation](https://mindrouter.io/docs)
|
|
44
|
+
- [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MindRouterApi = void 0;
|
|
4
|
+
class MindRouterApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'mindRouterApi';
|
|
7
|
+
this.displayName = 'MindRouter API';
|
|
8
|
+
this.documentationUrl = 'https://mindrouter.io/docs';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'API Key',
|
|
12
|
+
name: 'apiKey',
|
|
13
|
+
type: 'string',
|
|
14
|
+
typeOptions: { password: true },
|
|
15
|
+
required: true,
|
|
16
|
+
default: '',
|
|
17
|
+
description: 'Create one in the MindRouter dashboard under API Keys',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'Base URL',
|
|
21
|
+
name: 'baseUrl',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: 'https://api.mindrouter.io/v1',
|
|
24
|
+
description: 'Change only for a self-hosted or regional gateway',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
this.authenticate = {
|
|
28
|
+
type: 'generic',
|
|
29
|
+
properties: {
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: '=Bearer {{$credentials.apiKey}}',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
this.test = {
|
|
36
|
+
request: {
|
|
37
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
38
|
+
url: '/models',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.MindRouterApi = MindRouterApi;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MindRouter = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
function normalizeBaseUrl(raw) {
|
|
6
|
+
const url = String(raw !== null && raw !== void 0 ? raw : 'https://api.mindrouter.io/v1').trim();
|
|
7
|
+
return url.replace(/\/+$/, '');
|
|
8
|
+
}
|
|
9
|
+
class MindRouter {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.description = {
|
|
12
|
+
displayName: 'MindRouter',
|
|
13
|
+
name: 'mindRouter',
|
|
14
|
+
icon: 'file:mindrouter.svg',
|
|
15
|
+
group: ['transform'],
|
|
16
|
+
version: 1,
|
|
17
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
18
|
+
description: 'Chat with AI models through the MindRouter gateway',
|
|
19
|
+
defaults: {
|
|
20
|
+
name: 'MindRouter',
|
|
21
|
+
},
|
|
22
|
+
usableAsTool: true,
|
|
23
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
24
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
25
|
+
credentials: [
|
|
26
|
+
{
|
|
27
|
+
name: 'mindRouterApi',
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
properties: [
|
|
32
|
+
{
|
|
33
|
+
displayName: 'Operation',
|
|
34
|
+
name: 'operation',
|
|
35
|
+
type: 'options',
|
|
36
|
+
noDataExpression: true,
|
|
37
|
+
options: [
|
|
38
|
+
{
|
|
39
|
+
name: 'Chat',
|
|
40
|
+
value: 'chat',
|
|
41
|
+
action: 'Send a chat completion',
|
|
42
|
+
description: 'Send messages to a model and get its reply',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'List Models',
|
|
46
|
+
value: 'listModels',
|
|
47
|
+
action: 'List available models',
|
|
48
|
+
description: 'List the models your account can use',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
default: 'chat',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
displayName: 'Model Name or ID',
|
|
55
|
+
name: 'model',
|
|
56
|
+
type: 'options',
|
|
57
|
+
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
58
|
+
typeOptions: {
|
|
59
|
+
loadOptionsMethod: 'getModels',
|
|
60
|
+
},
|
|
61
|
+
displayOptions: {
|
|
62
|
+
show: { operation: ['chat'] },
|
|
63
|
+
},
|
|
64
|
+
required: true,
|
|
65
|
+
default: '',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
displayName: 'Prompt',
|
|
69
|
+
name: 'prompt',
|
|
70
|
+
type: 'string',
|
|
71
|
+
typeOptions: { rows: 4 },
|
|
72
|
+
displayOptions: {
|
|
73
|
+
show: { operation: ['chat'] },
|
|
74
|
+
},
|
|
75
|
+
required: true,
|
|
76
|
+
default: '',
|
|
77
|
+
description: 'The user message to send',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
displayName: 'Options',
|
|
81
|
+
name: 'options',
|
|
82
|
+
type: 'collection',
|
|
83
|
+
placeholder: 'Add option',
|
|
84
|
+
default: {},
|
|
85
|
+
displayOptions: {
|
|
86
|
+
show: { operation: ['chat'] },
|
|
87
|
+
},
|
|
88
|
+
options: [
|
|
89
|
+
{
|
|
90
|
+
displayName: 'System Prompt',
|
|
91
|
+
name: 'systemPrompt',
|
|
92
|
+
type: 'string',
|
|
93
|
+
typeOptions: { rows: 3 },
|
|
94
|
+
default: '',
|
|
95
|
+
description: 'Instructions that steer the model, sent as the system message',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
displayName: 'Messages (JSON)',
|
|
99
|
+
name: 'messagesJson',
|
|
100
|
+
type: 'json',
|
|
101
|
+
default: '',
|
|
102
|
+
description: 'Full messages array in OpenAI format. When set, it replaces Prompt and System Prompt.',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
displayName: 'Temperature',
|
|
106
|
+
name: 'temperature',
|
|
107
|
+
type: 'number',
|
|
108
|
+
typeOptions: { minValue: 0, maxValue: 2, numberPrecision: 2 },
|
|
109
|
+
default: 1,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
displayName: 'Max Output Tokens',
|
|
113
|
+
name: 'maxTokens',
|
|
114
|
+
type: 'number',
|
|
115
|
+
typeOptions: { minValue: 1 },
|
|
116
|
+
default: 1024,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
displayName: 'Simplify Output',
|
|
120
|
+
name: 'simplify',
|
|
121
|
+
type: 'boolean',
|
|
122
|
+
default: true,
|
|
123
|
+
description: 'Whether to return just the reply text, model, and token usage instead of the raw API response',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
this.methods = {
|
|
130
|
+
loadOptions: {
|
|
131
|
+
async getModels() {
|
|
132
|
+
var _a;
|
|
133
|
+
const credentials = await this.getCredentials('mindRouterApi');
|
|
134
|
+
const baseUrl = normalizeBaseUrl(credentials.baseUrl);
|
|
135
|
+
const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'mindRouterApi', {
|
|
136
|
+
method: 'GET',
|
|
137
|
+
url: `${baseUrl}/models`,
|
|
138
|
+
json: true,
|
|
139
|
+
}));
|
|
140
|
+
const models = (_a = response.data) !== null && _a !== void 0 ? _a : [];
|
|
141
|
+
return models
|
|
142
|
+
.map((m) => ({
|
|
143
|
+
name: m.display_name || m.id,
|
|
144
|
+
value: m.id,
|
|
145
|
+
}))
|
|
146
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async execute() {
|
|
152
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
153
|
+
const items = this.getInputData();
|
|
154
|
+
const returnData = [];
|
|
155
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
156
|
+
const credentials = await this.getCredentials('mindRouterApi');
|
|
157
|
+
const baseUrl = normalizeBaseUrl(credentials.baseUrl);
|
|
158
|
+
for (let i = 0; i < items.length; i++) {
|
|
159
|
+
try {
|
|
160
|
+
if (operation === 'listModels') {
|
|
161
|
+
const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'mindRouterApi', {
|
|
162
|
+
method: 'GET',
|
|
163
|
+
url: `${baseUrl}/models`,
|
|
164
|
+
json: true,
|
|
165
|
+
}));
|
|
166
|
+
for (const model of (_a = response.data) !== null && _a !== void 0 ? _a : []) {
|
|
167
|
+
returnData.push({ json: model, pairedItem: { item: i } });
|
|
168
|
+
}
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const model = this.getNodeParameter('model', i);
|
|
172
|
+
const prompt = this.getNodeParameter('prompt', i);
|
|
173
|
+
const options = this.getNodeParameter('options', i, {});
|
|
174
|
+
let messages;
|
|
175
|
+
if (options.messagesJson) {
|
|
176
|
+
const parsed = typeof options.messagesJson === 'string'
|
|
177
|
+
? JSON.parse(options.messagesJson)
|
|
178
|
+
: options.messagesJson;
|
|
179
|
+
if (!Array.isArray(parsed)) {
|
|
180
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Messages (JSON) must be an array', {
|
|
181
|
+
itemIndex: i,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
messages = parsed;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
messages = [];
|
|
188
|
+
if (options.systemPrompt) {
|
|
189
|
+
messages.push({ role: 'system', content: options.systemPrompt });
|
|
190
|
+
}
|
|
191
|
+
messages.push({ role: 'user', content: prompt });
|
|
192
|
+
}
|
|
193
|
+
const body = { model, messages };
|
|
194
|
+
if (options.temperature !== undefined)
|
|
195
|
+
body.temperature = options.temperature;
|
|
196
|
+
if (options.maxTokens !== undefined)
|
|
197
|
+
body.max_tokens = options.maxTokens;
|
|
198
|
+
const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'mindRouterApi', {
|
|
199
|
+
method: 'POST',
|
|
200
|
+
url: `${baseUrl}/chat/completions`,
|
|
201
|
+
body,
|
|
202
|
+
json: true,
|
|
203
|
+
}));
|
|
204
|
+
if (options.simplify === false) {
|
|
205
|
+
returnData.push({ json: response, pairedItem: { item: i } });
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const choices = response.choices;
|
|
209
|
+
const message = (_b = choices === null || choices === void 0 ? void 0 : choices[0]) === null || _b === void 0 ? void 0 : _b.message;
|
|
210
|
+
returnData.push({
|
|
211
|
+
json: {
|
|
212
|
+
text: (_c = message === null || message === void 0 ? void 0 : message.content) !== null && _c !== void 0 ? _c : '',
|
|
213
|
+
model: (_d = response.model) !== null && _d !== void 0 ? _d : model,
|
|
214
|
+
usage: (_e = response.usage) !== null && _e !== void 0 ? _e : null,
|
|
215
|
+
finish_reason: (_g = (_f = choices === null || choices === void 0 ? void 0 : choices[0]) === null || _f === void 0 ? void 0 : _f.finish_reason) !== null && _g !== void 0 ? _g : null,
|
|
216
|
+
},
|
|
217
|
+
pairedItem: { item: i },
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (this.continueOnFail()) {
|
|
223
|
+
returnData.push({
|
|
224
|
+
json: { error: error.message },
|
|
225
|
+
pairedItem: { item: i },
|
|
226
|
+
});
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return [returnData];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.MindRouter = MindRouter;
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-mindrouter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "n8n community node for MindRouter — unified gateway to AI models with Indonesian PII guardrails",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"mindrouter",
|
|
8
|
+
"ai",
|
|
9
|
+
"llm",
|
|
10
|
+
"gateway"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://mindrouter.io",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "MindRouter",
|
|
16
|
+
"email": "support@mindrouter.io"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/mindrouterio/mindrouterio-n8n.git"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18.10"
|
|
24
|
+
},
|
|
25
|
+
"main": "index.js",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc && node scripts/copy-assets.mjs",
|
|
28
|
+
"dev": "tsc --watch",
|
|
29
|
+
"lint": "eslint nodes credentials",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"n8n": {
|
|
36
|
+
"n8nNodesApiVersion": 1,
|
|
37
|
+
"credentials": [
|
|
38
|
+
"dist/credentials/MindRouterApi.credentials.js"
|
|
39
|
+
],
|
|
40
|
+
"nodes": [
|
|
41
|
+
"dist/nodes/MindRouter/MindRouter.node.js"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
46
|
+
"eslint": "^9.0.0",
|
|
47
|
+
"eslint-plugin-n8n-nodes-base": "^1.16.0",
|
|
48
|
+
"n8n-workflow": "^1.70.0",
|
|
49
|
+
"typescript": "^5.6.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"n8n-workflow": "*"
|
|
53
|
+
}
|
|
54
|
+
}
|