@imenam/self-register-proxy 1.0.46 → 1.0.48

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/meta-config.js CHANGED
@@ -1,336 +1,336 @@
1
- /**
2
- * MetaConfigManager - Gestion des méta-configurations pour le proxy
3
- *
4
- * Permet de grouper les configurations de plusieurs MCPs en un seul profil
5
- * applicable en un clic depuis le dashboard.
6
- *
7
- * Persistence : meta-configs.json dans le répertoire du proxy (__dirname)
8
- */
9
-
10
- const fs = require('fs');
11
- const path = require('path');
12
-
13
- // Détermine le dossier de configuration
14
- // Utilise PROXY_CONFIG_DIR si défini, sinon le dossier courant (__dirname)
15
- const configDir = process.env.PROXY_CONFIG_DIR
16
- ? path.resolve(__dirname, process.env.PROXY_CONFIG_DIR)
17
- : __dirname;
18
-
19
- // Création du dossier s'il n'existe pas
20
- if (!fs.existsSync(configDir)) {
21
- try {
22
- fs.mkdirSync(configDir, { recursive: true });
23
- } catch (err) {
24
- console.error(`[META-CONFIG] Erreur lors de la création du dossier ${configDir}:`, err.message);
25
- }
26
- }
27
-
28
- const META_CONFIGS_FILE = path.join(configDir, 'meta-configs.json');
29
-
30
- const DISCOVERY_TIMEOUT = 3000; // 3s timeout per MCP
31
-
32
- /**
33
- * Structure du fichier meta-configs.json :
34
- * {
35
- * "metaConfigs": {
36
- * "<id>": {
37
- * "name": "string",
38
- * "description": "string",
39
- * "mappings": { "<mcpPath>": "<configId>", ... },
40
- * "createdAt": "ISO8601"
41
- * }
42
- * },
43
- * "activeMetaConfig": "<id>" | null
44
- * }
45
- */
46
-
47
- /**
48
- * Charge les méta-configurations depuis le fichier JSON
49
- * @returns {{ metaConfigs: Object, activeMetaConfig: string|null }}
50
- */
51
- function loadMetaConfigs() {
52
- try {
53
- if (fs.existsSync(META_CONFIGS_FILE)) {
54
- const raw = fs.readFileSync(META_CONFIGS_FILE, 'utf-8');
55
- return JSON.parse(raw);
56
- }
57
- } catch (err) {
58
- console.error('[META-CONFIG] Error loading meta-configs:', err.message);
59
- }
60
- return { metaConfigs: {}, activeMetaConfig: null };
61
- }
62
-
63
- /**
64
- * Sauvegarde les méta-configurations dans le fichier JSON
65
- * @param {{ metaConfigs: Object, activeMetaConfig: string|null }} data
66
- */
67
- function saveMetaConfigs(data) {
68
- try {
69
- fs.writeFileSync(META_CONFIGS_FILE, JSON.stringify(data, null, 2), 'utf-8');
70
- } catch (err) {
71
- console.error('[META-CONFIG] Error saving meta-configs:', err.message);
72
- throw err;
73
- }
74
- }
75
-
76
- /**
77
- * Retourne toutes les méta-configurations
78
- * @returns {{ metaConfigs: Object, activeMetaConfig: string|null }}
79
- */
80
- function getAll() {
81
- return loadMetaConfigs();
82
- }
83
-
84
- /**
85
- * Retourne une méta-configuration par son ID
86
- * @param {string} id
87
- * @returns {Object|null}
88
- */
89
- function getById(id) {
90
- const data = loadMetaConfigs();
91
- return data.metaConfigs[id] || null;
92
- }
93
-
94
- /**
95
- * Crée une nouvelle méta-configuration
96
- * @param {string} id - Identifiant unique (slug)
97
- * @param {string} name - Nom affiché
98
- * @param {string} description - Description
99
- * @param {Object} mappings - { mcpPath: configId, ... }
100
- * @returns {Object} La méta-configuration créée
101
- */
102
- function create(id, name, description, mappings) {
103
- const data = loadMetaConfigs();
104
-
105
- if (data.metaConfigs[id]) {
106
- throw new Error(`Meta-config "${id}" already exists`);
107
- }
108
-
109
- data.metaConfigs[id] = {
110
- name,
111
- description: description || '',
112
- mappings: mappings || {},
113
- createdAt: new Date().toISOString()
114
- };
115
-
116
- saveMetaConfigs(data);
117
- return data.metaConfigs[id];
118
- }
119
-
120
- /**
121
- * Met à jour partiellement une méta-configuration
122
- * @param {string} id
123
- * @param {Object} updates - Champs à mettre à jour (name, description, mappings)
124
- * @returns {Object} La méta-configuration mise à jour
125
- */
126
- function update(id, updates) {
127
- const data = loadMetaConfigs();
128
-
129
- if (!data.metaConfigs[id]) {
130
- throw new Error(`Meta-config "${id}" not found`);
131
- }
132
-
133
- const config = data.metaConfigs[id];
134
- if (updates.name !== undefined) config.name = updates.name;
135
- if (updates.description !== undefined) config.description = updates.description;
136
- if (updates.mappings !== undefined) config.mappings = updates.mappings;
137
-
138
- saveMetaConfigs(data);
139
- return config;
140
- }
141
-
142
- /**
143
- * Supprime une méta-configuration
144
- * @param {string} id
145
- */
146
- function remove(id) {
147
- const data = loadMetaConfigs();
148
-
149
- if (!data.metaConfigs[id]) {
150
- throw new Error(`Meta-config "${id}" not found`);
151
- }
152
-
153
- delete data.metaConfigs[id];
154
-
155
- if (data.activeMetaConfig === id) {
156
- data.activeMetaConfig = null;
157
- }
158
-
159
- saveMetaConfigs(data);
160
- }
161
-
162
- /**
163
- * Définit la méta-configuration active
164
- * @param {string|null} id
165
- */
166
- function setActive(id) {
167
- const data = loadMetaConfigs();
168
-
169
- if (id !== null && !data.metaConfigs[id]) {
170
- throw new Error(`Meta-config "${id}" not found`);
171
- }
172
-
173
- data.activeMetaConfig = id;
174
- saveMetaConfigs(data);
175
- }
176
-
177
- /**
178
- * Retourne l'ID de la méta-configuration active
179
- * @returns {string|null}
180
- */
181
- function getActive() {
182
- const data = loadMetaConfigs();
183
- return data.activeMetaConfig;
184
- }
185
-
186
- /**
187
- * Découvre les configurations disponibles auprès de tous les MCPs enregistrés
188
- * Interroge GET /api/configurations sur chaque MCP via son target direct
189
- *
190
- * @param {Map} routes - Le registry.routes du proxy
191
- * @returns {Promise<Array<{ path: string, name: string, configurations: Array|null, error: string|null }>>}
192
- */
193
- async function discoverConfigurations(routes) {
194
- const results = [];
195
-
196
- const promises = [];
197
- for (const [mcpPath, route] of routes.entries()) {
198
- const target = route.target || 'http://localhost';
199
- const url = `${target}:${route.port}/api/configurations`;
200
-
201
- promises.push(
202
- (async () => {
203
- try {
204
- const controller = new AbortController();
205
- const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT);
206
-
207
- const response = await fetch(url, {
208
- method: 'GET',
209
- signal: controller.signal
210
- });
211
-
212
- clearTimeout(timeoutId);
213
-
214
- if (response.status === 200) {
215
- const data = await response.json();
216
- return {
217
- path: mcpPath,
218
- name: route.name,
219
- configurations: data.configurations || [],
220
- error: null
221
- };
222
- }
223
-
224
- return {
225
- path: mcpPath,
226
- name: route.name,
227
- configurations: null,
228
- error: `HTTP ${response.status}`
229
- };
230
- } catch (err) {
231
- return {
232
- path: mcpPath,
233
- name: route.name,
234
- configurations: null,
235
- error: err.name === 'AbortError' ? 'timeout' : err.message
236
- };
237
- }
238
- })()
239
- );
240
- }
241
-
242
- const settled = await Promise.all(promises);
243
- results.push(...settled);
244
-
245
- return results;
246
- }
247
-
248
- /**
249
- * Applique une méta-configuration : envoie POST /api/configurations/apply à chaque MCP
250
- *
251
- * @param {string} metaConfigId - ID de la méta-configuration à appliquer
252
- * @param {Map} routes - Le registry.routes du proxy
253
- * @returns {Promise<{ success: boolean, results: Array<{ path: string, success: boolean, error?: string }> }>}
254
- */
255
- async function applyMetaConfig(metaConfigId, routes) {
256
- const config = getById(metaConfigId);
257
- if (!config) {
258
- throw new Error(`Meta-config "${metaConfigId}" not found`);
259
- }
260
-
261
- const missingResults = [];
262
- const promises = [];
263
-
264
- for (const [mcpPath, configId] of Object.entries(config.mappings)) {
265
- const route = routes.get(mcpPath);
266
- if (!route) {
267
- missingResults.push({
268
- path: mcpPath,
269
- success: false,
270
- error: 'MCP not registered'
271
- });
272
- continue;
273
- }
274
-
275
- const target = route.target || 'http://localhost';
276
- const url = `${target}:${route.port}/api/configurations/apply`;
277
-
278
- promises.push(
279
- (async () => {
280
- try {
281
- const controller = new AbortController();
282
- const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT);
283
-
284
- const response = await fetch(url, {
285
- method: 'POST',
286
- headers: { 'Content-Type': 'application/json' },
287
- body: JSON.stringify({ configId }),
288
- signal: controller.signal
289
- });
290
-
291
- clearTimeout(timeoutId);
292
-
293
- if (response.ok) {
294
- const data = await response.json();
295
- return { path: mcpPath, success: true, appliedConfig: data.appliedConfig || configId };
296
- }
297
-
298
- const errorData = await response.json().catch(() => ({}));
299
- return { path: mcpPath, success: false, error: errorData.error || `HTTP ${response.status}` };
300
- } catch (err) {
301
- return {
302
- path: mcpPath,
303
- success: false,
304
- error: err.name === 'AbortError' ? 'timeout' : err.message
305
- };
306
- }
307
- })()
308
- );
309
- }
310
-
311
- const settled = await Promise.all(promises);
312
-
313
- const allAppliedSuccess = settled.length === 0 || settled.every(r => r.success);
314
-
315
- if (allAppliedSuccess) {
316
- setActive(metaConfigId);
317
- }
318
-
319
- return {
320
- success: allAppliedSuccess,
321
- warnings: missingResults,
322
- results: [...missingResults, ...settled]
323
- };
324
- }
325
-
326
- module.exports = {
327
- getAll,
328
- getById,
329
- create,
330
- update,
331
- remove,
332
- setActive,
333
- getActive,
334
- discoverConfigurations,
335
- applyMetaConfig
336
- };
1
+ /**
2
+ * MetaConfigManager - Gestion des méta-configurations pour le proxy
3
+ *
4
+ * Permet de grouper les configurations de plusieurs MCPs en un seul profil
5
+ * applicable en un clic depuis le dashboard.
6
+ *
7
+ * Persistence : meta-configs.json dans le répertoire du proxy (__dirname)
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ // Détermine le dossier de configuration
14
+ // Utilise PROXY_CONFIG_DIR si défini, sinon le dossier courant (__dirname)
15
+ const configDir = process.env.PROXY_CONFIG_DIR
16
+ ? path.resolve(__dirname, process.env.PROXY_CONFIG_DIR)
17
+ : __dirname;
18
+
19
+ // Création du dossier s'il n'existe pas
20
+ if (!fs.existsSync(configDir)) {
21
+ try {
22
+ fs.mkdirSync(configDir, { recursive: true });
23
+ } catch (err) {
24
+ console.error(`[META-CONFIG] Erreur lors de la création du dossier ${configDir}:`, err.message);
25
+ }
26
+ }
27
+
28
+ const META_CONFIGS_FILE = path.join(configDir, 'meta-configs.json');
29
+
30
+ const DISCOVERY_TIMEOUT = 3000; // 3s timeout per MCP
31
+
32
+ /**
33
+ * Structure du fichier meta-configs.json :
34
+ * {
35
+ * "metaConfigs": {
36
+ * "<id>": {
37
+ * "name": "string",
38
+ * "description": "string",
39
+ * "mappings": { "<mcpPath>": "<configId>", ... },
40
+ * "createdAt": "ISO8601"
41
+ * }
42
+ * },
43
+ * "activeMetaConfig": "<id>" | null
44
+ * }
45
+ */
46
+
47
+ /**
48
+ * Charge les méta-configurations depuis le fichier JSON
49
+ * @returns {{ metaConfigs: Object, activeMetaConfig: string|null }}
50
+ */
51
+ function loadMetaConfigs() {
52
+ try {
53
+ if (fs.existsSync(META_CONFIGS_FILE)) {
54
+ const raw = fs.readFileSync(META_CONFIGS_FILE, 'utf-8');
55
+ return JSON.parse(raw);
56
+ }
57
+ } catch (err) {
58
+ console.error('[META-CONFIG] Error loading meta-configs:', err.message);
59
+ }
60
+ return { metaConfigs: {}, activeMetaConfig: null };
61
+ }
62
+
63
+ /**
64
+ * Sauvegarde les méta-configurations dans le fichier JSON
65
+ * @param {{ metaConfigs: Object, activeMetaConfig: string|null }} data
66
+ */
67
+ function saveMetaConfigs(data) {
68
+ try {
69
+ fs.writeFileSync(META_CONFIGS_FILE, JSON.stringify(data, null, 2), 'utf-8');
70
+ } catch (err) {
71
+ console.error('[META-CONFIG] Error saving meta-configs:', err.message);
72
+ throw err;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Retourne toutes les méta-configurations
78
+ * @returns {{ metaConfigs: Object, activeMetaConfig: string|null }}
79
+ */
80
+ function getAll() {
81
+ return loadMetaConfigs();
82
+ }
83
+
84
+ /**
85
+ * Retourne une méta-configuration par son ID
86
+ * @param {string} id
87
+ * @returns {Object|null}
88
+ */
89
+ function getById(id) {
90
+ const data = loadMetaConfigs();
91
+ return data.metaConfigs[id] || null;
92
+ }
93
+
94
+ /**
95
+ * Crée une nouvelle méta-configuration
96
+ * @param {string} id - Identifiant unique (slug)
97
+ * @param {string} name - Nom affiché
98
+ * @param {string} description - Description
99
+ * @param {Object} mappings - { mcpPath: configId, ... }
100
+ * @returns {Object} La méta-configuration créée
101
+ */
102
+ function create(id, name, description, mappings) {
103
+ const data = loadMetaConfigs();
104
+
105
+ if (data.metaConfigs[id]) {
106
+ throw new Error(`Meta-config "${id}" already exists`);
107
+ }
108
+
109
+ data.metaConfigs[id] = {
110
+ name,
111
+ description: description || '',
112
+ mappings: mappings || {},
113
+ createdAt: new Date().toISOString()
114
+ };
115
+
116
+ saveMetaConfigs(data);
117
+ return data.metaConfigs[id];
118
+ }
119
+
120
+ /**
121
+ * Met à jour partiellement une méta-configuration
122
+ * @param {string} id
123
+ * @param {Object} updates - Champs à mettre à jour (name, description, mappings)
124
+ * @returns {Object} La méta-configuration mise à jour
125
+ */
126
+ function update(id, updates) {
127
+ const data = loadMetaConfigs();
128
+
129
+ if (!data.metaConfigs[id]) {
130
+ throw new Error(`Meta-config "${id}" not found`);
131
+ }
132
+
133
+ const config = data.metaConfigs[id];
134
+ if (updates.name !== undefined) config.name = updates.name;
135
+ if (updates.description !== undefined) config.description = updates.description;
136
+ if (updates.mappings !== undefined) config.mappings = updates.mappings;
137
+
138
+ saveMetaConfigs(data);
139
+ return config;
140
+ }
141
+
142
+ /**
143
+ * Supprime une méta-configuration
144
+ * @param {string} id
145
+ */
146
+ function remove(id) {
147
+ const data = loadMetaConfigs();
148
+
149
+ if (!data.metaConfigs[id]) {
150
+ throw new Error(`Meta-config "${id}" not found`);
151
+ }
152
+
153
+ delete data.metaConfigs[id];
154
+
155
+ if (data.activeMetaConfig === id) {
156
+ data.activeMetaConfig = null;
157
+ }
158
+
159
+ saveMetaConfigs(data);
160
+ }
161
+
162
+ /**
163
+ * Définit la méta-configuration active
164
+ * @param {string|null} id
165
+ */
166
+ function setActive(id) {
167
+ const data = loadMetaConfigs();
168
+
169
+ if (id !== null && !data.metaConfigs[id]) {
170
+ throw new Error(`Meta-config "${id}" not found`);
171
+ }
172
+
173
+ data.activeMetaConfig = id;
174
+ saveMetaConfigs(data);
175
+ }
176
+
177
+ /**
178
+ * Retourne l'ID de la méta-configuration active
179
+ * @returns {string|null}
180
+ */
181
+ function getActive() {
182
+ const data = loadMetaConfigs();
183
+ return data.activeMetaConfig;
184
+ }
185
+
186
+ /**
187
+ * Découvre les configurations disponibles auprès de tous les MCPs enregistrés
188
+ * Interroge GET /api/configurations sur chaque MCP via son target direct
189
+ *
190
+ * @param {Map} routes - Le registry.routes du proxy
191
+ * @returns {Promise<Array<{ path: string, name: string, configurations: Array|null, error: string|null }>>}
192
+ */
193
+ async function discoverConfigurations(routes) {
194
+ const results = [];
195
+
196
+ const promises = [];
197
+ for (const [mcpPath, route] of routes.entries()) {
198
+ const target = route.target || 'http://localhost';
199
+ const url = `${target}:${route.port}/api/configurations`;
200
+
201
+ promises.push(
202
+ (async () => {
203
+ try {
204
+ const controller = new AbortController();
205
+ const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT);
206
+
207
+ const response = await fetch(url, {
208
+ method: 'GET',
209
+ signal: controller.signal
210
+ });
211
+
212
+ clearTimeout(timeoutId);
213
+
214
+ if (response.status === 200) {
215
+ const data = await response.json();
216
+ return {
217
+ path: mcpPath,
218
+ name: route.name,
219
+ configurations: data.configurations || [],
220
+ error: null
221
+ };
222
+ }
223
+
224
+ return {
225
+ path: mcpPath,
226
+ name: route.name,
227
+ configurations: null,
228
+ error: `HTTP ${response.status}`
229
+ };
230
+ } catch (err) {
231
+ return {
232
+ path: mcpPath,
233
+ name: route.name,
234
+ configurations: null,
235
+ error: err.name === 'AbortError' ? 'timeout' : err.message
236
+ };
237
+ }
238
+ })()
239
+ );
240
+ }
241
+
242
+ const settled = await Promise.all(promises);
243
+ results.push(...settled);
244
+
245
+ return results;
246
+ }
247
+
248
+ /**
249
+ * Applique une méta-configuration : envoie POST /api/configurations/apply à chaque MCP
250
+ *
251
+ * @param {string} metaConfigId - ID de la méta-configuration à appliquer
252
+ * @param {Map} routes - Le registry.routes du proxy
253
+ * @returns {Promise<{ success: boolean, results: Array<{ path: string, success: boolean, error?: string }> }>}
254
+ */
255
+ async function applyMetaConfig(metaConfigId, routes) {
256
+ const config = getById(metaConfigId);
257
+ if (!config) {
258
+ throw new Error(`Meta-config "${metaConfigId}" not found`);
259
+ }
260
+
261
+ const missingResults = [];
262
+ const promises = [];
263
+
264
+ for (const [mcpPath, configId] of Object.entries(config.mappings)) {
265
+ const route = routes.get(mcpPath);
266
+ if (!route) {
267
+ missingResults.push({
268
+ path: mcpPath,
269
+ success: false,
270
+ error: 'MCP not registered'
271
+ });
272
+ continue;
273
+ }
274
+
275
+ const target = route.target || 'http://localhost';
276
+ const url = `${target}:${route.port}/api/configurations/apply`;
277
+
278
+ promises.push(
279
+ (async () => {
280
+ try {
281
+ const controller = new AbortController();
282
+ const timeoutId = setTimeout(() => controller.abort(), DISCOVERY_TIMEOUT);
283
+
284
+ const response = await fetch(url, {
285
+ method: 'POST',
286
+ headers: { 'Content-Type': 'application/json' },
287
+ body: JSON.stringify({ configId }),
288
+ signal: controller.signal
289
+ });
290
+
291
+ clearTimeout(timeoutId);
292
+
293
+ if (response.ok) {
294
+ const data = await response.json();
295
+ return { path: mcpPath, success: true, appliedConfig: data.appliedConfig || configId };
296
+ }
297
+
298
+ const errorData = await response.json().catch(() => ({}));
299
+ return { path: mcpPath, success: false, error: errorData.error || `HTTP ${response.status}` };
300
+ } catch (err) {
301
+ return {
302
+ path: mcpPath,
303
+ success: false,
304
+ error: err.name === 'AbortError' ? 'timeout' : err.message
305
+ };
306
+ }
307
+ })()
308
+ );
309
+ }
310
+
311
+ const settled = await Promise.all(promises);
312
+
313
+ const allAppliedSuccess = settled.length === 0 || settled.every(r => r.success);
314
+
315
+ if (allAppliedSuccess) {
316
+ setActive(metaConfigId);
317
+ }
318
+
319
+ return {
320
+ success: allAppliedSuccess,
321
+ warnings: missingResults,
322
+ results: [...missingResults, ...settled]
323
+ };
324
+ }
325
+
326
+ module.exports = {
327
+ getAll,
328
+ getById,
329
+ create,
330
+ update,
331
+ remove,
332
+ setActive,
333
+ getActive,
334
+ discoverConfigurations,
335
+ applyMetaConfig
336
+ };