From d031fc51565a4cd052b55a554130d0ebe52fed0b Mon Sep 17 00:00:00 2001 From: rachittiwari Date: Mon, 12 Oct 2026 20:33:29 +0530 Subject: [PATCH 1/6] ci: add extract-then-spawn check on Windows, Linux and macOS Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/binary-spawn-check.yml | 27 ++++++++++++++ scripts/check-binary-spawn.js | 46 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .github/workflows/binary-spawn-check.yml create mode 100644 scripts/check-binary-spawn.js diff --git a/.github/workflows/binary-spawn-check.yml b/.github/workflows/binary-spawn-check.yml new file mode 100644 index 0000000..de881e4 --- /dev/null +++ b/.github/workflows/binary-spawn-check.yml @@ -0,0 +1,27 @@ +name: Binary extract and spawn check +on: + push: + pull_request: + +jobs: + spawn: + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + node: [18, 22] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - name: Enable Defender real-time protection + if: runner.os == 'Windows' + shell: pwsh + run: | + Set-MpPreference -DisableRealtimeMonitoring $false + Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled, OnAccessProtectionEnabled + - run: npm install --omit=dev --no-audit --no-fund + - run: node scripts/check-binary-spawn.js 15 diff --git a/scripts/check-binary-spawn.js b/scripts/check-binary-spawn.js new file mode 100644 index 0000000..4176247 --- /dev/null +++ b/scripts/check-binary-spawn.js @@ -0,0 +1,46 @@ +// Downloads and extracts the tunnel binary into a fresh dir, then spawns it immediately (as Tunnel.start does), N times. +var fs = require('fs'), + os = require('os'), + path = require('path'), + childProcess = require('child_process'), + TunnelBinary = require('../lib/tunnel_binary'), + cfg = require('../lib/cfg/node-tunnel-config-v3-latest.json'); + +var runs = Number(process.argv[2]) || 10; +var failures = 0; + +function once(i, done) { + var tb = new TunnelBinary({ jsonResponse: cfg, logger: { log: function() {} } }, {}); + var dir = fs.mkdtempSync(path.join(os.tmpdir(), 'lt-')); + var timer = setTimeout(function() { + done(new Error('timed out waiting for download/extract')); + }, 120000); + tb.download_({}, dir, 1, function(binaryPath) { + clearTimeout(timer); + var proc; + try { + proc = childProcess.spawn(binaryPath, ['--help'], { stdio: 'ignore' }); + } catch (e) { + return done(e); + } + proc.on('error', done); + proc.on('spawn', function() { + proc.kill(); + done(); + }); + }); +} + +function next(i) { + if (i > runs) { + console.log('RESULT ' + (runs - failures) + '/' + runs + ' spawned ok'); + process.exit(failures ? 1 : 0); + } + once(i, function(err) { + if (err) failures++; + console.log('run ' + i + ': ' + (err ? 'FAIL ' + (err.code || err.message) : 'ok')); + next(i + 1); + }); +} + +next(1); From 1d7b4dd67316b66b784bc0f601bdd9ae6e4f2789 Mon Sep 17 00:00:00 2001 From: rachittiwari Date: Mon, 12 Oct 2026 20:33:38 +0530 Subject: [PATCH 2/6] fix: wait for extracted binary to close before spawning (Windows EBUSY) Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/tunnel_binary.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/tunnel_binary.js b/lib/tunnel_binary.js index 1c63b62..e92bfe8 100644 --- a/lib/tunnel_binary.js +++ b/lib/tunnel_binary.js @@ -140,7 +140,20 @@ function TunnelBinary(httpTunnelConfig, options) { console.log(`Extracting binary`); var zip = new StreamZip.async({ file: binaryPath }); zip - .extract(null, path.resolve(destParentDir)) + .stream(destBinaryName) + .then(function(entryStream) { + // Resolve on 'close', not 'finish': spawning before the fd closes fails with EBUSY on Windows. + return new Promise(function(resolve, reject) { + var out = fs.createWriteStream(destBinaryPath); + entryStream.on('error', function(e) { + out.destroy(); + reject(e); + }); + out.on('error', reject); + out.on('close', resolve); + entryStream.pipe(out); + }); + }) .then(function() { return zip.close(); }) From a25e31f84d9c80db558be9527dd3894c04e16553 Mon Sep 17 00:00:00 2001 From: rachittiwari Date: Mon, 12 Oct 2026 20:36:07 +0530 Subject: [PATCH 3/6] ci: add spawn check with simulated AV slow close Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/binary-spawn-check.yml | 4 ++++ scripts/slow-close.js | 25 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 scripts/slow-close.js diff --git a/.github/workflows/binary-spawn-check.yml b/.github/workflows/binary-spawn-check.yml index de881e4..9c1d843 100644 --- a/.github/workflows/binary-spawn-check.yml +++ b/.github/workflows/binary-spawn-check.yml @@ -20,8 +20,12 @@ jobs: - name: Enable Defender real-time protection if: runner.os == 'Windows' shell: pwsh + continue-on-error: true run: | + Remove-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender' -Recurse -Force -ErrorAction SilentlyContinue Set-MpPreference -DisableRealtimeMonitoring $false Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled, OnAccessProtectionEnabled - run: npm install --omit=dev --no-audit --no-fund - run: node scripts/check-binary-spawn.js 15 + - name: Spawn check with slow binary close (simulated AV scan-on-close) + run: node -r ./scripts/slow-close.js scripts/check-binary-spawn.js 5 diff --git a/scripts/slow-close.js b/scripts/slow-close.js new file mode 100644 index 0000000..5c1982b --- /dev/null +++ b/scripts/slow-close.js @@ -0,0 +1,25 @@ +// Preload that delays closing the extracted LT binary by 800ms, mimicking real-time AV scan-on-close on Windows. +var fs = require('fs'); +var binaryFds = {}; +var origOpen = fs.open; +var origClose = fs.close; + +fs.open = function(file) { + var args = Array.prototype.slice.call(arguments); + var cb = args[args.length - 1]; + args[args.length - 1] = function(err, fd) { + if (!err && /[\\/]LT(\.exe)?$/.test(String(file))) binaryFds[fd] = true; + cb(err, fd); + }; + return origOpen.apply(fs, args); +}; + +fs.close = function(fd, cb) { + if (binaryFds[fd]) { + delete binaryFds[fd]; + return setTimeout(function() { + origClose.call(fs, fd, cb); + }, 800); + } + return origClose.apply(fs, arguments); +}; From 10cf189337bd968fcd8bb77affaa10661b000a2e Mon Sep 17 00:00:00 2001 From: rachittiwari Date: Mon, 12 Oct 2026 20:40:57 +0530 Subject: [PATCH 4/6] ci: drop Defender step (hosted runners keep real-time protection off) Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/binary-spawn-check.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/binary-spawn-check.yml b/.github/workflows/binary-spawn-check.yml index 9c1d843..1af3741 100644 --- a/.github/workflows/binary-spawn-check.yml +++ b/.github/workflows/binary-spawn-check.yml @@ -17,14 +17,6 @@ jobs: - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - - name: Enable Defender real-time protection - if: runner.os == 'Windows' - shell: pwsh - continue-on-error: true - run: | - Remove-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender' -Recurse -Force -ErrorAction SilentlyContinue - Set-MpPreference -DisableRealtimeMonitoring $false - Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled, OnAccessProtectionEnabled - run: npm install --omit=dev --no-audit --no-fund - run: node scripts/check-binary-spawn.js 15 - name: Spawn check with slow binary close (simulated AV scan-on-close) From e2f501e8bcf72477d7d64989d39f6dc34ebf6f0b Mon Sep 17 00:00:00 2001 From: rachittiwari Date: Mon, 12 Oct 2026 20:40:57 +0530 Subject: [PATCH 5/6] chore(release): bump version to 4.0.14 Co-Authored-By: Claude Opus 5.5 (1M context) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bfb1b02..4352cfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lambdatest/node-tunnel", - "version": "4.0.13", + "version": "4.0.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@lambdatest/node-tunnel", - "version": "4.0.13", + "version": "4.0.14", "license": "ISC", "dependencies": { "axios": "^1.6.2", diff --git a/package.json b/package.json index 134ffb1..219c171 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lambdatest/node-tunnel", - "version": "4.0.13", + "version": "4.0.14", "description": "Nodejs bindings for LambdaTest Tunnel", "main": "index.js", "files": [ From 01246cf36262996e43ecfc9170ebd9c2c1a50263 Mon Sep 17 00:00:00 2001 From: rachittiwari Date: Mon, 12 Oct 2026 20:43:08 +0530 Subject: [PATCH 6/6] chore(config): set latest 4.0.14, add 4.0.13/4.0.14 to supportedVersions, sync FreeBSD hashes Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/cfg/node-tunnel-config-v3-latest.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/cfg/node-tunnel-config-v3-latest.json b/lib/cfg/node-tunnel-config-v3-latest.json index e6fcb4b..79ed1a9 100644 --- a/lib/cfg/node-tunnel-config-v3-latest.json +++ b/lib/cfg/node-tunnel-config-v3-latest.json @@ -63,20 +63,20 @@ { "httpPath": "https://downloads.lambdatest.com/tunnel/v3/freebsd/32bit/LT_Freebsd.zip", "binaryName": "LT_Freebsd.zip", - "hash": "3525242664a3a24aa9e49969d137dadb" + "hash": "0747869934c7838e1e812d508c242d50" }, "64bit": { "httpPath": "https://downloads.lambdatest.com/tunnel/v3/freebsd/64bit/LT_Freebsd.zip", "binaryName": "LT_Freebsd.zip", - "hash": "e5fec50196cd1fa02306bdd74acccea3" + "hash": "5a64de0bf4e821da8ddf26c830fc322a" } } } }, "AuthUrl": "https://accounts.lambdatest.com/api/user/token/auth", "logEnable": true, - "latest": "4.0.12", + "latest": "4.0.14", "supportedVersions": [ "1.0.0", @@ -125,6 +125,8 @@ "4.0.9", "4.0.10", "4.0.11", - "4.0.12" + "4.0.12", + "4.0.13", + "4.0.14" ] }