cyclops-clientdevbridge-cli 0.1.1

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.
Files changed (112) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +21 -0
  3. package/README.md +218 -0
  4. package/dist/artifacts.d.ts +56 -0
  5. package/dist/artifacts.d.ts.map +1 -0
  6. package/dist/artifacts.js +58 -0
  7. package/dist/artifacts.js.map +1 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +348 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/compare.d.ts +27 -0
  13. package/dist/commands/compare.d.ts.map +1 -0
  14. package/dist/commands/compare.js +209 -0
  15. package/dist/commands/compare.js.map +1 -0
  16. package/dist/commands/context.d.ts +25 -0
  17. package/dist/commands/context.d.ts.map +1 -0
  18. package/dist/commands/context.js +36 -0
  19. package/dist/commands/context.js.map +1 -0
  20. package/dist/commands/doctor.d.ts +31 -0
  21. package/dist/commands/doctor.d.ts.map +1 -0
  22. package/dist/commands/doctor.js +234 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/hotswap.d.ts +7 -0
  25. package/dist/commands/hotswap.d.ts.map +1 -0
  26. package/dist/commands/hotswap.js +76 -0
  27. package/dist/commands/hotswap.js.map +1 -0
  28. package/dist/commands/input.d.ts +53 -0
  29. package/dist/commands/input.d.ts.map +1 -0
  30. package/dist/commands/input.js +204 -0
  31. package/dist/commands/input.js.map +1 -0
  32. package/dist/commands/inspect.d.ts +19 -0
  33. package/dist/commands/inspect.d.ts.map +1 -0
  34. package/dist/commands/inspect.js +90 -0
  35. package/dist/commands/inspect.js.map +1 -0
  36. package/dist/commands/lifecycle.d.ts +29 -0
  37. package/dist/commands/lifecycle.d.ts.map +1 -0
  38. package/dist/commands/lifecycle.js +201 -0
  39. package/dist/commands/lifecycle.js.map +1 -0
  40. package/dist/commands/snapshot.d.ts +37 -0
  41. package/dist/commands/snapshot.d.ts.map +1 -0
  42. package/dist/commands/snapshot.js +133 -0
  43. package/dist/commands/snapshot.js.map +1 -0
  44. package/dist/commands/world.d.ts +27 -0
  45. package/dist/commands/world.d.ts.map +1 -0
  46. package/dist/commands/world.js +203 -0
  47. package/dist/commands/world.js.map +1 -0
  48. package/dist/detect.d.ts +58 -0
  49. package/dist/detect.d.ts.map +1 -0
  50. package/dist/detect.js +185 -0
  51. package/dist/detect.js.map +1 -0
  52. package/dist/errors.d.ts +24 -0
  53. package/dist/errors.d.ts.map +1 -0
  54. package/dist/errors.js +37 -0
  55. package/dist/errors.js.map +1 -0
  56. package/dist/hotswap.d.ts +58 -0
  57. package/dist/hotswap.d.ts.map +1 -0
  58. package/dist/hotswap.js +284 -0
  59. package/dist/hotswap.js.map +1 -0
  60. package/dist/index.d.ts +11 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +11 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/initscript.d.ts +49 -0
  65. package/dist/initscript.d.ts.map +1 -0
  66. package/dist/initscript.js +202 -0
  67. package/dist/initscript.js.map +1 -0
  68. package/dist/java.d.ts +47 -0
  69. package/dist/java.d.ts.map +1 -0
  70. package/dist/java.js +114 -0
  71. package/dist/java.js.map +1 -0
  72. package/dist/launcher.d.ts +104 -0
  73. package/dist/launcher.d.ts.map +1 -0
  74. package/dist/launcher.js +609 -0
  75. package/dist/launcher.js.map +1 -0
  76. package/dist/loopback.d.ts +17 -0
  77. package/dist/loopback.d.ts.map +1 -0
  78. package/dist/loopback.js +19 -0
  79. package/dist/loopback.js.map +1 -0
  80. package/dist/output.d.ts +21 -0
  81. package/dist/output.d.ts.map +1 -0
  82. package/dist/output.js +41 -0
  83. package/dist/output.js.map +1 -0
  84. package/dist/paths.d.ts +23 -0
  85. package/dist/paths.d.ts.map +1 -0
  86. package/dist/paths.js +47 -0
  87. package/dist/paths.js.map +1 -0
  88. package/dist/protocol/client.d.ts +35 -0
  89. package/dist/protocol/client.d.ts.map +1 -0
  90. package/dist/protocol/client.js +170 -0
  91. package/dist/protocol/client.js.map +1 -0
  92. package/dist/protocol/types.d.ts +114 -0
  93. package/dist/protocol/types.d.ts.map +1 -0
  94. package/dist/protocol/types.js +38 -0
  95. package/dist/protocol/types.js.map +1 -0
  96. package/dist/session.d.ts +77 -0
  97. package/dist/session.d.ts.map +1 -0
  98. package/dist/session.js +79 -0
  99. package/dist/session.js.map +1 -0
  100. package/dist/snapshot/model.d.ts +327 -0
  101. package/dist/snapshot/model.d.ts.map +1 -0
  102. package/dist/snapshot/model.js +91 -0
  103. package/dist/snapshot/model.js.map +1 -0
  104. package/dist/snapshot/outline.d.ts +35 -0
  105. package/dist/snapshot/outline.d.ts.map +1 -0
  106. package/dist/snapshot/outline.js +135 -0
  107. package/dist/snapshot/outline.js.map +1 -0
  108. package/dist/xvfb.d.ts +30 -0
  109. package/dist/xvfb.d.ts.map +1 -0
  110. package/dist/xvfb.js +66 -0
  111. package/dist/xvfb.js.map +1 -0
  112. package/package.json +66 -0
