donsetch 4.3.7 → 4.4.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 (2) hide show
  1. package/install.js +33 -30
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -11,6 +11,7 @@ const crypto = require('crypto');
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
13
  const { execFileSync } = require('child_process');
14
+ const { pipeline } = require('stream/promises');
14
15
 
15
16
  const REPO = 'dondai44423/donsetch';
16
17
  const VERSION = require('./package.json').version;
@@ -117,8 +118,6 @@ if (fs.existsSync(binaryPath) && fs.existsSync(stampPath)) {
117
118
  console.log(`donsetch: binary version stamp mismatch; re-downloading ${VERSION}`);
118
119
  }
119
120
 
120
- fs.mkdirSync(binDir, { recursive: true });
121
-
122
121
  const releasesBase = (process.env.DONSETCH_RELEASES_BASE
123
122
  || `https://github.com/${REPO}/releases/download`).replace(/\/+$/, '');
124
123
  const assetUrl = `${releasesBase}/${TAG}/${plat.asset}`;
@@ -253,15 +252,11 @@ async function download(url, dest) {
253
252
  await drain(response);
254
253
  throw error;
255
254
  }
256
- await new Promise((resolve, reject) => {
257
- const file = fs.createWriteStream(dest);
258
- response.pipe(file);
259
- file.on('finish', () => {
260
- file.close(resolve);
261
- });
262
- file.on('error', reject);
263
- response.on('error', reject);
264
- });
255
+ // pipeline destroys BOTH ends on error. A bare
256
+ // `response.pipe(file)` leaves the write stream open when the
257
+ // source errors (documented Node behavior), so the unlink below
258
+ // fails with EBUSY on Windows and the partial download survives.
259
+ await pipeline(response, fs.createWriteStream(dest));
265
260
  return;
266
261
  } catch (error) {
267
262
  try { fs.unlinkSync(dest); } catch (_) {}
@@ -277,6 +272,10 @@ async function main() {
277
272
  const tarball = path.join(binDir, plat.asset);
278
273
  const checksumFile = path.join(binDir, 'checksum.sha256');
279
274
 
275
+ // Inside main() so an unwritable install dir gets the curated
276
+ // error below, not a raw Node stack trace from a top-level throw.
277
+ fs.mkdirSync(binDir, { recursive: true });
278
+
280
279
  if (process.platform === 'win32') {
281
280
  try {
282
281
  execFileSync('tar', ['--version'], { stdio: 'ignore' });
@@ -289,29 +288,33 @@ async function main() {
289
288
  }
290
289
 
291
290
  console.log(`donsetch: downloading ${plat.asset} from ${TAG}...`);
292
- await download(assetUrl, tarball);
293
- console.log('donsetch: verifying checksum...');
294
- await download(checksumUrl, checksumFile);
291
+ try {
292
+ await download(assetUrl, tarball);
293
+ console.log('donsetch: verifying checksum...');
294
+ await download(checksumUrl, checksumFile);
295
+
296
+ const expectedHash = fs.readFileSync(checksumFile, 'utf8').trim().split(/\s+/)[0];
297
+ const actualHash = crypto.createHash('sha256').update(fs.readFileSync(tarball)).digest('hex');
298
+ if (!/^[a-f0-9]{64}$/.test(expectedHash) || actualHash !== expectedHash) {
299
+ throw new Error(`SHA256 mismatch (expected ${expectedHash}, actual ${actualHash})`);
300
+ }
301
+
302
+ console.log('donsetch: extracting...');
303
+ execFileSync('tar', ['xzf', tarball, '-C', binDir], { stdio: 'inherit' });
295
304
 
296
- const expectedHash = fs.readFileSync(checksumFile, 'utf8').trim().split(/\s+/)[0];
297
- const actualHash = crypto.createHash('sha256').update(fs.readFileSync(tarball)).digest('hex');
298
- if (!/^[a-f0-9]{64}$/.test(expectedHash) || actualHash !== expectedHash) {
305
+ if (!fs.existsSync(binaryPath)) {
306
+ throw new Error(`expected ${plat.binary} not found after extraction in ${binDir}`);
307
+ }
308
+ if (process.platform !== 'win32') fs.chmodSync(binaryPath, 0o755);
309
+ fs.writeFileSync(stampPath, `${VERSION}\n`);
310
+ console.log(`donsetch: installed ${plat.binary} to ${binaryPath}`);
311
+ } finally {
312
+ // Always drop the download artifacts: a failed checksum fetch, a
313
+ // failed extract, or a missing binary must not leave the tarball
314
+ // and checksum sitting in ./binaries/ forever.
299
315
  try { fs.unlinkSync(tarball); } catch (_) {}
300
316
  try { fs.unlinkSync(checksumFile); } catch (_) {}
301
- throw new Error(`SHA256 mismatch (expected ${expectedHash}, actual ${actualHash})`);
302
- }
303
-
304
- console.log('donsetch: extracting...');
305
- execFileSync('tar', ['xzf', tarball, '-C', binDir], { stdio: 'inherit' });
306
- try { fs.unlinkSync(tarball); } catch (_) {}
307
- try { fs.unlinkSync(checksumFile); } catch (_) {}
308
-
309
- if (!fs.existsSync(binaryPath)) {
310
- throw new Error(`expected ${plat.binary} not found after extraction in ${binDir}`);
311
317
  }
312
- if (process.platform !== 'win32') fs.chmodSync(binaryPath, 0o755);
313
- fs.writeFileSync(stampPath, `${VERSION}\n`);
314
- console.log(`donsetch: installed ${plat.binary} to ${binaryPath}`);
315
318
  }
316
319
 
317
320
  main().catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donsetch",
3
- "version": "4.3.7",
3
+ "version": "4.4.1",
4
4
  "description": "MCP server that gives AI agents the web: fetch, search, crawl and screenshot from one local Rust binary. Zero API keys, zero accounts, Chrome-true TLS.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": "Bishesh Bhandari",