@aiconnect/agentjobs-mcp 1.0.9 → 1.2.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/.env.example +7 -0
- package/README.md +67 -0
- package/build/config.js +10 -3
- package/build/config.test.js +22 -0
- package/build/debug.js +86 -0
- package/build/index.js +71 -7
- package/build/lib/agentJobsClient.js +90 -0
- package/build/test-tools.js +53 -0
- package/build/tools/cancel_job.js +35 -50
- package/build/tools/create_job.js +49 -69
- package/build/tools/get_context.js +61 -0
- package/build/tools/get_job.js +30 -58
- package/build/tools/get_job_type.js +40 -56
- package/build/tools/get_jobs_stats.js +47 -0
- package/build/tools/list_jobs.js +44 -58
- package/build/utils/debugger.js +74 -0
- package/build/utils/formatters.js +516 -44
- package/build/utils/formatters.test.js +629 -0
- package/build/utils/schemas.js +24 -0
- package/build/utils/schemas.test.js +95 -0
- package/build/utils/version.js +12 -0
- package/build/utils/version.test.js +9 -0
- package/docs/agent-jobs-api.md +41 -0
- package/docs/debug-guide.md +203 -0
- package/package.json +13 -4
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
4
|
+
import { flexibleDateTimeSchema } from './schemas.js';
|
|
5
|
+
describe('flexibleDateTimeSchema', () => {
|
|
6
|
+
it('should validate a correct ISO 8601 string with Zulu time', () => {
|
|
7
|
+
const result = flexibleDateTimeSchema().safeParse('2025-07-23T21:00:00Z');
|
|
8
|
+
expect(result.success).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
it('should validate a correct ISO 8601 string with a positive offset', () => {
|
|
11
|
+
const result = flexibleDateTimeSchema().safeParse('2025-07-23T22:00:00+01:00');
|
|
12
|
+
expect(result.success).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it('should validate a correct ISO 8601 string with a negative offset', () => {
|
|
15
|
+
const result = flexibleDateTimeSchema().safeParse('2025-07-23T16:00:00-05:00');
|
|
16
|
+
expect(result.success).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it('should not validate an incorrect date format', () => {
|
|
19
|
+
const result = flexibleDateTimeSchema().safeParse('23/07/2025 21:00:00');
|
|
20
|
+
expect(result.success).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
it('should not validate a gibberish string', () => {
|
|
23
|
+
const result = flexibleDateTimeSchema().safeParse('not-a-date');
|
|
24
|
+
expect(result.success).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it('should not validate an empty string', () => {
|
|
27
|
+
const result = flexibleDateTimeSchema().safeParse('');
|
|
28
|
+
expect(result.success).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
it('should validate a date-only string (no time component)', () => {
|
|
31
|
+
const result = flexibleDateTimeSchema().safeParse('2024-07-23');
|
|
32
|
+
expect(result.success).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('should reject invalid string with the exact ISO 8601 error message', () => {
|
|
35
|
+
const result = flexibleDateTimeSchema().safeParse('not-a-date');
|
|
36
|
+
expect(result.success).toBe(false);
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
expect(result.error.issues[0].message).toBe('Invalid date-time string. Please use a valid ISO 8601 format.');
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
it('should reject null when used with .optional() (string | undefined, not nullable)', () => {
|
|
42
|
+
const schema = flexibleDateTimeSchema().optional();
|
|
43
|
+
expect(schema.safeParse(undefined).success).toBe(true);
|
|
44
|
+
const nullResult = schema.safeParse(null);
|
|
45
|
+
expect(nullResult.success).toBe(false);
|
|
46
|
+
if (!nullResult.success) {
|
|
47
|
+
const issue = nullResult.error.issues[0];
|
|
48
|
+
expect(issue.code).toBe('invalid_type');
|
|
49
|
+
// Zod 3: { expected: 'string', received: 'null' }
|
|
50
|
+
expect(issue.expected).toBe('string');
|
|
51
|
+
expect(issue.received).toBe('null');
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('JSON Schema serialization', () => {
|
|
56
|
+
it('should inline each list_jobs date filter field without $ref', () => {
|
|
57
|
+
const shape = z.object({
|
|
58
|
+
scheduled_at: flexibleDateTimeSchema().optional(),
|
|
59
|
+
scheduled_at_gte: flexibleDateTimeSchema().optional(),
|
|
60
|
+
scheduled_at_lte: flexibleDateTimeSchema().optional(),
|
|
61
|
+
created_at_gte: flexibleDateTimeSchema().optional(),
|
|
62
|
+
created_at_lte: flexibleDateTimeSchema().optional(),
|
|
63
|
+
});
|
|
64
|
+
const json = zodToJsonSchema(shape);
|
|
65
|
+
const expectedFields = [
|
|
66
|
+
'scheduled_at',
|
|
67
|
+
'scheduled_at_gte',
|
|
68
|
+
'scheduled_at_lte',
|
|
69
|
+
'created_at_gte',
|
|
70
|
+
'created_at_lte',
|
|
71
|
+
];
|
|
72
|
+
for (const field of expectedFields) {
|
|
73
|
+
const prop = json.properties[field];
|
|
74
|
+
expect(prop.$ref, `expected ${field} to be inline, got $ref ${String(prop.$ref)}`).toBeUndefined();
|
|
75
|
+
expect(prop.type, `expected ${field} to have type 'string', got ${String(prop.type)}`).toBe('string');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
it('should produce $ref-free JSON for both list_jobs and get_jobs_stats shapes', () => {
|
|
79
|
+
const listJobsShape = z.object({
|
|
80
|
+
scheduled_at: flexibleDateTimeSchema().optional(),
|
|
81
|
+
scheduled_at_gte: flexibleDateTimeSchema().optional(),
|
|
82
|
+
scheduled_at_lte: flexibleDateTimeSchema().optional(),
|
|
83
|
+
created_at_gte: flexibleDateTimeSchema().optional(),
|
|
84
|
+
created_at_lte: flexibleDateTimeSchema().optional(),
|
|
85
|
+
});
|
|
86
|
+
const statsShape = z.object({
|
|
87
|
+
scheduled_at_gte: flexibleDateTimeSchema().optional(),
|
|
88
|
+
scheduled_at_lte: flexibleDateTimeSchema().optional(),
|
|
89
|
+
created_at_gte: flexibleDateTimeSchema().optional(),
|
|
90
|
+
created_at_lte: flexibleDateTimeSchema().optional(),
|
|
91
|
+
});
|
|
92
|
+
expect(JSON.stringify(zodToJsonSchema(listJobsShape))).not.toContain('$ref');
|
|
93
|
+
expect(JSON.stringify(zodToJsonSchema(statsShape))).not.toContain('$ref');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
function readVersion() {
|
|
3
|
+
try {
|
|
4
|
+
const raw = fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf-8');
|
|
5
|
+
const pkg = JSON.parse(raw);
|
|
6
|
+
return pkg.version ?? 'unknown';
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return 'unknown';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export const mcpServerVersion = readVersion();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mcpServerVersion } from './version.js';
|
|
3
|
+
describe('mcpServerVersion', () => {
|
|
4
|
+
it('resolves to a non-empty string from package.json in dev tree', () => {
|
|
5
|
+
expect(typeof mcpServerVersion).toBe('string');
|
|
6
|
+
expect(mcpServerVersion.length).toBeGreaterThan(0);
|
|
7
|
+
expect(mcpServerVersion).not.toBe('unknown');
|
|
8
|
+
});
|
|
9
|
+
});
|
package/docs/agent-jobs-api.md
CHANGED
|
@@ -239,6 +239,47 @@ Cancels a job by changing its status to CANCELED.
|
|
|
239
239
|
- 404: Job not found
|
|
240
240
|
- 409: Conflict (job is already in a terminal state)
|
|
241
241
|
|
|
242
|
+
### List Job Types (`agent-jobs-type`)
|
|
243
|
+
|
|
244
|
+
Lists agent job types registered for an organization. Used by `get_job_type` and `get_context` in this MCP.
|
|
245
|
+
|
|
246
|
+
#### Path em uso pelas tools deste MCP hoje
|
|
247
|
+
|
|
248
|
+
```http
|
|
249
|
+
GET /organizations/:org_id/agent-jobs-type
|
|
250
|
+
GET /organizations/:org_id/agent-jobs-type/:job_type_id
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
(Sem prefixo `/services/`.) **Toda tool nova neste repo deve usar este caminho** até a unificação descrita abaixo, para manter consistência com `get_job_type`.
|
|
254
|
+
|
|
255
|
+
#### Path canônico upstream
|
|
256
|
+
|
|
257
|
+
```http
|
|
258
|
+
GET /services/organizations/:org_id/agent-jobs-type
|
|
259
|
+
GET /services/organizations/:org_id/agent-jobs-type/:job_type_id
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Este é o caminho documentado upstream pelo AI Connect API. O backend serve a mesma controller em ambos os mounts (legacy + atual). Uma migração futura para o caminho canônico deve ser feita em **change separado abrangendo todas as tools afetadas**: `get_job_type`, `list_jobs`, `create_job`, `get_jobs_stats` e `get_context`.
|
|
263
|
+
|
|
264
|
+
#### Query parameters suportados
|
|
265
|
+
|
|
266
|
+
- `enrich=emoji` — adiciona o campo `emoji` no payload (cache server-side de 1h por job_type).
|
|
267
|
+
- `include=schema` — inclui `params_schema` completo de cada job type (não usado por `get_context`; pesado).
|
|
268
|
+
- `limit` — número máximo de itens por página. `get_context` fixa em `100`.
|
|
269
|
+
- `offset` — paginação numérica.
|
|
270
|
+
- `sort` — formato `field:direction`, ex: `name:asc` (default usado por `get_context`).
|
|
271
|
+
|
|
272
|
+
#### Response shape
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"data": [
|
|
277
|
+
{ "id": "string", "name": "string", "description": "string?", "emoji": "string?" }
|
|
278
|
+
],
|
|
279
|
+
"meta": { "total": 0, "limit": 100, "offset": 0 }
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
242
283
|
## Job Status Values
|
|
243
284
|
|
|
244
285
|
Jobs can have the following status values:
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# 🔍 Guia de Debug do MCP Server
|
|
2
|
+
|
|
3
|
+
Este guia fornece várias maneiras de depurar e diagnosticar problemas no seu MCP Server.
|
|
4
|
+
|
|
5
|
+
## 🚀 Comandos de Debug Disponíveis
|
|
6
|
+
|
|
7
|
+
### 1. Debug Básico
|
|
8
|
+
```bash
|
|
9
|
+
# Compilar e rodar em modo debug
|
|
10
|
+
npm run debug
|
|
11
|
+
|
|
12
|
+
# Debug com variáveis de ambiente específicas
|
|
13
|
+
MCP_DEBUG=true npm run debug
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 2. Testar Tools
|
|
17
|
+
```bash
|
|
18
|
+
# Verificar se todas as tools estão carregando
|
|
19
|
+
npm run test:tools
|
|
20
|
+
|
|
21
|
+
# Verificar configuração atual
|
|
22
|
+
npm run cli:config
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 3. Debug com Logs Detalhados
|
|
26
|
+
```bash
|
|
27
|
+
# Usar arquivo de ambiente de debug
|
|
28
|
+
cp .env.debug .env
|
|
29
|
+
# Editar .env com suas credenciais
|
|
30
|
+
npm run debug
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 🛠️ Tipos de Debug
|
|
34
|
+
|
|
35
|
+
### 1. **Debug de Configuração**
|
|
36
|
+
```bash
|
|
37
|
+
npm run cli:config
|
|
38
|
+
```
|
|
39
|
+
Este comando mostra:
|
|
40
|
+
- URL da API
|
|
41
|
+
- Status da chave de API
|
|
42
|
+
- Versão do Node.js
|
|
43
|
+
- Versão do MCP Server
|
|
44
|
+
|
|
45
|
+
### 2. **Debug de Carregamento de Tools**
|
|
46
|
+
```bash
|
|
47
|
+
npm run test:tools
|
|
48
|
+
```
|
|
49
|
+
Este comando:
|
|
50
|
+
- Lista todas as tools encontradas
|
|
51
|
+
- Testa o carregamento de cada tool
|
|
52
|
+
- Mostra erros de carregamento se houver
|
|
53
|
+
|
|
54
|
+
### 3. **Debug Completo em Runtime**
|
|
55
|
+
```bash
|
|
56
|
+
MCP_DEBUG=true npm run debug
|
|
57
|
+
```
|
|
58
|
+
Com `MCP_DEBUG=true`, você verá:
|
|
59
|
+
- Logs de inicialização detalhados
|
|
60
|
+
- Configuração carregada
|
|
61
|
+
- Chamadas de tools em tempo real
|
|
62
|
+
- Respostas das APIs
|
|
63
|
+
- Erros detalhados
|
|
64
|
+
|
|
65
|
+
## 📊 Interpretando os Logs
|
|
66
|
+
|
|
67
|
+
### Logs de Tool
|
|
68
|
+
```
|
|
69
|
+
[MCP-DEBUG 2025-08-13T...] [INFO] Tool called: get_jobs_stats
|
|
70
|
+
[MCP-DEBUG 2025-08-13T...] [DEBUG] Data: {"org_id":"aiconnect"}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Logs de API
|
|
74
|
+
```
|
|
75
|
+
[MCP-DEBUG 2025-08-13T...] [DEBUG] HTTP GET https://api.aiconnect.cloud/api/v0/jobs/stats
|
|
76
|
+
[MCP-DEBUG 2025-08-13T...] [DEBUG] HTTP Response GET [200]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Logs de Erro
|
|
80
|
+
```
|
|
81
|
+
[MCP-DEBUG 2025-08-13T...] [ERROR] Tool error: get_jobs_stats
|
|
82
|
+
[MCP-DEBUG 2025-08-13T...] [ERROR] Data: {"error": "API key not provided"}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 🔧 Problemas Comuns e Soluções
|
|
86
|
+
|
|
87
|
+
### 1. **API Key não configurada**
|
|
88
|
+
**Erro:** `API key not provided`
|
|
89
|
+
**Solução:**
|
|
90
|
+
```bash
|
|
91
|
+
# Definir a chave de API
|
|
92
|
+
export AICONNECT_API_KEY="sua-chave-aqui"
|
|
93
|
+
npm run debug
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. **URL da API incorreta**
|
|
97
|
+
**Erro:** `Request failed with status 404`
|
|
98
|
+
**Solução:**
|
|
99
|
+
```bash
|
|
100
|
+
# Verificar/definir URL da API
|
|
101
|
+
export AICONNECT_API_URL="https://api.aiconnect.cloud/api/v0"
|
|
102
|
+
npm run debug
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 3. **Tools não carregando**
|
|
106
|
+
**Erro:** `Error loading tools`
|
|
107
|
+
**Solução:**
|
|
108
|
+
```bash
|
|
109
|
+
# Verificar se o build está atualizado
|
|
110
|
+
npm run build
|
|
111
|
+
npm run test:tools
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 4. **Problemas de Conexão MCP**
|
|
115
|
+
**Erro:** `Transport connection closed`
|
|
116
|
+
**Solução:**
|
|
117
|
+
- Verificar se o cliente MCP está conectado corretamente
|
|
118
|
+
- Usar logs de debug para ver detalhes da conexão
|
|
119
|
+
|
|
120
|
+
## 📝 Configuração para Claude Desktop
|
|
121
|
+
|
|
122
|
+
Para debug no Claude Desktop, use esta configuração no `claude_desktop_config.json`:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcpServers": {
|
|
127
|
+
"agentjobs-debug": {
|
|
128
|
+
"command": "node",
|
|
129
|
+
"args": ["/caminho/para/agentjobs-mcp/build/debug.js"],
|
|
130
|
+
"env": {
|
|
131
|
+
"MCP_DEBUG": "true",
|
|
132
|
+
"AICONNECT_API_KEY": "sua-chave-aqui",
|
|
133
|
+
"AICONNECT_API_URL": "https://api.aiconnect.cloud/api/v0",
|
|
134
|
+
"DEFAULT_ORG_ID": "aiconnect"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## 🧪 Testes de Funcionalidade
|
|
142
|
+
|
|
143
|
+
### Teste Manual das Tools
|
|
144
|
+
|
|
145
|
+
1. **Listar Jobs:**
|
|
146
|
+
- Comando: "List all jobs"
|
|
147
|
+
- Debug: Verificar se a API é chamada e se os dados são formatados
|
|
148
|
+
|
|
149
|
+
2. **Buscar Job Específico:**
|
|
150
|
+
- Comando: "Get job details for job-123"
|
|
151
|
+
- Debug: Verificar parâmetros enviados e resposta recebida
|
|
152
|
+
|
|
153
|
+
3. **Criar Job:**
|
|
154
|
+
- Comando: "Create a new job"
|
|
155
|
+
- Debug: Verificar payload da criação e resposta
|
|
156
|
+
|
|
157
|
+
### Teste de Configuração
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Verificar todas as configurações
|
|
161
|
+
npm run cli:config
|
|
162
|
+
|
|
163
|
+
# Verificar carregamento das tools
|
|
164
|
+
npm run test:tools
|
|
165
|
+
|
|
166
|
+
# Verificar versão
|
|
167
|
+
npm run cli:version
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 🎯 Dicas de Debug
|
|
171
|
+
|
|
172
|
+
1. **Use logs incrementais:** Comece com debug básico e aumente verbosidade conforme necessário
|
|
173
|
+
|
|
174
|
+
2. **Teste isoladamente:** Use `npm run test:tools` para verificar carregamento antes de testar funcionalidade
|
|
175
|
+
|
|
176
|
+
3. **Monitore a API:** Se possível, monitore logs da API AI Connect em paralelo
|
|
177
|
+
|
|
178
|
+
4. **Verifique configuração:** Sempre rode `npm run cli:config` primeiro
|
|
179
|
+
|
|
180
|
+
5. **Use ambiente controlado:** Use `.env.debug` para configuração consistente
|
|
181
|
+
|
|
182
|
+
## 📋 Checklist de Debug
|
|
183
|
+
|
|
184
|
+
- [ ] Configuração carregada corretamente (`npm run cli:config`)
|
|
185
|
+
- [ ] Tools carregando sem erro (`npm run test:tools`)
|
|
186
|
+
- [ ] API key configurada
|
|
187
|
+
- [ ] URL da API acessível
|
|
188
|
+
- [ ] Logs de debug habilitados (`MCP_DEBUG=true`)
|
|
189
|
+
- [ ] Conexão MCP funcionando
|
|
190
|
+
- [ ] Tools respondendo conforme esperado
|
|
191
|
+
|
|
192
|
+
## 🆘 Debugging Avançado
|
|
193
|
+
|
|
194
|
+
Para problemas complexos, você pode:
|
|
195
|
+
|
|
196
|
+
1. **Adicionar mais logs** nas tools específicas
|
|
197
|
+
2. **Usar Node.js debugger** com `--inspect`
|
|
198
|
+
3. **Monitorar requisições HTTP** com `DEBUG=axios`
|
|
199
|
+
4. **Verificar memória e performance** com `node --trace-warnings`
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
💡 **Lembre-se:** O debug é mais eficiente quando feito de forma incremental, testando cada componente isoladamente antes de testar o sistema completo.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiconnect/agentjobs-mcp",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server for managing Agent Jobs in the AI Connect platform. Developed by AI Connect - Advanced AI automation and integration solutions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -28,10 +28,14 @@
|
|
|
28
28
|
"build": "tsc && chmod +x build/index.js",
|
|
29
29
|
"start": "node build/index.js",
|
|
30
30
|
"dev": "tsc && node build/index.js",
|
|
31
|
+
"debug": "tsc && node build/debug.js",
|
|
32
|
+
"debug:watch": "tsc --watch & sleep 2 && node build/debug.js",
|
|
31
33
|
"clean": "rm -rf build",
|
|
32
34
|
"typecheck": "tsc --noEmit",
|
|
33
|
-
"lint": "
|
|
34
|
-
"
|
|
35
|
+
"lint": "eslint . --ext .ts,.js",
|
|
36
|
+
"lint:fix": "eslint . --ext .ts,.js --fix",
|
|
37
|
+
"test": "vitest",
|
|
38
|
+
"test:tools": "npm run build && node build/test-tools.js",
|
|
35
39
|
"prepare": "npm run build",
|
|
36
40
|
"version:patch": "npm version patch",
|
|
37
41
|
"version:minor": "npm version minor",
|
|
@@ -53,7 +57,12 @@
|
|
|
53
57
|
},
|
|
54
58
|
"devDependencies": {
|
|
55
59
|
"@types/node": "^22.10.5",
|
|
56
|
-
"
|
|
60
|
+
"@eslint/js": "^9.14.0",
|
|
61
|
+
"eslint": "^9.14.0",
|
|
62
|
+
"typescript-eslint": "^8.12.2",
|
|
63
|
+
"typescript": "^5.7.2",
|
|
64
|
+
"vitest": "^3.2.4",
|
|
65
|
+
"zod-to-json-schema": "^3.23.0"
|
|
57
66
|
},
|
|
58
67
|
"engines": {
|
|
59
68
|
"node": ">=18.0.0"
|