agentic-numerical-control 0.1.8 → 0.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/package.json CHANGED
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "name": "agentic-numerical-control",
3
- "version": "0.1.8",
4
- "description": "Agentic Numerical Control: see and run CNC jobs from your AI assistant. A 3D toolpath viewer, live machine status and job control for CNCjs/GRBL machines.",
3
+ "version": "0.2.0",
4
+ "description": "Agentic Numerical Control: see and run CNC jobs from your AI assistant. A 3D toolpath viewer, live machine status and job control for GRBL machines, with a built-in CNCjs engine.",
5
5
  "scripts": {
6
- "build": "tsc --noEmit -p tsconfig.app.json && cross-env INPUT=viewer.html vite build && cross-env INPUT=jobs.html vite build && cross-env INPUT=machine.html vite build && cross-env INPUT=setup.html vite build && tsc -p tsconfig.build.json",
6
+ "build": "node engines/cncjs/build.mjs && node engines/kiri/build.mjs && tsc --noEmit -p tsconfig.app.json && cross-env INPUT=viewer.html vite build && cross-env INPUT=jobs.html vite build && cross-env INPUT=machine.html vite build && cross-env INPUT=setup.html vite build && tsc -p tsconfig.build.json",
7
7
  "dev": "npm run build && tsx server/cli.ts start",
8
8
  "dev:serve": "npm run build && tsx server/cli.ts serve",
9
9
  "dev:stdio": "npm run build 1>&2 && tsx server/cli.ts stdio",
10
10
  "sim:build": "node sim/build.mjs",
11
11
  "sim:e2e": "node sim/e2e.mjs",
12
12
  "prepack": "npm run build",
13
- "demo:site": "tsx scripts/build-demo.ts && tsx scripts/build-catalogue.ts"
13
+ "demo:site": "tsx scripts/build-demo.ts && tsx scripts/build-catalogue.ts",
14
+ "engines:check": "node engines/update.mjs",
15
+ "cam:check": "node engines/kiri/check.mjs"
14
16
  },
15
17
  "license": "MIT",
