From 9c7c0c5531cb72c8a471293d16459ce2ce8f2d5a Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 16:45:48 +0800 Subject: [PATCH 01/13] Replace pear-updater-bootstrap with pear-install --- package.json | 11 +---- pear.js | 126 ++++++++++++++++++--------------------------------- 2 files changed, 44 insertions(+), 93 deletions(-) diff --git a/package.json b/package.json index 36685eb..3fbbfb0 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,6 @@ "keywords": [ "pear" ], - "pear": { - "platform": { - "key": "pear://pzcjqmpoo6szkoc4bpkw65ib9ctnrq7b6mneeinbhbheihaq6p6o" - } - }, "author": "Holepunch Inc", "license": "Apache-2.0", "bugs": { @@ -26,13 +21,9 @@ "homepage": "https://docs.pears.com", "dependencies": { "graceful-goodbye": "^1.3.2", - "corestore": "7.9.2", - "hypercore": "11.29.0", - "hypercore-crypto": "^3.6.1", - "hypercore-id-encoding": "^1.2.0", + "pear-install": "^1.2.0", "pear-updater-bootstrap": "^2.0.0", "rocksdb-native": "^3.5.7", - "speedometer": "^1.1.0", "tiny-byte-size": "^1.1.0", "which-runtime": "^1.2.0" }, diff --git a/pear.js b/pear.js index 095ebc8..de293ce 100755 --- a/pear.js +++ b/pear.js @@ -1,22 +1,16 @@ #!/usr/bin/env node const process = require('process') -const HypercoreID = require('hypercore-id-encoding') const os = require('os') const path = require('path') const fs = require('fs') -const { isWindows, isLinux, isMac, platform, arch } = require('which-runtime') +const { isWindows, isLinux, isMac } = require('which-runtime') const goodbye = require('graceful-goodbye') -const speedometer = require('speedometer') const byteSize = require('tiny-byte-size') -const { discoveryKey } = require('hypercore-crypto') const isTTY = process.stdout.isTTY -const PROD_KEY = 'pear://pzcjqmpoo6szkoc4bpkw65ib9ctnrq7b6mneeinbhbheihaq6p6o' -const PEAR_KEY = require('./package.json').pear.platform.key -const DKEY = discoveryKey(HypercoreID.decode(PEAR_KEY)).toString('hex') - -const HOST = platform + '-' + arch +const PROD_KEY = 'pear://' +const PEAR_KEY = PROD_KEY const PEAR_DIR = isMac ? path.join(os.homedir(), 'Library', 'Application Support', 'pear') @@ -24,14 +18,8 @@ const PEAR_DIR = isMac ? path.join(os.homedir(), '.config', 'pear') : path.join(os.homedir(), 'AppData', 'Roaming', 'pear') -const LINK = path.join(PEAR_DIR, 'current') const BIN = path.join(PEAR_DIR, 'bin') -const CURRENT_BIN = path.join( - LINK, - 'by-arch', - HOST, - 'bin/pear-runtime' + (isWindows ? '.exe' : '') -) +const CURRENT_BIN = path.join(BIN, `pear${isWindows ? '.exe' : ''}`) const forceUpdate = process.argv[2] === 'update' @@ -72,18 +60,21 @@ Please install it first using the appropriate package manager for your system. `) process.exit(1) } - const bootstrap = require('pear-updater-bootstrap') + const Install = require('pear-install') console.log( 'Installing Pear Runtime (Please stand by, this might take a bit...)\n' ) if (PEAR_KEY !== PROD_KEY) console.log('Bootstrapping:', PEAR_KEY) - bootstrap(PEAR_KEY, PEAR_DIR, { - onupdater: startDriveMonitor, - force: forceUpdate - }).then( - function () { - stopDriveMonitor() + + const install = new Install({ link: PEAR_KEY, to: PEAR_DIR }) + + if (isTTY) install.on('stats', printStats) + + install.on('final', (result) => { + if (isTTY) clear() + + if (result.success) { console.log('Pear Runtime installed!') console.log() console.log('Finish the installation by opening the runtime app') @@ -100,17 +91,30 @@ Please install it first using the appropriate package manager for your system. console.log(`export PATH="${BIN}:$PATH"`) } } - }, - function (err) { - if (forceUpdate && err.code === 'ENOENT') { - console.log( - `Update failed: Pear Runtime is not installed at ${err.path}` - ) - } else { - throw err - } + } else { + console.error('Installation failed:', result) + process.exit(1) } - ) + }) + + install.on('error', (err) => { + if (isTTY) clear() + if (forceUpdate && err.code === 'ENOENT') { + console.log(`Update failed: Pear Runtime is not installed at ${err.path}`) + } else { + throw err + } + }) + + install + .ready() + .catch((err) => { + if (isTTY) clear() + throw err + }) + .finally(() => { + install.close() + }) } function makeBin() { @@ -133,63 +137,19 @@ function makeBin() { } function isInstalled() { - try { - const p = fs.realpathSync(LINK) - return path.basename(path.dirname(p)) === DKEY - } catch { - return false - } + return fs.existsSync(CURRENT_BIN) } -let monitorInterval = null - function clear() { process.stdout.write('\x1b[2K') // clear line process.stdout.write('\r') // cursor to 0 } - -function stopDriveMonitor() { - clearInterval(monitorInterval) - if (isTTY) clear() -} - -function startDriveMonitor(updater) { +function printStats(stats) { if (!isTTY) return - - const downloadSpeedometer = speedometer() - const uploadSpeedometer = speedometer() - let peers = 0 - let downloadedBytes = 0 - let uploadedBytes = 0 - - updater.drive - .getBlobs() - .then((blobs) => { - blobs.core.on('download', (_index, bytes) => { - downloadedBytes += bytes - downloadSpeedometer(bytes) - }) - blobs.core.on('upload', (_index, bytes) => { - uploadedBytes += bytes - uploadSpeedometer(bytes) - }) - blobs.core.on('peer-add', () => { - peers = blobs.core.peers.length - }) - blobs.core.on('peer-remove', () => { - peers = blobs.core.peers.length - }) - }) - .catch(() => { - // ignore - }) - - monitorInterval = setInterval(() => { - clear() - process.stdout.write( - `[⬇ ${byteSize(downloadedBytes)} - ${byteSize(downloadSpeedometer())}/s - ${peers} peers] [⬆ ${byteSize(uploadedBytes)} - ${byteSize(uploadSpeedometer())}/s - ${peers} peers]` - ) - }, 500) + clear() + process.stdout.write( + `[⬇ ${byteSize(stats.download.bytes)} - ${byteSize(stats.download.speed)}/s - ${stats.peers} peers]` + ) } function libatomicCheck() { From 604096162d9f6c598cd89427a90dfec68e2a012e Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 17:36:17 +0800 Subject: [PATCH 02/13] Cleanup messages about PATH --- pear.js | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/pear.js b/pear.js index de293ce..dcf747b 100755 --- a/pear.js +++ b/pear.js @@ -76,21 +76,6 @@ Please install it first using the appropriate package manager for your system. if (result.success) { console.log('Pear Runtime installed!') - console.log() - console.log('Finish the installation by opening the runtime app') - console.log() - console.log('npx pear run pear://runtime') - if (makeBin()) { - console.log() - console.log('Or by adding the following to your path') - console.log() - if (isWindows) { - console.log(`cmd: set PATH="${BIN};%PATH%"`) - console.log(`PowerShell: $env:PATH="${BIN};$env:PATH"`) - } else { - console.log(`export PATH="${BIN}:$PATH"`) - } - } } else { console.error('Installation failed:', result) process.exit(1) @@ -117,25 +102,6 @@ Please install it first using the appropriate package manager for your system. }) } -function makeBin() { - try { - fs.mkdirSync(BIN, { recursive: true }) - - if (isWindows) { - fs.writeFileSync( - path.join(BIN, 'pear.cmd'), - `@echo off\r\n"${CURRENT_BIN}" %*` - ) - fs.writeFileSync(path.join(BIN, 'pear.ps1'), `& "${CURRENT_BIN}" @args`) - } else { - fs.symlinkSync(CURRENT_BIN, path.join(BIN, 'pear')) - } - } catch { - return false - } - return true -} - function isInstalled() { return fs.existsSync(CURRENT_BIN) } @@ -144,6 +110,7 @@ function clear() { process.stdout.write('\x1b[2K') // clear line process.stdout.write('\r') // cursor to 0 } + function printStats(stats) { if (!isTTY) return clear() From 121213cc0ad0b74515054da5cabbbcca4650bbff Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 17:37:42 +0800 Subject: [PATCH 03/13] Remove difference between PEAR_KEY and PROD_KEY --- pear.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pear.js b/pear.js index dcf747b..f67c52f 100755 --- a/pear.js +++ b/pear.js @@ -9,8 +9,7 @@ const byteSize = require('tiny-byte-size') const isTTY = process.stdout.isTTY -const PROD_KEY = 'pear://' -const PEAR_KEY = PROD_KEY +const PEAR_KEY = 'pear://' const PEAR_DIR = isMac ? path.join(os.homedir(), 'Library', 'Application Support', 'pear') @@ -65,7 +64,7 @@ Please install it first using the appropriate package manager for your system. console.log( 'Installing Pear Runtime (Please stand by, this might take a bit...)\n' ) - if (PEAR_KEY !== PROD_KEY) console.log('Bootstrapping:', PEAR_KEY) + console.log('Bootstrapping:', PEAR_KEY) const install = new Install({ link: PEAR_KEY, to: PEAR_DIR }) From 0a7f146012715cac130efbf55609057216397567 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 17:50:23 +0800 Subject: [PATCH 04/13] Use default dir used by pear-install --- pear.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pear.js b/pear.js index f67c52f..3dacc84 100755 --- a/pear.js +++ b/pear.js @@ -11,13 +11,12 @@ const isTTY = process.stdout.isTTY const PEAR_KEY = 'pear://' -const PEAR_DIR = isMac - ? path.join(os.homedir(), 'Library', 'Application Support', 'pear') - : isLinux - ? path.join(os.homedir(), '.config', 'pear') - : path.join(os.homedir(), 'AppData', 'Roaming', 'pear') - -const BIN = path.join(PEAR_DIR, 'bin') +const home = os.homedir() +const localAppData = + process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local') +const BIN = isWindows + ? path.join(localAppData, 'Programs', 'pear') + : path.join(home, '.local', 'bin') const CURRENT_BIN = path.join(BIN, `pear${isWindows ? '.exe' : ''}`) const forceUpdate = process.argv[2] === 'update' @@ -66,7 +65,7 @@ Please install it first using the appropriate package manager for your system. ) console.log('Bootstrapping:', PEAR_KEY) - const install = new Install({ link: PEAR_KEY, to: PEAR_DIR }) + const install = new Install({ link: PEAR_KEY }) if (isTTY) install.on('stats', printStats) From 9418209a01f37efdd59dd2fe8932825701c828c1 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:00:13 +0800 Subject: [PATCH 05/13] Remove unused import --- pear.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pear.js b/pear.js index 3dacc84..55b390f 100755 --- a/pear.js +++ b/pear.js @@ -3,7 +3,7 @@ const process = require('process') const os = require('os') const path = require('path') const fs = require('fs') -const { isWindows, isLinux, isMac } = require('which-runtime') +const { isWindows, isLinux } = require('which-runtime') const goodbye = require('graceful-goodbye') const byteSize = require('tiny-byte-size') From fa89f8dd4750f17c0e9252032e01d7bb541a71c4 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:02:48 +0800 Subject: [PATCH 06/13] Indent printed path --- pear.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pear.js b/pear.js index 55b390f..43edcc5 100755 --- a/pear.js +++ b/pear.js @@ -23,7 +23,7 @@ const forceUpdate = process.argv[2] === 'update' if (isInstalled() && !forceUpdate) { const warning = `[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}: -${BIN} + ${BIN} Until then, this executable spawns the ${'`pear`'} binary. Fix automatically with: pear run pear://runtime` console.error(warning) From 16b2cdd895793e04d21072a7dc97c53258dd0665 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:03:19 +0800 Subject: [PATCH 07/13] Improve var naming --- pear.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pear.js b/pear.js index 43edcc5..6139c3e 100755 --- a/pear.js +++ b/pear.js @@ -11,26 +11,28 @@ const isTTY = process.stdout.isTTY const PEAR_KEY = 'pear://' -const home = os.homedir() -const localAppData = - process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local') -const BIN = isWindows - ? path.join(localAppData, 'Programs', 'pear') - : path.join(home, '.local', 'bin') -const CURRENT_BIN = path.join(BIN, `pear${isWindows ? '.exe' : ''}`) +const HOME = os.homedir() +const BIN_PATH = isWindows + ? path.join( + process.env.LOCALAPPDATA || path.join(HOME, 'AppData', 'Local'), + 'Programs', + 'pear' + ) + : path.join(HOME, '.local', 'bin') +const BIN = path.join(BIN_PATH, `pear${isWindows ? '.exe' : ''}`) const forceUpdate = process.argv[2] === 'update' if (isInstalled() && !forceUpdate) { const warning = `[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}: - ${BIN} + ${BIN_PATH} Until then, this executable spawns the ${'`pear`'} binary. Fix automatically with: pear run pear://runtime` console.error(warning) let child = null const childProcessExit = new Promise((resolve) => { child = require('child_process') - .spawn(CURRENT_BIN, process.argv.slice(2), { + .spawn(BIN, process.argv.slice(2), { stdio: 'inherit' }) .on('exit', function (code) { @@ -101,7 +103,7 @@ Please install it first using the appropriate package manager for your system. } function isInstalled() { - return fs.existsSync(CURRENT_BIN) + return fs.existsSync(BIN) } function clear() { From f34a1f4bedf558fd64bfd148f4bffe9b7bfbd5c8 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:04:03 +0800 Subject: [PATCH 08/13] Inline one-liner check --- pear.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pear.js b/pear.js index 6139c3e..81b1f30 100755 --- a/pear.js +++ b/pear.js @@ -23,7 +23,7 @@ const BIN = path.join(BIN_PATH, `pear${isWindows ? '.exe' : ''}`) const forceUpdate = process.argv[2] === 'update' -if (isInstalled() && !forceUpdate) { +if (fs.existsSync(BIN) && !forceUpdate) { const warning = `[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}: ${BIN_PATH} Until then, this executable spawns the ${'`pear`'} binary. @@ -102,10 +102,6 @@ Please install it first using the appropriate package manager for your system. }) } -function isInstalled() { - return fs.existsSync(BIN) -} - function clear() { process.stdout.write('\x1b[2K') // clear line process.stdout.write('\r') // cursor to 0 From 9bba5d85fd0ad5a588d131caf2cf0975c2278814 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:05:14 +0800 Subject: [PATCH 09/13] Remove unnecessary var declaration --- pear.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pear.js b/pear.js index 81b1f30..3b44db4 100755 --- a/pear.js +++ b/pear.js @@ -24,11 +24,10 @@ const BIN = path.join(BIN_PATH, `pear${isWindows ? '.exe' : ''}`) const forceUpdate = process.argv[2] === 'update' if (fs.existsSync(BIN) && !forceUpdate) { - const warning = `[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}: + console.error(`[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}: ${BIN_PATH} Until then, this executable spawns the ${'`pear`'} binary. -Fix automatically with: pear run pear://runtime` - console.error(warning) +Fix automatically with: pear run pear://runtime`) let child = null const childProcessExit = new Promise((resolve) => { child = require('child_process') From ec079b74211e3c55ea6653a82c638cdeaea7a7b9 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:08:39 +0800 Subject: [PATCH 10/13] Set production key to PEAR_KEY --- pear.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pear.js b/pear.js index 3b44db4..d7f93ec 100755 --- a/pear.js +++ b/pear.js @@ -9,7 +9,7 @@ const byteSize = require('tiny-byte-size') const isTTY = process.stdout.isTTY -const PEAR_KEY = 'pear://' +const PEAR_KEY = 'pear://smw4thqaqed9iq6bae7a9cxd4fesruixgkafe38jny33ahs33igy' const HOME = os.homedir() const BIN_PATH = isWindows From e2377ac7b98d8ea6fe1142df880e461ef683a0a4 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:14:52 +0800 Subject: [PATCH 11/13] Remove pear-updater-bootstrap from dependencies --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 3fbbfb0..c715163 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "dependencies": { "graceful-goodbye": "^1.3.2", "pear-install": "^1.2.0", - "pear-updater-bootstrap": "^2.0.0", "rocksdb-native": "^3.5.7", "tiny-byte-size": "^1.1.0", "which-runtime": "^1.2.0" From 7c10ad212ac94068dbb5ebb727ee3ff77ed0e352 Mon Sep 17 00:00:00 2001 From: Jan Keith Darunday Date: Tue, 7 Jul 2026 18:19:26 +0800 Subject: [PATCH 12/13] Remove force update path as pear-install does not support it --- pear.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pear.js b/pear.js index d7f93ec..1407780 100755 --- a/pear.js +++ b/pear.js @@ -21,9 +21,7 @@ const BIN_PATH = isWindows : path.join(HOME, '.local', 'bin') const BIN = path.join(BIN_PATH, `pear${isWindows ? '.exe' : ''}`) -const forceUpdate = process.argv[2] === 'update' - -if (fs.existsSync(BIN) && !forceUpdate) { +if (fs.existsSync(BIN)) { console.error(`[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}: ${BIN_PATH} Until then, this executable spawns the ${'`pear`'} binary. @@ -83,11 +81,7 @@ Please install it first using the appropriate package manager for your system. install.on('error', (err) => { if (isTTY) clear() - if (forceUpdate && err.code === 'ENOENT') { - console.log(`Update failed: Pear Runtime is not installed at ${err.path}`) - } else { - throw err - } + throw err }) install From 7cc43a0f9dda3c6849fd70e03dd18c02516c9e01 Mon Sep 17 00:00:00 2001 From: rafapaezbas Date: Tue, 7 Jul 2026 16:48:23 +0200 Subject: [PATCH 13/13] tweaks --- pear.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/pear.js b/pear.js index 1407780..2b5cb75 100755 --- a/pear.js +++ b/pear.js @@ -10,6 +10,7 @@ const byteSize = require('tiny-byte-size') const isTTY = process.stdout.isTTY const PEAR_KEY = 'pear://smw4thqaqed9iq6bae7a9cxd4fesruixgkafe38jny33ahs33igy' +const key = PEAR_KEY const HOME = os.homedir() const BIN_PATH = isWindows @@ -22,10 +23,6 @@ const BIN_PATH = isWindows const BIN = path.join(BIN_PATH, `pear${isWindows ? '.exe' : ''}`) if (fs.existsSync(BIN)) { - console.error(`[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}: - ${BIN_PATH} -Until then, this executable spawns the ${'`pear`'} binary. -Fix automatically with: pear run pear://runtime`) let child = null const childProcessExit = new Promise((resolve) => { child = require('child_process') @@ -59,12 +56,10 @@ Please install it first using the appropriate package manager for your system. } const Install = require('pear-install') - console.log( - 'Installing Pear Runtime (Please stand by, this might take a bit...)\n' - ) - console.log('Bootstrapping:', PEAR_KEY) + console.log('Installing Pear (Please stand by, this might take a bit...)') + console.log('Bootstrapping:', key) - const install = new Install({ link: PEAR_KEY }) + const install = new Install({ link: key }) if (isTTY) install.on('stats', printStats) @@ -72,7 +67,7 @@ Please install it first using the appropriate package manager for your system. if (isTTY) clear() if (result.success) { - console.log('Pear Runtime installed!') + console.log('Pear installed!') } else { console.error('Installation failed:', result) process.exit(1) @@ -88,7 +83,7 @@ Please install it first using the appropriate package manager for your system. .ready() .catch((err) => { if (isTTY) clear() - throw err + console.error(err.message) }) .finally(() => { install.close()