From 8c92e1c9631bd214e3fc398a4cae90142e511b6f Mon Sep 17 00:00:00 2001 From: Paskooter Date: Sat, 25 Apr 2026 12:45:48 -0400 Subject: [PATCH] Fix Track.lookAt crash and fetchBuffer hang; bump to 2.0.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Track.lookAt: was calling client.user.lookAtEntity (undefined), now correctly routes to client.behavior.lookAtEntity — fixes unhandled promise rejections on every face-detection event - connection: httpGet/httpGetStream had no socket timeout; added 15 s req.setTimeout so fetchBuffer/pipe reject instead of hanging forever - connection: _txSend silently dropped commands when session not yet ready and returned a dead txId, causing callers to hang for the full timeout; now throws immediately with code NOT_READY Co-Authored-By: Claude Sonnet 4.6 --- API.md | 2 +- package.json | 2 +- src/connection.js | 14 +++++++++++--- src/structures/Track.js | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/API.md b/API.md index 5e2126d..1474a23 100644 --- a/API.md +++ b/API.md @@ -1,6 +1,6 @@ # rom-control -Discord.js-style OOP client for the Jibo ROM WebSocket API (port 8160). +Robust client for the Jibo ROM WebSocket API (port 8160). **Requires:** Node.js ≥ 16, `ws` ^8.14.2 diff --git a/package.json b/package.json index cd5fe06..24cd784 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rom-control", - "version": "2.0.0", + "version": "2.0.1", "description": "Discord.js-style OOP client for the Jibo ROM WebSocket API", "main": "./index.js", "exports": { diff --git a/src/connection.js b/src/connection.js index 4773d9c..637f347 100644 --- a/src/connection.js +++ b/src/connection.js @@ -41,24 +41,30 @@ function httpPost(host, port, path, body) { }); } -function httpGet(host, port, path) { +function httpGet(host, port, path, timeoutMs = 15000) { return new Promise((resolve, reject) => { const chunks = []; const req = http.get({ host, port, path }, (res) => { res.on('data', d => chunks.push(d)); res.on('end', () => resolve(Buffer.concat(chunks))); }); + req.setTimeout(timeoutMs, () => { + req.destroy(Object.assign(new Error('fetchMedia timed out'), { code: 'FETCH_TIMEOUT' })); + }); req.on('error', reject); }); } -function httpGetStream(host, port, path, dest) { +function httpGetStream(host, port, path, dest, timeoutMs = 15000) { return new Promise((resolve, reject) => { const req = http.get({ host, port, path }, (res) => { res.pipe(dest); res.on('end', resolve); dest.on('close', () => req.destroy()); }); + req.setTimeout(timeoutMs, () => { + req.destroy(Object.assign(new Error('fetchMediaStream timed out'), { code: 'FETCH_TIMEOUT' })); + }); req.on('error', reject); }); } @@ -323,7 +329,9 @@ class RomConnection extends EventEmitter { if (this.ws && this.ws.readyState === WebSocket.OPEN) { // Don't send any command except StartSession before the session ID arrives. // Commands sent with an empty SessionID are rejected by ROM with 403 Forbidden. - if (!this.sessionID && command.Type !== 'StartSession') return txId; + if (!this.sessionID && command.Type !== 'StartSession') { + throw Object.assign(new Error(`Cannot send ${command.Type}: session not ready`), { code: 'NOT_READY' }); + } this._txCommands.set(txId, command.Type); if (this._txCommands.size > 60) { diff --git a/src/structures/Track.js b/src/structures/Track.js index 88ec61e..7070e47 100644 --- a/src/structures/Track.js +++ b/src/structures/Track.js @@ -14,7 +14,7 @@ class Track { // Look at this entity. Resolves when the head reaches the target. async lookAt(track = true) { - return this._client.user.lookAtEntity(this.id, track); + return this._client.behavior.lookAtEntity(this.id, track); } }