16
18
  "dependencies": {
@@ -18,11 +20,12 @@
18
20
  "@modelcontextprotocol/ext-apps": "^2.0.3",
19
21
  "@modelcontextprotocol/node": "^2.0.0",
20
22
  "@modelcontextprotocol/server": "^2.0.0",
21
- "cncjs": "^1.11.5",
23
+ "@serialport/parser-readline": "10.5.0",
22
24
  "cors": "^2.8.6",
23
25
  "express": "^5.3.0",
24
26
  "gcode-toolpath": "^3.0.0",
25
27
  "jsonwebtoken": "^9.0.3",
28
+ "serialport": "10.5.0",
26
29
  "socket.io-client": "^2.2.0",
27
30
  "three": "^0.186.1",
28
31
  "ws": "^8.22.0",
@@ -36,6 +39,7 @@
36
39
  "@types/three": "^0.186.0",
37
40
  "@types/ws": "^8.18.2",
38
41
  "cross-env": "^10.1.0",
42
+ "esbuild": "0.25.10",
39
43
  "tsx": "^4.23.15",
40
44
  "typescript": "^5.9.3",
41
45
  "vite": "^6.4.4",
package/product.json CHANGED
@@ -1,8 +1,8 @@
1
- {
2
- "name": "agentic-numerical-control",
3
- "displayName": "Agentic Numerical Control",
4
- "shortName": "anc",
5
- "dirName": "AgenticNumericalControl",
6
- "cloudUrl": "https://api.agenticnumericalcontrol.com",
7
- "siteUrl": "https://agenticnumericalcontrol.com"
8
- }
1
+ {
2
+ "name": "agentic-numerical-control",
3
+ "displayName": "Agentic Numerical Control",
4
+ "shortName": "anc",
5
+ "dirName": "AgenticNumericalControl",
6
+ "cloudUrl": "https://api.agenticnumericalcontrol.com",
7
+ "siteUrl": "https://agenticnumericalcontrol.com"
8
+ }
@@ -1,130 +1,130 @@
1
- (Agentic Numerical Control demo: pocket, profile, drill)
2
- G21 G90 G17 G54
3
- M3 S12000
4
- G0 Z5
5
- G0 X-20.000 Y-12.000
6
- G1 Z-1.500 F300
7
- G1 Y-12.000 F1200
8
- G1 X20.000
9
- G1 Y-9.000 F1200
10
- G1 X-20.000
11
- G1 Y-6.000 F1200
12
- G1 X20.000
13
- G1 Y-3.000 F1200
14
- G1 X-20.000
15
- G1 Y0.000 F1200
16
- G1 X20.000
17
- G1 Y3.000 F1200
18
- G1 X-20.000
19
- G1 Y6.000 F1200
20
- G1 X20.000
21
- G1 Y9.000 F1200
22
- G1 X-20.000
23
- G1 Y12.000 F1200
24
- G1 X20.000
25
- G0 Z5
26
- G0 X-20.000 Y-12.000
27
- G1 Z-3.000 F300
28
- G1 Y-12.000 F1200
29
- G1 X20.000
30
- G1 Y-9.000 F1200
31
- G1 X-20.000
32
- G1 Y-6.000 F1200
33
- G1 X20.000
34
- G1 Y-3.000 F1200
35
- G1 X-20.000
36
- G1 Y0.000 F1200
37
- G1 X20.000
38
- G1 Y3.000 F1200
39
- G1 X-20.000
40
- G1 Y6.000 F1200
41
- G1 X20.000
42
- G1 Y9.000 F1200
43
- G1 X-20.000
44
- G1 Y12.000 F1200
45
- G1 X20.000
46
- G0 Z5
47
- G0 X-29.000 Y-25.000
48
- G1 Z-2.000 F300
49
- G1 X29.000 F1000
50
- G3 X35.000 Y-19.000 I0 J6.000
51
- G1 Y19.000
52
- G3 X29.000 Y25.000 I-6.000 J0
53
- G1 X-29.000
54
- G3 X-35.000 Y19.000 I0 J-6.000
55
- G1 Y-19.000
56
- G3 X-29.000 Y-25.000 I6.000 J0
57
- G0 Z5
58
- G0 X-29.000 Y-25.000
59
- G1 Z-4.000 F300
60
- G1 X29.000 F1000
61
- G3 X35.000 Y-19.000 I0 J6.000
62
- G1 Y19.000
63
- G3 X29.000 Y25.000 I-6.000 J0
64
- G1 X-29.000
65
- G3 X-35.000 Y19.000 I0 J-6.000
66
- G1 Y-19.000
67
- G3 X-29.000 Y-25.000 I6.000 J0
68
- G0 Z5
69
- G0 X-29.000 Y-25.000
70
- G1 Z-6.000 F300
71
- G1 X29.000 F1000
72
- G3 X35.000 Y-19.000 I0 J6.000
73
- G1 Y19.000
74
- G3 X29.000 Y25.000 I-6.000 J0
75
- G1 X-29.000
76
- G3 X-35.000 Y19.000 I0 J-6.000
77
- G1 Y-19.000
78
- G3 X-29.000 Y-25.000 I6.000 J0
79
- G0 Z5
80
- G0 X-28.000 Y-18.000
81
- G1 Z-2.000 F200
82
- G0 Z1
83
- G1 Z-4.000 F200
84
- G0 Z1
85
- G1 Z-6.000 F200
86
- G0 Z1
87
- G1 Z-8.000 F200
88
- G0 Z1
89
- G0 Z5
90
- G0 X28.000 Y-18.000
91
- G1 Z-2.000 F200
92
- G0 Z1
93
- G1 Z-4.000 F200
94
- G0 Z1
95
- G1 Z-6.000 F200
96
- G0 Z1
97
- G1 Z-8.000 F200
98
- G0 Z1
99
- G0 Z5
100
- G0 X28.000 Y18.000
101
- G1 Z-2.000 F200
102
- G0 Z1
103
- G1 Z-4.000 F200
104
- G0 Z1
105
- G1 Z-6.000 F200
106
- G0 Z1
107
- G1 Z-8.000 F200
108
- G0 Z1
109
- G0 Z5
110
- G0 X-28.000 Y18.000
111
- G1 Z-2.000 F200
112
- G0 Z1
113
- G1 Z-4.000 F200
114
- G0 Z1
115
- G1 Z-6.000 F200
116
- G0 Z1
117
- G1 Z-8.000 F200
118
- G0 Z1
119
- G0 Z5
120
- G0 X4 Y0
121
- G1 Z0 F300
122
- G2 X4 Y0 Z-1.500 I-4 J0 F600
123
- G2 X4 Y0 Z-3.000 I-4 J0 F600
124
- G2 X4 Y0 Z-4.500 I-4 J0 F600
125
- G2 X4 Y0 Z-6.000 I-4 J0 F600
126
- G2 X4 Y0 I-4 J0
127
- G0 Z5
128
- M5
129
- G0 X0 Y0
130
- M30
1
+ (Agentic Numerical Control demo: pocket, profile, drill)
2
+ G21 G90 G17 G54
3
+ M3 S12000
4
+ G0 Z5
5
+ G0 X-20.000 Y-12.000
6
+ G1 Z-1.500 F300
7
+ G1 Y-12.000 F1200
8
+ G1 X20.000
9
+ G1 Y-9.000 F1200
10
+ G1 X-20.000
11
+ G1 Y-6.000 F1200
12
+ G1 X20.000
13
+ G1 Y-3.000 F1200
14
+ G1 X-20.000
15
+ G1 Y0.000 F1200
16
+ G1 X20.000
17
+ G1 Y3.000 F1200
18
+ G1 X-20.000
19
+ G1 Y6.000 F1200
20
+ G1 X20.000
21
+ G1 Y9.000 F1200
22
+ G1 X-20.000
23
+ G1 Y12.000 F1200
24
+ G1 X20.000
25
+ G0 Z5
26
+ G0 X-20.000 Y-12.000
27
+ G1 Z-3.000 F300
28
+ G1 Y-12.000 F1200
29
+ G1 X20.000
30
+ G1 Y-9.000 F1200
31
+ G1 X-20.000
32
+ G1 Y-6.000 F1200
33
+ G1 X20.000
34
+ G1 Y-3.000 F1200
35
+ G1 X-20.000
36
+ G1 Y0.000 F1200
37
+ G1 X20.000
38
+ G1 Y3.000 F1200
39
+ G1 X-20.000
40
+ G1 Y6.000 F1200
41
+ G1 X20.000
42
+ G1 Y9.000 F1200
43
+ G1 X-20.000
44
+ G1 Y12.000 F1200
45
+ G1 X20.000
46
+ G0 Z5
47
+ G0 X-29.000 Y-25.000
48
+ G1 Z-2.000 F300
49
+ G1 X29.000 F1000
50
+ G3 X35.000 Y-19.000 I0 J6.000
51
+ G1 Y19.000
52
+ G3 X29.000 Y25.000 I-6.000 J0
53
+ G1 X-29.000
54
+ G3 X-35.000 Y19.000 I0 J-6.000
55
+ G1 Y-19.000
56
+ G3 X-29.000 Y-25.000 I6.000 J0
57
+ G0 Z5
58
+ G0 X-29.000 Y-25.000
59
+ G1 Z-4.000 F300
60
+ G1 X29.000 F1000
61
+ G3 X35.000 Y-19.000 I0 J6.000
62
+ G1 Y19.000
63
+ G3 X29.000 Y25.000 I-6.000 J0
64
+ G1 X-29.000
65
+ G3 X-35.000 Y19.000 I0 J-6.000
66
+ G1 Y-19.000
67
+ G3 X-29.000 Y-25.000 I6.000 J0
68
+ G0 Z5
69
+ G0 X-29.000 Y-25.000
70
+ G1 Z-6.000 F300
71
+ G1 X29.000 F1000
72
+ G3 X35.000 Y-19.000 I0 J6.000
73
+ G1 Y19.000
74
+ G3 X29.000 Y25.000 I-6.000 J0
75
+ G1 X-29.000
76
+ G3 X-35.000 Y19.000 I0 J-6.000
77
+ G1 Y-19.000
78
+ G3 X-29.000 Y-25.000 I6.000 J0
79
+ G0 Z5
80
+ G0 X-28.000 Y-18.000
81
+ G1 Z-2.000 F200
82
+ G0 Z1
83
+ G1 Z-4.000 F200
84
+ G0 Z1
85
+ G1 Z-6.000 F200
86
+ G0 Z1
87
+ G1 Z-8.000 F200
88
+ G0 Z1
89
+ G0 Z5
90
+ G0 X28.000 Y-18.000
91
+ G1 Z-2.000 F200
92
+ G0 Z1
93
+ G1 Z-4.000 F200
94
+ G0 Z1
95
+ G1 Z-6.000 F200
96
+ G0 Z1
97
+ G1 Z-8.000 F200
98
+ G0 Z1
99
+ G0 Z5
100
+ G0 X28.000 Y18.000
101
+ G1 Z-2.000 F200
102
+ G0 Z1
103
+ G1 Z-4.000 F200
104
+ G0 Z1
105
+ G1 Z-6.000 F200
106
+ G0 Z1
107
+ G1 Z-8.000 F200
108
+ G0 Z1
109
+ G0 Z5
110
+ G0 X-28.000 Y18.000
111
+ G1 Z-2.000 F200
112
+ G0 Z1
113
+ G1 Z-4.000 F200
114
+ G0 Z1
115
+ G1 Z-6.000 F200
116
+ G0 Z1
117
+ G1 Z-8.000 F200
118
+ G0 Z1
119
+ G0 Z5
120
+ G0 X4 Y0
121
+ G1 Z0 F300
122
+ G2 X4 Y0 Z-1.500 I-4 J0 F600
123
+ G2 X4 Y0 Z-3.000 I-4 J0 F600
124
+ G2 X4 Y0 Z-4.500 I-4 J0 F600
125
+ G2 X4 Y0 Z-6.000 I-4 J0 F600
126
+ G2 X4 Y0 I-4 J0
127
+ G0 Z5
128
+ M5
129
+ G0 X0 Y0
130
+ M30
@@ -1,134 +1,134 @@
1
- // Preloaded into CNCjs (`node --require sim/virtual-port.cjs`) to add a
2
- // "GRBL-SIM" serial port backed by the grblHAL simulator (sim/build-sim.sh) over stdio. Real serial ports keep
3
- // working unchanged. No virtual COM driver is needed.
4
-
5
- const { spawn } = require("node:child_process");
6
- const fs = require("node:fs");
7
- const path = require("node:path");
8
- const Module = require("node:module");
9
- // Resolved from the serialport package so it is the same copy CNCjs uses.
10
- const { SerialPortStream } = require(require.resolve("@serialport/stream", { paths: [path.dirname(require.resolve("serialport"))] }));
11
-
12
- const SIM_PATH = "GRBL-SIM";
13
- const SIM_BIN = path.join(__dirname, ".build", "grbl_sim");
14
- // Set GRBL_SIM_LOG to a file path to record the serial traffic (for debugging).
15
- const SIM_LOG = process.env.GRBL_SIM_LOG;
16
- const trace = (dir, data) => {
17
- if (SIM_LOG) fs.appendFileSync(SIM_LOG, `${new Date().toISOString()} ${dir} ${JSON.stringify(data.toString())}
18
- `);
19
- };
20
-
21
- /** The simulator is a Linux/macOS program; on Windows it runs inside WSL. */
22
- function simCommand() {
23
- // -n: plain Grbl replies (no comment prefix); step/block traces discarded.
24
- const args = ["-n", "-s", "/dev/null", "-b", "/dev/null"];
25
- if (process.platform !== "win32") return [SIM_BIN, args];
26
- const wslPath = SIM_BIN.replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`).replace(/\\/g, "/");
27
- return ["wsl.exe", ["--", wslPath, ...args]];
28
- }
29
-
30
- class CanceledError extends Error {
31
- constructor(message) {
32
- super(message);
33
- this.canceled = true;
34
- }
35
- }
36
-
37
- class SimPortBinding {
38
- constructor(openOptions) {
39
- this.openOptions = openOptions;
40
- this.isOpen = true;
41
- this.data = Buffer.alloc(0);
42
- this.pendingRead = null;
43
- const [cmd, args] = simCommand();
44
- // cwd: the simulator keeps its EEPROM.DAT (Grbl settings) there.
45
- this.child = spawn(cmd, args, { cwd: path.dirname(SIM_BIN), stdio: ["pipe", "pipe", "inherit"], windowsHide: true });
46
- this.child.stdout.on("data", (chunk) => {
47
- trace("<<", chunk);
48
- this.data = Buffer.concat([this.data, chunk]);
49
- const wake = this.pendingRead;
50
- this.pendingRead = null;
51
- if (wake) wake();
52
- });
53
- this.child.on("exit", (code, signal) => {
54
- trace("--", `simulator exited (${code ?? signal})`);
55
- this.isOpen = false;
56
- const wake = this.pendingRead;
57
- this.pendingRead = null;
58
- if (wake) wake(new CanceledError("simulator exited"));
59
- });
60
- }
61
-
62
- async close() {
63
- this.isOpen = false;
64
- this.child.kill();
65
- const wake = this.pendingRead;
66
- this.pendingRead = null;
67
- if (wake) wake(new CanceledError("port is closed"));
68
- }
69
-
70
- async read(buffer, offset, length) {
71
- if (!this.isOpen) throw new CanceledError("Read canceled");
72
- if (this.data.length === 0) {
73
- await new Promise((resolve, reject) => {
74
- this.pendingRead = (err) => (err ? reject(err) : resolve());
75
- });
76
- }
77
- const bytesRead = this.data.copy(buffer, offset, 0, Math.min(length, this.data.length));
78
- this.data = this.data.subarray(bytesRead);
79
- return { bytesRead, buffer };
80
- }
81
-
82
- async write(buffer) {
83
- if (!this.isOpen) throw new Error("Port is not open");
84
- trace(">>", buffer);
85
- await new Promise((resolve, reject) => this.child.stdin.write(buffer, (err) => (err ? reject(err) : resolve())));
86
- }
87
-
88
- async update(options) {
89
- this.openOptions.baudRate = options.baudRate;
90
- }
91
- async set() {}
92
- async get() {
93
- return { cts: true, dsr: true, dcd: true };
94
- }
95
- async getBaudRate() {
96
- return { baudRate: this.openOptions.baudRate };
97
- }
98
- async flush() {
99
- this.data = Buffer.alloc(0);
100
- }
101
- async drain() {}
102
- }
103
-
104
- function withSimPort(serialport) {
105
- const real = serialport.SerialPort.binding;
106
- const binding = {
107
- async list() {
108
- const ports = await real.list();
109
- if (fs.existsSync(SIM_BIN)) ports.push({ path: SIM_PATH, manufacturer: "grblHAL simulator (Agentic Numerical Control)" });
110
- return ports;
111
- },
112
- async open(options) {
113
- if (options.path === SIM_PATH) return new SimPortBinding({ ...options });
114
- return real.open(options);
115
- },
116
- };
117
- class SerialPort extends SerialPortStream {
118
- constructor(options, openCallback) {
119
- super({ binding, ...options }, openCallback);
120
- }
121
- }
122
- SerialPort.list = binding.list;
123
- SerialPort.binding = binding;
124
- return { ...serialport, SerialPort };
125
- }
126
-
127
- let patched = null;
128
- const load = Module._load;
129
- Module._load = function (request, parent, isMain) {
130
- const mod = load.call(this, request, parent, isMain);
131
- if (request !== "serialport") return mod;
132
- patched ??= withSimPort(mod);
133
- return patched;
134
- };
1
+ // Preloaded into CNCjs (`node --require sim/virtual-port.cjs`) to add a
2
+ // "GRBL-SIM" serial port backed by the grblHAL simulator (sim/build-sim.sh) over stdio. Real serial ports keep
3
+ // working unchanged. No virtual COM driver is needed.
4
+
5
+ const { spawn } = require("node:child_process");
6
+ const fs = require("node:fs");
7
+ const path = require("node:path");
8
+ const Module = require("node:module");
9
+ // Resolved from the serialport package so it is the same copy CNCjs uses.
10
+ const { SerialPortStream } = require(require.resolve("@serialport/stream", { paths: [path.dirname(require.resolve("serialport"))] }));
11
+
12
+ const SIM_PATH = "GRBL-SIM";
13
+ const SIM_BIN = path.join(__dirname, ".build", "grbl_sim");
14
+ // Set GRBL_SIM_LOG to a file path to record the serial traffic (for debugging).
15
+ const SIM_LOG = process.env.GRBL_SIM_LOG;
16
+ const trace = (dir, data) => {
17
+ if (SIM_LOG) fs.appendFileSync(SIM_LOG, `${new Date().toISOString()} ${dir} ${JSON.stringify(data.toString())}
18
+ `);
19
+ };
20
+
21
+ /** The simulator is a Linux/macOS program; on Windows it runs inside WSL. */
22
+ function simCommand() {
23
+ // -n: plain Grbl replies (no comment prefix); step/block traces discarded.
24
+ const args = ["-n", "-s", "/dev/null", "-b", "/dev/null"];
25
+ if (process.platform !== "win32") return [SIM_BIN, args];
26
+ const wslPath = SIM_BIN.replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`).replace(/\\/g, "/");
27
+ return ["wsl.exe", ["--", wslPath, ...args]];
28
+ }
29
+
30
+ class CanceledError extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.canceled = true;
34
+ }
35
+ }
36
+
37
+ class SimPortBinding {
38
+ constructor(openOptions) {
39
+ this.openOptions = openOptions;
40
+ this.isOpen = true;
41
+ this.data = Buffer.alloc(0);
42
+ this.pendingRead = null;
43
+ const [cmd, args] = simCommand();
44
+ // cwd: the simulator keeps its EEPROM.DAT (Grbl settings) there.
45
+ this.child = spawn(cmd, args, { cwd: path.dirname(SIM_BIN), stdio: ["pipe", "pipe", "inherit"], windowsHide: true });
46
+ this.child.stdout.on("data", (chunk) => {
47
+ trace("<<", chunk);
48
+ this.data = Buffer.concat([this.data, chunk]);
49
+ const wake = this.pendingRead;
50
+ this.pendingRead = null;
51
+ if (wake) wake();
52
+ });
53
+ this.child.on("exit", (code, signal) => {
54
+ trace("--", `simulator exited (${code ?? signal})`);
55
+ this.isOpen = false;
56
+ const wake = this.pendingRead;
57
+ this.pendingRead = null;
58
+ if (wake) wake(new CanceledError("simulator exited"));
59
+ });
60
+ }
61
+
62
+ async close() {
63
+ this.isOpen = false;
64
+ this.child.kill();
65
+ const wake = this.pendingRead;
66
+ this.pendingRead = null;
67
+ if (wake) wake(new CanceledError("port is closed"));
68
+ }
69
+
70
+ async read(buffer, offset, length) {
71
+ if (!this.isOpen) throw new CanceledError("Read canceled");
72
+ if (this.data.length === 0) {
73
+ await new Promise((resolve, reject) => {
74
+ this.pendingRead = (err) => (err ? reject(err) : resolve());
75
+ });
76
+ }
77
+ const bytesRead = this.data.copy(buffer, offset, 0, Math.min(length, this.data.length));
78
+ this.data = this.data.subarray(bytesRead);
79
+ return { bytesRead, buffer };
80
+ }
81
+
82
+ async write(buffer) {
83
+ if (!this.isOpen) throw new Error("Port is not open");
84
+ trace(">>", buffer);
85
+ await new Promise((resolve, reject) => this.child.stdin.write(buffer, (err) => (err ? reject(err) : resolve())));
86
+ }
87
+
88
+ async update(options) {
89
+ this.openOptions.baudRate = options.baudRate;
90
+ }
91
+ async set() {}
92
+ async get() {
93
+ return { cts: true, dsr: true, dcd: true };
94
+ }
95
+ async getBaudRate() {
96
+ return { baudRate: this.openOptions.baudRate };
97
+ }
98
+ async flush() {
99
+ this.data = Buffer.alloc(0);
100
+ }
101
+ async drain() {}
102
+ }
103
+
104
+ function withSimPort(serialport) {
105
+ const real = serialport.SerialPort.binding;
106
+ const binding = {
107
+ async list() {
108
+ const ports = await real.list();
109
+ if (fs.existsSync(SIM_BIN)) ports.push({ path: SIM_PATH, manufacturer: "grblHAL simulator (Agentic Numerical Control)" });
110
+ return ports;
111
+ },
112
+ async open(options) {
113
+ if (options.path === SIM_PATH) return new SimPortBinding({ ...options });
114
+ return real.open(options);
115
+ },
116
+ };
117
+ class SerialPort extends SerialPortStream {
118
+ constructor(options, openCallback) {
119
+ super({ binding, ...options }, openCallback);
120
+ }
121
+ }
122
+ SerialPort.list = binding.list;
123
+ SerialPort.binding = binding;
124
+ return { ...serialport, SerialPort };
125
+ }
126
+
127
+ let patched = null;
128
+ const load = Module._load;
129
+ Module._load = function (request, parent, isMain) {
130
+ const mod = load.call(this, request, parent, isMain);
131
+ if (request !== "serialport") return mod;
132
+ patched ??= withSimPort(mod);
133
+ return patched;
134
+ };