nova-control-mcp-server 0.0.8 → 0.0.9

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
@@ -129,7 +129,7 @@ All movement tools accept an optional `within_ms` parameter. When provided, the
129
129
  | `lift_to` | `angle: number`; `within_ms?` | lift head on secondary axis, range 20°–150° — s5 |
130
130
  | `wait` | `ms: number` | pause for `ms` milliseconds before the next action |
131
131
  | `get_state` | — | return current servo positions as a JSON object with keys `s1`–`s5` |
132
- | `run_script` | `script: string` | execute a multi-line movement script (one command per line; blank lines and `#`-comments ignored; commands: `home`, `shift-to`, `roll-to`, `pitch-to`, `rotate-to`, `lift-to`, `move`, `wait`) |
132
+ | `run_script` | `script: string` | execute a multi-line movement script (one command per line; blank lines and `#`-comments ignored; commands: `home [<within_ms>]`, `shift-to <angle> [<within_ms>]`, `roll-to`, `pitch-to`, `rotate-to`, `lift-to`, `move [...] [within-ms <ms>]`, `wait <ms>`) |
133
133
  | `disconnect` | — | close the serial connection to free the port; the connection reopens automatically on the next movement command |
134
134
 
135
135
  ### Servo mapping
@@ -7,42 +7,43 @@ import { Server as i } from "@modelcontextprotocol/sdk/server/index.js";
7
7
  import { StdioServerTransport as a } from "@modelcontextprotocol/sdk/server/stdio.js";
8
8
  import { StreamableHTTPServerTransport as o } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9
9
  import { CallToolRequestSchema as s, ListToolsRequestSchema as c } from "@modelcontextprotocol/sdk/types.js";
10
- import { SerialPort as l } from "serialport";
10
+ import { z as l } from "zod";
11
+ import { SerialPort as u } from "serialport";
11
12
  //#region ../nova-control-node/dist/nova-control-node.js