@@ -0,0 +1,135 @@
1
+ import { centre, flatten, label, simpleType } from './model.js';
2
+ /**
3
+ * Renders a snapshot as one line per widget, indented by depth.
4
+ *
5
+ * This is the format an agent actually reads, so it is optimised for scanning: the type and label
6
+ * first, then the geometry it would need to click, then only the flags that are *not* the default.
7
+ * A line looks like:
8
+ *
9
+ * ```
10
+ * Button "Apply" @(312,208 60x20) disabled /root/children[3]
11
+ * ```
12
+ *
13
+ * The trailing path is what `click --widget` consumes, so an outline line can be copied verbatim.
14
+ */
15
+ export function formatOutline(snapshot, options = {}) {
16
+ const lines = [];
17
+ if (snapshot.screenClass === null) {
18
+ lines.push('No screen is open.');
19
+ lines.push(`window ${snapshot.pixelWidth}x${snapshot.pixelHeight}px, gui ${snapshot.guiWidth}x${snapshot.guiHeight} @ scale ${snapshot.guiScale}`);
20
+ return lines.join('\n');
21
+ }
22
+ lines.push(`${simpleType({ type: snapshot.screenClass })} "${snapshot.title ?? ''}"`);
23
+ lines.push(`gui ${snapshot.guiWidth}x${snapshot.guiHeight} @ scale ${snapshot.guiScale}` +
24
+ `, window ${snapshot.pixelWidth}x${snapshot.pixelHeight}px` +
25
+ `, mouse at ${snapshot.mouse.join(',')}`);
26
+ if (snapshot.container !== null) {
27
+ const container = snapshot.container;
28
+ const filled = container.slots.filter((slot) => slot.item !== null).length;
29
+ lines.push(`container ${simpleType({ type: container.menuClass })}` +
30
+ ` at (${container.leftPos},${container.topPos}) ${container.imageWidth}x${container.imageHeight}` +
31
+ `, ${container.slots.length} slots (${filled} filled)`);
32
+ for (const slot of container.slots) {
33
+ if (slot.item === null && !slot.hovered) {
34
+ continue;
35
+ }
36
+ lines.push(` ${formatSlot(slot)}`);
37
+ }
38
+ if (container.carried.item !== null) {
39
+ lines.push(` carried: ${container.carried.item} x${container.carried.count}`);
40
+ }
41
+ }
42
+ const nodes = flatten(snapshot.root);
43
+ // The root node is the screen itself, already summarised above.
44
+ for (const { node, depth } of nodes.slice(1)) {
45
+ if (!node.visible && options.includeHidden !== true) {
46
+ continue;
47
+ }
48
+ lines.push(`${' '.repeat(depth)}${formatNode(node)}`);
49
+ }
50
+ if (nodes.length <= 1 && snapshot.container === null) {
51
+ lines.push('(this screen has no widgets)');
52
+ }
53
+ if (snapshot.truncated) {
54
+ lines.push('# truncated: the widget tree hit the depth or node limit');
55
+ }
56
+ return lines.join('\n');
57
+ }
58
+ export function formatSlot(slot) {
59
+ const item = slot.item === null ? '(empty)' : `${slot.item} x${slot.count}`;
60
+ const flags = slot.hovered ? ' hovered' : '';
61
+ return `slot ${String(slot.index).padStart(2)} ${item} @(${slot.x},${slot.y})${flags}`;
62
+ }
63
+ export function formatNode(node) {
64
+ const parts = [simpleType(node)];
65
+ const text = label(node);
66
+ if (text.length > 0) {
67
+ parts.push(`"${text}"`);
68
+ }
69
+ if (node.bounds !== null) {
70
+ parts.push(`@(${node.bounds.x},${node.bounds.y} ${node.bounds.w}x${node.bounds.h})`);
71
+ }
72
+ else {
73
+ parts.push('@(unknown)');
74
+ }
75
+ if (node.value !== null && node.value !== undefined) {
76
+ parts.push(`= ${formatValue(node.value)}`);
77
+ }
78
+ // Only the states that differ from the default are worth a reader's attention.
79
+ if (!node.active) {
80
+ parts.push('disabled');
81
+ }
82
+ if (!node.visible) {
83
+ parts.push('hidden');
84
+ }
85
+ if (node.focused) {
86
+ parts.push('focused');
87
+ }
88
+ if (node.hovered) {
89
+ parts.push('hovered');
90
+ }
91
+ const tooltip = node.extra['tooltip'];
92
+ if (Array.isArray(tooltip) && tooltip.length > 0) {
93
+ parts.push(`tooltip="${tooltip.join(' / ')}"`);
94
+ }
95
+ parts.push(` ${node.path}`);
96
+ return parts.join(' ');
97
+ }
98
+ function formatValue(value) {
99
+ if (typeof value === 'string') {
100
+ return `"${value}"`;
101
+ }
102
+ if (typeof value === 'number') {
103
+ return Number.isInteger(value) ? String(value) : value.toFixed(3);
104
+ }
105
+ return JSON.stringify(value);
106
+ }
107
+ /**
108
+ * Finds nodes by label, by path, or by type.
109
+ *
110
+ * An exact path match wins outright, then an exact label match, then a case-insensitive
111
+ * substring match — so `click --widget Apply` picks the "Apply" button even on a screen that
112
+ * also has an "Apply to all" one.
113
+ */
114
+ export function findNodes(snapshot, query, typeFilter) {
115
+ const all = flatten(snapshot.root).map(({ node }) => node);
116
+ const candidates = typeFilter === undefined
117
+ ? all
118
+ : all.filter((node) => simpleType(node).toLowerCase().includes(typeFilter.toLowerCase()));
119
+ const byPath = candidates.filter((node) => node.path === query);
120
+ if (byPath.length > 0) {
121
+ return byPath.map(toMatch);
122
+ }
123
+ const lowered = query.toLowerCase();
124
+ const exact = candidates.filter((node) => label(node).toLowerCase() === lowered);
125
+ if (exact.length > 0) {
126
+ return exact.map(toMatch);
127
+ }
128
+ return candidates
129
+ .filter((node) => label(node).toLowerCase().includes(lowered) || simpleType(node).toLowerCase() === lowered)
130
+ .map(toMatch);
131
+ }
132
+ function toMatch(node) {
133
+ return { node, centre: centre(node) };
134
+ }
135
+ //# sourceMappingURL=outline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline.js","sourceRoot":"","sources":["../../src/snapshot/outline.ts"],"names":[],"mappings":"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"}
package/dist/xvfb.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ export interface DisplayPlan {
2
+ /** The command to run, which may be `xvfb-run` wrapping the real one. */
3
+ readonly command: string;
4
+ readonly prefixArgs: readonly string[];
5
+ readonly env: Readonly<Record<string, string>>;
6
+ readonly description: string;
7
+ }
8
+ export declare function commandExists(command: string): boolean;
9
+ /**
10
+ * Decides how to give the client a display.
11
+ *
12
+ * "Headless" here means a real client on a virtual display, not a client without rendering:
13
+ * on Linux without `$DISPLAY` that is Xvfb plus Mesa's llvmpipe software rasteriser. Everywhere
14
+ * else, and with `--headed`, the client just opens a window. Nothing else about the CLI's
15
+ * behaviour differs between the two.
16
+ */
17
+ export declare function planDisplay(command: string, options: {
18
+ headed: boolean;
19
+ width: number;
20
+ height: number;
21
+ }): DisplayPlan;
22
+ /**
23
+ * The pids of every Xvfb server currently running.
24
+ *
25
+ * `xvfb-run` picks a free display itself and never says which, so the only way to know what it
26
+ * started is to look before and after. Anything that appears in between is what this launch is
27
+ * responsible for cleaning up.
28
+ */
29
+ export declare function xvfbPids(): number[];
30
+ //# sourceMappingURL=xvfb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xvfb.d.ts","sourceRoot":"","sources":["../src/xvfb.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1D,WAAW,CA+Bb;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,IAAI,MAAM,EAAE,CAUnC"}
package/dist/xvfb.js ADDED
@@ -0,0 +1,66 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ export function commandExists(command) {
3
+ try {
4
+ execFileSync(process.platform === 'win32' ? 'where' : 'which', [command], { stdio: 'ignore' });
5
+ return true;
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ /**
12
+ * Decides how to give the client a display.
13
+ *
14
+ * "Headless" here means a real client on a virtual display, not a client without rendering:
15
+ * on Linux without `$DISPLAY` that is Xvfb plus Mesa's llvmpipe software rasteriser. Everywhere
16
+ * else, and with `--headed`, the client just opens a window. Nothing else about the CLI's
17
+ * behaviour differs between the two.
18
+ */
19
+ export function planDisplay(command, options) {
20
+ const hasDisplay = (process.env['DISPLAY'] ?? '').length > 0;
21
+ if (options.headed || hasDisplay || process.platform !== 'linux') {
22
+ return {
23
+ command,
24
+ prefixArgs: [],
25
+ env: {},
26
+ description: options.headed
27
+ ? 'headed (--headed)'
28
+ : hasDisplay
29
+ ? `existing display ${process.env['DISPLAY']}`
30
+ : `native window on ${process.platform}`,
31
+ };
32
+ }
33
+ if (!commandExists('xvfb-run')) {
34
+ throw new Error('No $DISPLAY is set and xvfb-run is not installed, so the client has nowhere to render.\n' +
35
+ 'Install it with: sudo apt-get install -y xvfb libgl1-mesa-dri\n' +
36
+ 'Or pass --headed if you do have a display available.');
37
+ }
38
+ return {
39
+ command: 'xvfb-run',
40
+ // A generous virtual screen: the client window is smaller, but resizing beyond the
41
+ // screen (window.resize) would otherwise silently clamp.
42
+ prefixArgs: ['-a', '-s', `-screen 0 ${Math.max(options.width, 1280)}x${Math.max(options.height, 800)}x24`],
43
+ env: { LIBGL_ALWAYS_SOFTWARE: '1' },
44
+ description: 'xvfb-run with Mesa llvmpipe',
45
+ };
46
+ }
47
+ /**
48
+ * The pids of every Xvfb server currently running.
49
+ *
50
+ * `xvfb-run` picks a free display itself and never says which, so the only way to know what it
51
+ * started is to look before and after. Anything that appears in between is what this launch is
52
+ * responsible for cleaning up.
53
+ */
54
+ export function xvfbPids() {
55
+ try {
56
+ return execFileSync('pgrep', ['-x', 'Xvfb'], { encoding: 'utf8' })
57
+ .split('\n')
58
+ .map((line) => Number(line.trim()))
59
+ .filter((pid) => Number.isInteger(pid) && pid > 0);
60
+ }
61
+ catch {
62
+ // pgrep exits non-zero when nothing matches, and is absent on platforms that never use Xvfb.
63
+ return [];
64
+ }
65
+ }
66
+ //# sourceMappingURL=xvfb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xvfb.js","sourceRoot":"","sources":["../src/xvfb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAUlD,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC/F,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,OAA2D;IAE3D,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,MAAM,IAAI,UAAU,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjE,OAAO;YACL,OAAO;YACP,UAAU,EAAE,EAAE;YACd,GAAG,EAAE,EAAE;YACP,WAAW,EAAE,OAAO,CAAC,MAAM;gBACzB,CAAC,CAAC,mBAAmB;gBACrB,CAAC,CAAC,UAAU;oBACV,CAAC,CAAC,oBAAoB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;oBAC9C,CAAC,CAAC,oBAAoB,OAAO,CAAC,QAAQ,EAAE;SAC7C,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,0FAA0F;YACxF,iEAAiE;YACjE,sDAAsD,CACzD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,mFAAmF;QACnF,yDAAyD;QACzD,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC;QAC1G,GAAG,EAAE,EAAE,qBAAqB,EAAE,GAAG,EAAE;QACnC,WAAW,EAAE,6BAA6B;KAC3C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ;IACtB,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;aAC/D,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;aAClC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,6FAA6F;QAC7F,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "cyclops-clientdevbridge-cli",
3
+ "version": "0.1.1",
4
+ "description": "CLI that launches and drives a Minecraft dev client through the ClientDevBridge mod, for coding agents.",
5
+ "keywords": [
6
+ "minecraft",
7
+ "modding",
8
+ "cyclopsmc",
9
+ "agent",
10
+ "claude-code",
11
+ "testing"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "rubensworks (aka kroeser)",
15
+ "homepage": "https://github.com/CyclopsMC/clientdevbridge-cli",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/CyclopsMC/clientdevbridge-cli.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/CyclopsMC/clientdevbridge-cli/issues"
22
+ },
23
+ "type": "module",
24
+ "bin": {
25
+ "clientdevbridge": "dist/cli.js"
26
+ },
27
+ "main": "dist/index.js",
28
+ "types": "dist/index.d.ts",
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "CHANGELOG.md",
33
+ "LICENSE"
34
+ ],
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "prepublishOnly": "npm run build",
41
+ "version": "manual-git-changelog onversion",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
44
+ "typecheck": "tsc -p tsconfig.json --noEmit",
45
+ "lint": "eslint src test --ext .ts",
46
+ "lint:fix": "eslint src test --ext .ts --fix"
47
+ },
48
+ "dependencies": {
49
+ "commander": "^12.1.0",
50
+ "pixelmatch": "^6.0.0",
51
+ "pngjs": "^7.0.0",
52
+ "ws": "^8.18.0",
53
+ "zod": "^3.23.8"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^20.14.0",
57
+ "@types/pngjs": "^6.0.5",
58
+ "@types/ws": "^8.5.12",
59
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
60
+ "@typescript-eslint/parser": "^7.18.0",
61
+ "eslint": "^8.57.0",
62
+ "manual-git-changelog": "^1.0.2",
63
+ "typescript": "^5.5.4",
64
+ "vitest": "^2.0.5"
65
+ }
66
+ }