mcp-state-machine-test-framework 1.2.0 → 1.2.3
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 +3 -2
- package/index.js +148 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,9 +51,10 @@ If you are an AI agent helping with this project, follow these rules:
|
|
|
51
51
|
## 🚀 Mission Bootstrap (Copy & Paste to any AI Agent)
|
|
52
52
|
> **YOUR ONLY MISSION**:
|
|
53
53
|
> 1. Run `npx -y -p mcp-state-machine-test-framework mcp-sms-init` to setup EVERYTHING.
|
|
54
|
-
> 2.
|
|
54
|
+
> 2. Use `design_wizard` (action: 'start') to design nodes step-by-step.
|
|
55
|
+
> 3. **STRICT RULE**: Never edit JSON files manually.
|
|
55
56
|
>
|
|
56
|
-
> "Run `npx -y -p mcp-state-machine-test-framework mcp-sms-init`.
|
|
57
|
+
> "Run `npx -y -p mcp-state-machine-test-framework mcp-sms-init`. Then start a design session using `design_wizard` (action: 'start'). Follow the wizard steps to create nodes and transitions."
|
|
57
58
|
|
|
58
59
|
---
|
|
59
60
|
*Developed with ❤️ for maximum automation efficiency.*
|
package/index.js
CHANGED
|
@@ -78,10 +78,10 @@ export class MaquinaDeEstados {
|
|
|
78
78
|
async loadExternalData(dataPath) {
|
|
79
79
|
const fullPath = path.isAbsolute(dataPath) ? dataPath : path.join(__dirname, dataPath);
|
|
80
80
|
if (!fsSync.existsSync(fullPath)) return null;
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
const content = await fs.readFile(fullPath, "utf-8");
|
|
83
83
|
if (fullPath.endsWith(".json")) return JSON.parse(content);
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
if (fullPath.endsWith(".csv")) {
|
|
86
86
|
const lines = content.split("\n").map(l => l.trim()).filter(l => l.length > 0);
|
|
87
87
|
if (lines.length < 2) return [];
|
|
@@ -104,7 +104,7 @@ export class MaquinaDeEstados {
|
|
|
104
104
|
async ejecutarSuite(suiteName) {
|
|
105
105
|
const suite = this.suites.get(suiteName);
|
|
106
106
|
if (!suite) throw new Error(`Suite '${suiteName}' no encontrada.`);
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
if (suite.state_map) {
|
|
109
109
|
process.stderr.write(`\n📍 [V12.3] Pre-flight Integrity Check for: ${suite.state_map}\n`);
|
|
110
110
|
// Simplified validation: Ensure the file exists and has the required structure
|
|
@@ -129,8 +129,8 @@ export class MaquinaDeEstados {
|
|
|
129
129
|
if (typeof text !== "string") return text;
|
|
130
130
|
// Enmascarar tokens de seguridad y JWTs
|
|
131
131
|
return text.replace(/("securityToken"\s*:\s*")[^"]+(")/g, '$1********$2')
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
.replace(/("token"\s*:\s*")[^"]+(")/g, '$1********$2')
|
|
133
|
+
.replace(/eyJhbGciOi[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*/g, '********[JWT_MASKED]********');
|
|
134
134
|
};
|
|
135
135
|
|
|
136
136
|
const runActions = async (actions, targetRes, data) => {
|
|
@@ -145,7 +145,7 @@ export class MaquinaDeEstados {
|
|
|
145
145
|
const executeAction = async (act) => {
|
|
146
146
|
const subRes = (act === action) ? actionRes : { action: act, status: "passed" };
|
|
147
147
|
if (act !== action) targetRes.push(subRes);
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
let finalAction = act;
|
|
150
150
|
try {
|
|
151
151
|
if (act.startsWith("transicion:")) {
|
|
@@ -153,7 +153,7 @@ export class MaquinaDeEstados {
|
|
|
153
153
|
const mapPath = path.join(__dirname, 'maps', suite.state_map);
|
|
154
154
|
const mapData = JSON.parse(await fs.readFile(mapPath, 'utf8'));
|
|
155
155
|
const nodes = mapData.nodos || mapData;
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
let foundAction = null;
|
|
158
158
|
for (const node of Object.values(nodes)) {
|
|
159
159
|
if (node.transiciones && node.transiciones[transName]) {
|
|
@@ -187,18 +187,18 @@ export class MaquinaDeEstados {
|
|
|
187
187
|
|
|
188
188
|
const { client } = await this.getMcpClient(serverName);
|
|
189
189
|
lastResult = await client.callTool({ name: toolName, arguments: toolArgs }, undefined, { timeout: 600000 });
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
if (lastResult.isError && toolName !== "close_session") {
|
|
192
192
|
throw new Error(lastResult.content?.[0]?.text || "Error desconocido en herramienta MCP");
|
|
193
193
|
}
|
|
194
|
-
|
|
194
|
+
|
|
195
195
|
if (lastResult.content) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
for (const item of lastResult.content) {
|
|
197
|
+
if (item.text) {
|
|
198
|
+
subRes.output = (subRes.output || "") + "\n" + maskSecrets(item.text);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
202
|
|
|
203
203
|
if (lastResult.content) {
|
|
204
204
|
let base64 = "";
|
|
@@ -233,19 +233,19 @@ export class MaquinaDeEstados {
|
|
|
233
233
|
for (const caseName of suite.tests) {
|
|
234
234
|
const testCase = this.casosPrueba.get(caseName);
|
|
235
235
|
if (!testCase) continue;
|
|
236
|
-
|
|
236
|
+
|
|
237
237
|
let dataRows = [null];
|
|
238
238
|
if (testCase.data) {
|
|
239
239
|
if (Array.isArray(testCase.data)) dataRows = testCase.data;
|
|
240
240
|
else if (typeof testCase.data === "string") dataRows = await this.loadExternalData(testCase.data) || [null];
|
|
241
241
|
}
|
|
242
|
-
|
|
242
|
+
|
|
243
243
|
for (let i = 0; i < dataRows.length; i++) {
|
|
244
244
|
const row = dataRows[i];
|
|
245
|
-
const suffix = row ? ` [Iteration ${i+1}]` : "";
|
|
245
|
+
const suffix = row ? ` [Iteration ${i + 1}]` : "";
|
|
246
246
|
const caseRes = { name: testCase.name + suffix, steps: [], status: "passed", hooks: { beforeCase: [], afterCase: [] } };
|
|
247
247
|
results.cases.push(caseRes);
|
|
248
|
-
|
|
248
|
+
|
|
249
249
|
try {
|
|
250
250
|
await runActions(suite.beforeCase, caseRes.hooks.beforeCase, row);
|
|
251
251
|
for (const step of testCase.steps) {
|
|
@@ -326,20 +326,20 @@ export class MaquinaDeEstados {
|
|
|
326
326
|
|
|
327
327
|
const maquina = new MaquinaDeEstados();
|
|
328
328
|
const server = new McpServer({ name: "demo-state-machine", version: "11.1.0" });
|
|
329
|
-
server.tool("execute_suite", "Ejecución de Suite Completa", {
|
|
330
|
-
name: z.string().describe("Nombre de la suite a ejecutar (ej: 'Suite_Login'). Buscará el archivo en /suites.")
|
|
329
|
+
server.tool("execute_suite", "Ejecución de Suite Completa", {
|
|
330
|
+
name: z.string().describe("Nombre de la suite a ejecutar (ej: 'Suite_Login'). Buscará el archivo en /suites.")
|
|
331
331
|
}, async ({ name }) => {
|
|
332
332
|
await maquina.cargar();
|
|
333
333
|
const dir = await maquina.ejecutarSuite(name);
|
|
334
334
|
return { content: [{ type: "text", text: `Reporte: ${dir}` }] };
|
|
335
335
|
});
|
|
336
336
|
|
|
337
|
-
server.tool("upsert_node", "Añadir o actualizar un nodo en el mapa de estados.", {
|
|
338
|
-
mapName: z.string().describe("Nombre del archivo del mapa (ej: 'home_map.json'). El servidor lo buscará en la carpeta /maps."),
|
|
339
|
-
nodeName: z.string().describe("Identificador único del nodo (ej: 'LOGIN_PAGE', 'HOME')."),
|
|
337
|
+
server.tool("upsert_node", "Añadir o actualizar un nodo en el mapa de estados.", {
|
|
338
|
+
mapName: z.string().describe("Nombre del archivo del mapa (ej: 'home_map.json'). El servidor lo buscará en la carpeta /maps."),
|
|
339
|
+
nodeName: z.string().describe("Identificador único del nodo (ej: 'LOGIN_PAGE', 'HOME')."),
|
|
340
340
|
nodeData: z.object({
|
|
341
341
|
transiciones: z.any().optional().describe("Objeto que define las salidas del nodo. Ej: { 'IR_A_LOGIN': { 'destino': 'LOGIN_PAGE', 'accion': 'mcp:wdio-mcp/click_element ...' } }")
|
|
342
|
-
}).passthrough().describe("Datos completos del nodo, incluyendo transiciones y metadatos.")
|
|
342
|
+
}).passthrough().describe("Datos completos del nodo, incluyendo transiciones y metadatos.")
|
|
343
343
|
}, async ({ mapName, nodeName, nodeData }) => {
|
|
344
344
|
const mapPath = path.join(__dirname, 'maps', mapName);
|
|
345
345
|
let map = { nodos: {} };
|
|
@@ -347,8 +347,8 @@ server.tool("upsert_node", "Añadir o actualizar un nodo en el mapa de estados."
|
|
|
347
347
|
const content = await fs.readFile(mapPath, 'utf8');
|
|
348
348
|
map = JSON.parse(content);
|
|
349
349
|
if (!map.nodos) map = { nodos: map };
|
|
350
|
-
} catch (e) {}
|
|
351
|
-
|
|
350
|
+
} catch (e) { }
|
|
351
|
+
|
|
352
352
|
map.nodos[nodeName] = nodeData;
|
|
353
353
|
await fs.mkdir(path.dirname(mapPath), { recursive: true });
|
|
354
354
|
await fs.writeFile(mapPath, JSON.stringify(map, null, 2));
|
|
@@ -372,8 +372,8 @@ server.tool("upsert_node", "Añadir o actualizar un nodo en el mapa de estados."
|
|
|
372
372
|
return { content: [{ type: "text", text: `Nodo '${nodeName}' actualizado. Diagrama visual regenerado en maps/${mapName.replace('.json', '.md')}` }] };
|
|
373
373
|
});
|
|
374
374
|
|
|
375
|
-
server.tool("inspect_framework", "Inspeccionar integridad y listar entidades del framework", {
|
|
376
|
-
filter: z.optional(z.string())
|
|
375
|
+
server.tool("inspect_framework", "Inspeccionar integridad y listar entidades del framework", {
|
|
376
|
+
filter: z.optional(z.string())
|
|
377
377
|
}, async () => {
|
|
378
378
|
const entities = { maps: [], test_cases: [], suites: [], health: [] };
|
|
379
379
|
const getFiles = async (dir) => {
|
|
@@ -402,15 +402,46 @@ server.tool("inspect_framework", "Inspeccionar integridad y listar entidades del
|
|
|
402
402
|
|
|
403
403
|
if (entities.health.length === 0) entities.health.push("✅ Estructura íntegra. Todos los vínculos son correctos.");
|
|
404
404
|
|
|
405
|
+
// Validación de integridad de Grafos (Nodos Huérfanos)
|
|
406
|
+
for (const mapFile of entities.maps) {
|
|
407
|
+
try {
|
|
408
|
+
const mapData = JSON.parse(await fs.readFile(path.join(__dirname, 'maps', mapFile), 'utf8'));
|
|
409
|
+
const nodos = mapData.nodos || mapData;
|
|
410
|
+
if (nodos && nodos["HOME"]) {
|
|
411
|
+
const reachable = new Set(["HOME"]);
|
|
412
|
+
const stack = ["HOME"];
|
|
413
|
+
while (stack.length > 0) {
|
|
414
|
+
const current = stack.pop();
|
|
415
|
+
const node = nodos[current];
|
|
416
|
+
if (node && node.transiciones) {
|
|
417
|
+
for (const t of Object.values(node.transiciones)) {
|
|
418
|
+
if (t.destino && nodos[t.destino] && !reachable.has(t.destino)) {
|
|
419
|
+
reachable.add(t.destino);
|
|
420
|
+
stack.push(t.destino);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const totalNodes = Object.keys(nodos);
|
|
426
|
+
const unreachable = totalNodes.filter(n => !reachable.has(n));
|
|
427
|
+
if (unreachable.length > 0) {
|
|
428
|
+
entities.health.push(`⚠️ Mapa '${mapFile}': Nodos inalcanzables desde HOME: ${unreachable.join(', ')}`);
|
|
429
|
+
}
|
|
430
|
+
} else if (nodos && !nodos["HOME"]) {
|
|
431
|
+
entities.health.push(`⚠️ Mapa '${mapFile}': No tiene un nodo 'HOME' (inicio), no se puede validar el grafo.`);
|
|
432
|
+
}
|
|
433
|
+
} catch (e) { entities.health.push(`❌ Error validando grafo de '${mapFile}': ${e.message}`); }
|
|
434
|
+
}
|
|
435
|
+
|
|
405
436
|
return { content: [{ type: "text", text: JSON.stringify(entities, null, 2) }] };
|
|
406
437
|
});
|
|
407
438
|
|
|
408
|
-
server.tool("save_test_case", "Crear o actualizar un caso de prueba.", {
|
|
409
|
-
name: z.string().describe("Nombre identificador del test (ej: 'TC_Login_Exitoso')."),
|
|
439
|
+
server.tool("save_test_case", "Crear o actualizar un caso de prueba.", {
|
|
440
|
+
name: z.string().describe("Nombre identificador del test (ej: 'TC_Login_Exitoso')."),
|
|
410
441
|
steps: z.array(z.object({
|
|
411
442
|
name: z.string().optional().describe("Descripción amigable del paso (ej: 'Ingresar Usuario')."),
|
|
412
443
|
action: z.string().optional().describe("Acción a realizar. Puede ser un nombre de transición del mapa o un comando mcp:wdio-mcp/...")
|
|
413
|
-
}).passthrough()).describe("Lista ordenada de pasos lógicos a ejecutar.")
|
|
444
|
+
}).passthrough()).describe("Lista ordenada de pasos lógicos a ejecutar.")
|
|
414
445
|
}, async ({ name, steps }) => {
|
|
415
446
|
const fileName = name.endsWith('.json') ? name : `${name}.json`;
|
|
416
447
|
const filePath = path.join(__dirname, 'test_cases', fileName);
|
|
@@ -420,8 +451,8 @@ server.tool("save_test_case", "Crear o actualizar un caso de prueba.", {
|
|
|
420
451
|
return { content: [{ type: "text", text: `Caso de prueba '${name}' guardado correctamente.` }] };
|
|
421
452
|
});
|
|
422
453
|
|
|
423
|
-
server.tool("save_suite", "Crear o actualizar una suite de pruebas.", {
|
|
424
|
-
name: z.string().describe("Nombre de la suite (ej: 'Suite_E2E_Sanity')."),
|
|
454
|
+
server.tool("save_suite", "Crear o actualizar una suite de pruebas.", {
|
|
455
|
+
name: z.string().describe("Nombre de la suite (ej: 'Suite_E2E_Sanity')."),
|
|
425
456
|
state_map: z.string().describe("Archivo del mapa de estados a usar (ej: 'mob_perfecto_map.json')."),
|
|
426
457
|
tests: z.array(z.string()).describe("Lista de nombres de casos de prueba a incluir en la suite."),
|
|
427
458
|
beforeSuite: z.array(z.string()).optional().default([]).describe("Acciones globales antes de la suite (ej: iniciar sesión)."),
|
|
@@ -435,11 +466,71 @@ server.tool("save_suite", "Crear o actualizar una suite de pruebas.", {
|
|
|
435
466
|
return { content: [{ type: "text", text: `Suite '${name}' guardada correctamente.` }] };
|
|
436
467
|
});
|
|
437
468
|
|
|
438
|
-
server.tool("
|
|
439
|
-
|
|
469
|
+
server.tool("design_wizard", "Asistente paso a paso para diseñar la máquina de estados", {
|
|
470
|
+
action: z.enum(["start", "add_node", "add_transition", "save"]),
|
|
471
|
+
data: z.record(z.any()).optional().describe("Datos según la fase: { mapName }, { nodeName }, { label, destino, accion }")
|
|
472
|
+
}, async ({ action, data }) => {
|
|
473
|
+
const contextPath = path.join(__dirname, 'data', '.design_context.json');
|
|
474
|
+
let context = { step: "IDLE", mapName: "", nodeName: "", transitions: {} };
|
|
475
|
+
|
|
476
|
+
try { context = JSON.parse(await fs.readFile(contextPath, 'utf8')); } catch (e) { }
|
|
477
|
+
|
|
478
|
+
if (action === "start") {
|
|
479
|
+
context = { step: "SELECT_NODE", mapName: data.mapName || "default_map.json", nodeName: "", transitions: {} };
|
|
480
|
+
await fs.writeFile(contextPath, JSON.stringify(context, null, 2));
|
|
481
|
+
return { content: [{ type: "text", text: `🧙♂️ Wizard Iniciado: Trabajando en '${context.mapName}'.\n\nPASO 1: ¿Qué nombre le damos al nodo? (Usa action: 'add_node')` }] };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (action === "add_node" && context.step === "SELECT_NODE") {
|
|
485
|
+
context.nodeName = data.nodeName;
|
|
486
|
+
context.step = "ADD_TRANSITIONS";
|
|
487
|
+
await fs.writeFile(contextPath, JSON.stringify(context, null, 2));
|
|
488
|
+
return { content: [{ type: "text", text: `📍 Nodo '${context.nodeName}' identificado.\n\nPASO 2: Define una transición. (Usa action: 'add_transition' con { label, destino, accion }). Puedes llamar a esto varias veces.` }] };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (action === "add_transition" && context.step === "ADD_TRANSITIONS") {
|
|
492
|
+
context.transitions[data.label] = { destino: data.destino, accion: data.accion };
|
|
493
|
+
await fs.writeFile(contextPath, JSON.stringify(context, null, 2));
|
|
494
|
+
return { content: [{ type: "text", text: `🔄 Transición '${data.label}' añadida.\n\n¿Quieres añadir otra o guardar? (Usa action: 'save' para finalizar)` }] };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (action === "save" && context.step === "ADD_TRANSITIONS") {
|
|
498
|
+
const result = await server.callTool("upsert_node", {
|
|
499
|
+
mapName: context.mapName,
|
|
500
|
+
nodeName: context.nodeName,
|
|
501
|
+
nodeData: { transiciones: context.transitions }
|
|
502
|
+
});
|
|
503
|
+
await fs.unlink(contextPath); // Reset wizard
|
|
504
|
+
return result;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return { content: [{ type: "text", text: `❌ Acción no permitida en el estado actual (${context.step}).` }] };
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
server.tool("begin_design", "Iniciar una sesión de diseño guiado para una aplicación", {
|
|
511
|
+
appName: z.string().describe("Nombre de la aplicación a diseñar (ej: 'AdvantageShopping').")
|
|
512
|
+
}, async ({ appName }) => {
|
|
513
|
+
const rules = `
|
|
514
|
+
🎭 HAS ENTRADO EN MODO DISEÑO (SMS ARCHITECT) 🎭
|
|
515
|
+
Estás diseñando la estructura para: ${appName}
|
|
516
|
+
|
|
517
|
+
REGLAS CRÍTICAS DE SUPERVIVENCIA:
|
|
518
|
+
1. ❌ PROHIBIDO editar archivos JSON manualmente.
|
|
519
|
+
2. ❌ PROHIBIDO usar los campos 'nodes' o 'edges'. NO SOMOS UNA LIBRERÍA DE GRÁFICOS.
|
|
520
|
+
3. ✅ USA EXCLUSIVAMENTE 'upsert_node' para crear o actualizar estados.
|
|
521
|
+
4. ✅ ESTRUCTURA OBLIGATORIA: { "nodos": { "NOMBRE_NODO": { "transiciones": {} } } }
|
|
522
|
+
5. 🔄 FLUJO: Primero propón el diseño en texto -> Pide confirmación -> Usa la herramienta.
|
|
523
|
+
|
|
524
|
+
Dime qué pantalla o flujo quieres empezar a mapear para ${appName}.
|
|
525
|
+
`;
|
|
526
|
+
return { content: [{ type: "text", text: rules }] };
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
server.tool("init_project", "Inicializa el proyecto con carpetas y archivos de plantilla", {
|
|
530
|
+
force: z.optional(z.boolean()).default(false)
|
|
440
531
|
}, async ({ force }) => {
|
|
441
532
|
await ensureDirectories();
|
|
442
|
-
|
|
533
|
+
|
|
443
534
|
const templates = {
|
|
444
535
|
'maps/template_map.json': {
|
|
445
536
|
nodos: {
|
|
@@ -478,7 +569,7 @@ server.tool("init_project", "Inicializa el proyecto con carpetas y archivos de p
|
|
|
478
569
|
try {
|
|
479
570
|
await fs.access(fullPath);
|
|
480
571
|
continue; // Skip if exists
|
|
481
|
-
} catch (e) {}
|
|
572
|
+
} catch (e) { }
|
|
482
573
|
}
|
|
483
574
|
await fs.writeFile(fullPath, JSON.stringify(content, null, 2));
|
|
484
575
|
} catch (e) {
|
|
@@ -486,7 +577,21 @@ server.tool("init_project", "Inicializa el proyecto con carpetas y archivos de p
|
|
|
486
577
|
}
|
|
487
578
|
}
|
|
488
579
|
|
|
489
|
-
return {
|
|
580
|
+
return {
|
|
581
|
+
content: [{
|
|
582
|
+
type: "text",
|
|
583
|
+
text: "✅ Instalación completada con éxito.\n\nComo este es un proyecto nuevo, el siguiente paso es definir tu aplicación. Por favor, llama a la herramienta `begin_design` para empezar a mapear tus primeros estados."
|
|
584
|
+
}]
|
|
585
|
+
};
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
server.tool("framework_menu", "Panel de control para elegir entre Modo Diseño o Modo Ejecución", {}, async () => {
|
|
589
|
+
return {
|
|
590
|
+
content: [{
|
|
591
|
+
type: "text",
|
|
592
|
+
text: "🎮 **Framework Control Panel**\n\n¿Qué deseas hacer ahora?\n1. 🎨 **Modo Diseño**: Añadir o modificar nodos y mapas (Usa `begin_design`).\n2. 🚀 **Modo Ejecución**: Lanzar suites de pruebas existentes (Usa `execute_suite`).\n3. 🔍 **Auditoría**: Revisar la integridad del sistema (Usa `inspect_framework`)."
|
|
593
|
+
}]
|
|
594
|
+
};
|
|
490
595
|
});
|
|
491
596
|
|
|
492
597
|
async function ensureDirectories() {
|
|
@@ -501,10 +606,10 @@ async function ensureDirectories() {
|
|
|
501
606
|
}
|
|
502
607
|
}
|
|
503
608
|
|
|
504
|
-
async function main() {
|
|
609
|
+
async function main() {
|
|
505
610
|
await ensureDirectories();
|
|
506
|
-
await maquina.cargar();
|
|
507
|
-
const transport = new StdioServerTransport();
|
|
508
|
-
await server.connect(transport);
|
|
611
|
+
await maquina.cargar();
|
|
612
|
+
const transport = new StdioServerTransport();
|
|
613
|
+
await server.connect(transport);
|
|
509
614
|
}
|
|
510
615
|
main().catch(console.error);
|