12
- var u = 9600, d = Object.freeze({
13
+ var d = 9600, f = Object.freeze({
13
14
  s1: 90,
14
15
  s2: 90,
15
16
  s3: 110,
16
17
  s4: 90,
17
18
  s5: 95
18
- }), f = Object.freeze({
19
+ }), p = Object.freeze({
19
20
  s1: [45, 135],
20
21
  s2: [10, 170],
21
22
  s3: [40, 150],
22
23
  s4: [30, 180],
23
24
  s5: [20, 150]
24
- }), p = Object.freeze({
25
- s1: (f.s1[1] - f.s1[0]) / 1e3,
26
- s2: (f.s2[1] - f.s2[0]) / 1e3,
27
- s3: (f.s3[1] - f.s3[0]) / 1e3,
28
- s4: (f.s4[1] - f.s4[0]) / 1e3,
29
- s5: (f.s5[1] - f.s5[0]) / 1e3
25
+ }), m = Object.freeze({
26
+ s1: (p.s1[1] - p.s1[0]) / 1e3,
27
+ s2: (p.s2[1] - p.s2[0]) / 1e3,
28
+ s3: (p.s3[1] - p.s3[0]) / 1e3,
29
+ s4: (p.s4[1] - p.s4[0]) / 1e3,
30
+ s5: (p.s5[1] - p.s5[0]) / 1e3
30
31
  });
31
- function m(e, t) {
32
- let [n, r] = f[t];
32
+ function h(e, t) {
33
+ let [n, r] = p[t];
33
34
  return Math.max(n, Math.min(r, Math.round(e)));
34
35
  }
35
- function h(e) {
36
+ function g(e) {
36
37
  return new Uint8Array([
37
- m(e.s4, "s4"),
38
- m(e.s3, "s3"),
39
- m(e.s2, "s2"),
40
- m(e.s1, "s1"),
41
- m(e.s5, "s5")
38
+ h(e.s4, "s4"),
39
+ h(e.s3, "s3"),
40
+ h(e.s2, "s2"),
41
+ h(e.s1, "s1"),
42
+ h(e.s5, "s5")
42
43
  ]);
43
44
  }
44
- async function g(e, t) {
45
- let n = new l({
45
+ async function _(e, t) {
46
+ let n = new u({
46
47
  path: e,
47
48
  baudRate: t,
48
49
  autoOpen: !1
@@ -68,22 +69,22 @@ async function g(e, t) {
68
69
  }
69
70
  };
70
71
  }
71
- function _(e, t) {
72
+ function v(e, t) {
72
73
  let n = Math.min(.499, Math.max(0, t)), r = 1 / (1 - n);
73
74
  if (e <= n) return r * e * e / (2 * n);
74
75
  if (e <= 1 - n) return r * (e - n / 2);
75
76
  let i = 1 - e;
76
77
  return 1 - r * i * i / (2 * n);
77
78
  }
78
- async function v(e, t = u, n) {
79
- let r = n?.StepIntervalMs ?? 20, i = n?.RampRatio ?? .25, a = await g(e, t), o = { ...d }, s, c = Promise.resolve();
79
+ async function y(e, t = d, n) {
80
+ let r = n?.StepIntervalMs ?? 20, i = n?.RampRatio ?? .25, a = await _(e, t), o = { ...f }, s, c = Promise.resolve();
80
81
  function l(e) {
81
82
  s = {
82
83
  ...s ?? o,
83
84
  ...e
84
85
  };
85
86
  }
86
- async function f() {
87
+ async function u() {
87
88
  let e = c;
88
89
  c = (async () => {
89
90
  try {
@@ -98,14 +99,14 @@ async function v(e, t = u, n) {
98
99
  "s4",
99
100
  "s5"
100
101
  ]) {
101
- let a = e[i] - o[i], s = r > 0 ? p[i] * r : Infinity;
102
+ let a = e[i] - o[i], s = r > 0 ? m[i] * r : Infinity;
102
103
  Math.abs(a) > s ? (n[i] = o[i] + Math.sign(a) * s, t = !1) : n[i] = e[i];
103
104
  }
104
- t && (s = void 0), o = { ...n }, await a.write(h(n)), t || await new Promise((e) => setTimeout(e, r));
105
+ t && (s = void 0), o = { ...n }, await a.write(g(n)), t || await new Promise((e) => setTimeout(e, r));
105
106
  }
106
107
  })(), await c;
107
108
  }
108
- async function m(e, t) {
109
+ async function p(e, t) {
109
110
  let n = c;
110
111
  c = (async () => {
111
112
  try {
@@ -114,33 +115,33 @@ async function v(e, t = u, n) {
114
115
  let c = { ...o }, l = r > 0 ? Math.max(1, Math.round(t / r)) : 1;
115
116
  s = void 0;
116
117
  for (let t = 1; t <= l; t++) {
117
- let n = _(t / l, i), s = { ...o };
118
+ let n = v(t / l, i), s = { ...o };
118
119
  for (let t of Object.keys(e)) s[t] = Math.round(c[t] + (e[t] - c[t]) * n);
119
- o = s, await a.write(h(s)), t < l && await new Promise((e) => setTimeout(e, r));
120
+ o = s, await a.write(g(s)), t < l && await new Promise((e) => setTimeout(e, r));
120
121
  }
121
122
  })(), await c;
122
123
  }
123
124
  return {
124
125
  async home(e) {
125
- e != null && e > 0 ? await m({ ...d }, e) : (l({ ...d }), await f());
126
+ e != null && e > 0 ? await p({ ...f }, e) : (l({ ...f }), await u());
126
127
  },
127
128
  async shiftHeadTo(e, t) {
128
- t != null && t > 0 ? await m({ s1: e }, t) : (l({ s1: e }), await f());
129
+ t != null && t > 0 ? await p({ s1: e }, t) : (l({ s1: e }), await u());
129
130
  },
130
131
  async rollHeadTo(e, t) {
131
- t != null && t > 0 ? await m({ s2: e }, t) : (l({ s2: e }), await f());
132
+ t != null && t > 0 ? await p({ s2: e }, t) : (l({ s2: e }), await u());
132
133
  },
133
134
  async pitchHeadTo(e, t) {
134
- t != null && t > 0 ? await m({ s3: e }, t) : (l({ s3: e }), await f());
135
+ t != null && t > 0 ? await p({ s3: e }, t) : (l({ s3: e }), await u());
135
136
  },
136
137
  async liftHeadTo(e, t) {
137
- t != null && t > 0 ? await m({ s5: e }, t) : (l({ s5: e }), await f());
138
+ t != null && t > 0 ? await p({ s5: e }, t) : (l({ s5: e }), await u());
138
139
  },
139
140
  async rotateBodyTo(e, t) {
140
- t != null && t > 0 ? await m({ s4: e }, t) : (l({ s4: e }), await f());
141
+ t != null && t > 0 ? await p({ s4: e }, t) : (l({ s4: e }), await u());
141
142
  },
142
143
  async moveTo(e, t) {
143
- t != null && t > 0 ? await m(e, t) : (l(e), await f());
144
+ t != null && t > 0 ? await p(e, t) : (l(e), await u());
144
145
  },
145
146
  get State() {
146
147
  return structuredClone(s ?? o);
@@ -152,14 +153,14 @@ async function v(e, t = u, n) {
152
153
  };
153
154
  },
154
155
  async sendServoState() {
155
- await f();
156
+ await u();
156
157
  },
157
158
  destroy() {
158
159
  a.destroy();
159
160
  }
160
161
  };
161
162
  }
162
- async function y(e, t) {
163
+ async function b(e, t) {
163
164
  let n = t.split("\n");
164
165
  for (let t = 0; t < n.length; t++) {
165
166
  let r = n[t].trim(), i = t + 1;
@@ -257,7 +258,7 @@ async function y(e, t) {
257
258
  }
258
259
  //#endregion
259
260
  //#region src/nova-control-mcp-server.ts
260
- function b() {
261
+ function x() {
261
262
  try {
262
263
  let { values: e } = r({
263
264
  args: process.argv.slice(2),
@@ -294,20 +295,20 @@ function b() {
294
295
  process.stderr.write(`nova-control-mcp: ${e.message ?? e}\n`), process.exit(1);
295
296
  }
296
297
  }
297
- var x = "", S = 9600, C;
298
- async function w() {
299
- return C ??= await v(x, S), C;
298
+ var S = "", C = 9600, w;
299
+ async function T() {
300
+ return w ??= await y(S, C), w;
300
301
  }
301
- function T() {
302
- C != null && (C.destroy(), C = void 0);
302
+ function E() {
303
+ w != null && (w.destroy(), w = void 0);
303
304
  }
304
- function E(e, t = 9600) {
305
- x = e, S = t;
305
+ function D(e, t = 9600) {
306
+ S = e, C = t;
306
307
  }
307
- function D() {
308
- T(), x = "", S = 9600;
308
+ function O() {
309
+ E(), S = "", C = 9600;
309
310
  }
310
- var O = [
311
+ var k = [
311
312
  {
312
313
  name: "home",
313
314
  description: "send all servos to their home positions — pass within_ms for smooth, fluid motion with automatic velocity ramp-up and ramp-down; without within_ms the robot moves at constant maximum speed",
@@ -498,7 +499,7 @@ var O = [
498
499
  },
499
500
  {
500
501
  name: "run_script",
501
- description: "execute a multi-line movement script — one command per line; blank lines and lines starting with # are ignored; commands: home | shift-to <angle> | roll-to <angle> | pitch-to <angle> | rotate-to <angle> | lift-to <angle> | move [shift-to <angle>] [roll-to <angle>] [pitch-to <angle>] [rotate-to <angle>] [lift-to <angle>] | wait <ms>",
502
+ description: "execute a multi-line movement script — one command per line; blank lines and lines starting with # are ignored; each command is fully awaited before the next begins; commands: home [<within_ms>] | shift-to <angle> [<within_ms>] | roll-to <angle> [<within_ms>] | pitch-to <angle> [<within_ms>] | rotate-to <angle> [<within_ms>] | lift-to <angle> [<within_ms>] | move [shift-to <angle>] [roll-to <angle>] [pitch-to <angle>] [rotate-to <angle>] [lift-to <angle>] [within-ms <ms>] | wait <ms>",
502
503
  inputSchema: {
503
504
  type: "object",
504
505
  properties: { script: {
@@ -516,105 +517,134 @@ var O = [
516
517
  properties: {}
517
518
  }
518
519
  }
519
- ];
520
- async function k(e) {
521
- let t = e.within_ms == null ? void 0 : Number(e.within_ms);
522
- return await (await w()).home(t), "all servos moved to home positions";
520
+ ], A = l.number().finite(), j = l.number().positive(), M = l.object({ within_ms: j.optional() }), N = l.object({
521
+ angle: A,
522
+ within_ms: j.optional()
523
+ }), P = l.object({
524
+ shift_to: A.optional(),
525
+ roll_to: A.optional(),
526
+ pitch_to: A.optional(),
527
+ rotate_to: A.optional(),
528
+ lift_to: A.optional(),
529
+ within_ms: j.optional()
530
+ }).superRefine((e, t) => {
531
+ e.shift_to == null && e.roll_to == null && e.pitch_to == null && e.rotate_to == null && e.lift_to == null && t.addIssue({
532
+ code: l.ZodIssueCode.custom,
533
+ message: "move: at least one of shift_to, roll_to, pitch_to, rotate_to, lift_to is required"
534
+ });
535
+ }), F = l.object({
536
+ within_ms: j,
537
+ s1: A.optional(),
538
+ s2: A.optional(),
539
+ s3: A.optional(),
540
+ s4: A.optional(),
541
+ s5: A.optional()
542
+ }).superRefine((e, t) => {
543
+ e.s1 == null && e.s2 == null && e.s3 == null && e.s4 == null && e.s5 == null && t.addIssue({
544
+ code: l.ZodIssueCode.custom,
545
+ message: "move_to: at least one servo target (s1–s5) must be specified"
546
+ });
547
+ }), I = l.object({ ms: l.number().finite().nonnegative() }), L = l.object({ script: l.string() });
548
+ function R(e) {
549
+ return e.issues.map((e) => `${e.path.length > 0 ? `${e.path.join(".")}: ` : ""}${e.message}`).join("; ");
523
550
  }
524
- async function A(e) {
525
- let t = {};
526
- if (e.shift_to != null && (t.s1 = Number(e.shift_to)), e.roll_to != null && (t.s2 = Number(e.roll_to)), e.pitch_to != null && (t.s3 = Number(e.pitch_to)), e.rotate_to != null && (t.s4 = Number(e.rotate_to)), e.lift_to != null && (t.s5 = Number(e.lift_to)), Object.keys(t).length === 0) throw Error("move: at least one of shift_to, roll_to, pitch_to, rotate_to, lift_to is required");
527
- let n = e.within_ms == null ? void 0 : Number(e.within_ms);
528
- return await (await w()).moveTo(t, n), `servos updated: ${JSON.stringify(t)}`;
551
+ async function z(e) {
552
+ let { within_ms: t } = M.parse(e);
553
+ return await (await T()).home(t), "all servos moved to home positions";
529
554
  }
530
- async function j(e) {
531
- let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
532
- return await (await w()).shiftHeadTo(t, n), `s1 (shift) ${t}°`;
555
+ async function B(e) {
556
+ let t = P.parse(e), n = {};
557
+ return t.shift_to != null && (n.s1 = t.shift_to), t.roll_to != null && (n.s2 = t.roll_to), t.pitch_to != null && (n.s3 = t.pitch_to), t.rotate_to != null && (n.s4 = t.rotate_to), t.lift_to != null && (n.s5 = t.lift_to), await (await T()).moveTo(n, t.within_ms), `servos updated: ${JSON.stringify(n)}`;
533
558
  }
534
- async function M(e) {
535
- let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
536
- return await (await w()).rollHeadTo(t, n), `s2 (roll) → ${t}°`;
559
+ async function V(e) {
560
+ let { angle: t, within_ms: n } = N.parse(e);
561
+ return await (await T()).shiftHeadTo(t, n), `s1 (shift) → ${t}°`;
537
562
  }
538
- async function N(e) {
539
- let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
540
- return await (await w()).pitchHeadTo(t, n), `s3 (pitch) → ${t}°`;
563
+ async function H(e) {
564
+ let { angle: t, within_ms: n } = N.parse(e);
565
+ return await (await T()).rollHeadTo(t, n), `s2 (roll) → ${t}°`;
541
566
  }
542
- async function P(e) {
543
- let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
544
- return await (await w()).rotateBodyTo(t, n), `s4 (rotate) → ${t}°`;
567
+ async function U(e) {
568
+ let { angle: t, within_ms: n } = N.parse(e);
569
+ return await (await T()).pitchHeadTo(t, n), `s3 (pitch) → ${t}°`;
545
570
  }
546
- async function F(e) {
547
- let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
548
- return await (await w()).liftHeadTo(t, n), `s5 (lift) → ${t}°`;
571
+ async function W(e) {
572
+ let { angle: t, within_ms: n } = N.parse(e);
573
+ return await (await T()).rotateBodyTo(t, n), `s4 (rotate) → ${t}°`;
549
574
  }
550
- async function I() {
551
- return C == null ? "not connected" : (T(), "disconnected");
575
+ async function G(e) {
576
+ let { angle: t, within_ms: n } = N.parse(e);
577
+ return await (await T()).liftHeadTo(t, n), `s5 (lift) → ${t}°`;
552
578
  }
553
- async function L(e) {
554
- let t = Number(e.within_ms);
555
- if (isNaN(t) || t <= 0) throw Error("move_to: within_ms must be a positive number");
556
- let n = {};
557
- if (e.s1 != null && (n.s1 = Number(e.s1)), e.s2 != null && (n.s2 = Number(e.s2)), e.s3 != null && (n.s3 = Number(e.s3)), e.s4 != null && (n.s4 = Number(e.s4)), e.s5 != null && (n.s5 = Number(e.s5)), Object.keys(n).length === 0) throw Error("move_to: at least one servo target (s1–s5) must be specified");
558
- return await (await w()).moveTo(n, t), "move completed";
579
+ async function K() {
580
+ return w == null ? "not connected" : (E(), "disconnected");
559
581
  }
560
- async function R(e) {
561
- let t = Number(e.ms);
562
- if (isNaN(t) || t < 0) throw Error(`wait: invalid duration '${e.ms}' expected a non-negative number`);
563
- return await new Promise((e) => setTimeout(e, t)), `waited ${t} ms`;
582
+ async function q(e) {
583
+ let t = F.parse(e), n = {};
584
+ return t.s1 != null && (n.s1 = t.s1), t.s2 != null && (n.s2 = t.s2), t.s3 != null && (n.s3 = t.s3), t.s4 != null && (n.s4 = t.s4), t.s5 != null && (n.s5 = t.s5), await (await T()).moveTo(n, t.within_ms), "move completed";
564
585
  }
565
- async function z() {
566
- let e = await w();
586
+ async function J(e) {
587
+ let t;
588
+ try {
589
+ t = I.parse(e);
590
+ } catch {
591
+ throw Error(`wait: invalid duration '${e.ms}' — expected a non-negative number`);
592
+ }
593
+ return await new Promise((e) => setTimeout(e, t.ms)), `waited ${t.ms} ms`;
594
+ }
595
+ async function Y() {
596
+ let e = await T();
567
597
  return JSON.stringify(e.State);
568
598
  }
569
- async function B(e) {
570
- let t = String(e.script ?? "");
571
- return await y(await w(), t), "script executed successfully";
599
+ async function X(e) {
600
+ let { script: t } = L.parse(e);
601
+ return await b(await T(), t), "script executed successfully";
572
602
  }
573
- function V() {
603
+ function Z() {
574
604
  let e = new i({
575
605
  name: "nova-control-mcp-server",
576
606
  version: "0.0.8"
577
607
  }, { capabilities: { tools: {} } });
578
- return e.setRequestHandler(c, async () => ({ tools: O })), e.setRequestHandler(s, async (e) => {
608
+ return e.setRequestHandler(c, async () => ({ tools: k })), e.setRequestHandler(s, async (e) => {
579
609
  let t = e.params.name, n = e.params.arguments ?? {};
580
610
  try {
581
611
  let e;
582
612
  switch (t) {
583
613
  case "home":
584
- e = await k(n);
614
+ e = await z(n);
585
615
  break;
586
616
  case "move":
587
- e = await A(n);
617
+ e = await B(n);
588
618
  break;
589
619
  case "shift_to":
590
- e = await j(n);
620
+ e = await V(n);
591
621
  break;
592
622
  case "roll_to":
593
- e = await M(n);
623
+ e = await H(n);
594
624
  break;
595
625
  case "pitch_to":
596
- e = await N(n);
626
+ e = await U(n);
597
627
  break;
598
628
  case "rotate_to":
599
- e = await P(n);
629
+ e = await W(n);
600
630
  break;
601
631
  case "lift_to":
602
- e = await F(n);
632
+ e = await G(n);
603
633
  break;
604
634
  case "move_to":
605
- e = await L(n);
635
+ e = await q(n);
606
636
  break;
607
637
  case "wait":
608
- e = await R(n);
638
+ e = await J(n);
609
639
  break;
610
640
  case "get_state":
611
- e = await z();
641
+ e = await Y();
612
642
  break;
613
643
  case "run_script":
614
- e = await B(n);
644
+ e = await X(n);
615
645
  break;
616
646
  case "disconnect":
617
- e = await I();
647
+ e = await K();
618
648
  break;
619
649
  default: return {
620
650
  content: [{
@@ -632,21 +662,21 @@ function V() {
632
662
  return {
633
663
  content: [{
634
664
  type: "text",
635
- text: e instanceof Error ? e.message : String(e)
665
+ text: e instanceof l.ZodError ? R(e) : e instanceof Error ? e.message : String(e)
636
666
  }],
637
667
  isError: !0
638
668
  };
639
669
  }
640
670
  }), e;
641
671
  }
642
- async function H(e) {
672
+ async function Q(e) {
643
673
  let t = new a();
644
674
  await e.connect(t);
645
675
  for (let e of ["SIGINT", "SIGTERM"]) process.on(e, () => {
646
- T(), process.exit(0);
676
+ E(), process.exit(0);
647
677
  });
648
678
  }
649
- async function U(e, t) {
679
+ async function $(e, t) {
650
680
  let r = new o({ sessionIdGenerator: void 0 });
651
681
  await e.connect(r);
652
682
  let i = n(async (e, t) => {
@@ -658,17 +688,17 @@ async function U(e, t) {
658
688
  }), i.once("error", n);
659
689
  });
660
690
  for (let e of ["SIGINT", "SIGTERM"]) process.on(e, async () => {
661
- await r.close(), i.close(), T(), process.exit(0);
691
+ await r.close(), i.close(), E(), process.exit(0);
662
692
  });
663
693
  }
664
- async function W() {
665
- let { Port: e, BaudRate: t, Transport: n, ListenPort: r } = b();
666
- x = e, S = t;
667
- let i = V();
668
- n === "http" ? await U(i, r) : await H(i);
694
+ async function ee() {
695
+ let { Port: e, BaudRate: t, Transport: n, ListenPort: r } = x();
696
+ S = e, C = t;
697
+ let i = Z();
698
+ n === "http" ? await $(i, r) : await Q(i);
669
699
  }
670
- t(process.argv[1]) === e(import.meta.url) && W().catch((e) => {
700
+ t(process.argv[1]) === e(import.meta.url) && ee().catch((e) => {
671
701
  process.stderr.write(`nova-control-mcp: fatal: ${e.message ?? e}\n`), process.exit(1);
672
702
  });
673
703
  //#endregion
674
- export { D as _destroyForTests, E as _setupForTests, V as createServer };
704
+ export { O as _destroyForTests, D as _setupForTests, Z as createServer };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nova-control-mcp-server",
3
3
  "description": "MCP server for controlling a NOVA DIY Artificial Intelligence Robot by Creoqode",
4
- "version": "0.0.8",
4
+ "version": "0.0.9",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "nova",
@@ -30,7 +30,8 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@modelcontextprotocol/sdk": "^1.0.0",
33
- "nova-control-node": "*"
33
+ "nova-control-node": "*",
34
+ "zod": "^4.3.6"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/node": "^22.0.0",