OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet commits / edbbabd

Wire up pear-runtime for P2P OTA updates
- Replace pear-appdrive with local filesystem serving in serveStatic() - Fix UI_DIR to use __dirname instead of process.cwd() - Remove Pear CORS origin entry (apps run locally now) - Remove appDrive initialization from startServer() - Add pear-runtime for background OTA updates in bare mode - Add upgrade field to package.json - Fix args parsing to support Bare.argv Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
edbbabd147d6fbc70af9b13082790480844338fc
by plankatron <fleeky@gmail.com> · 2026-02-20T19:45:32.000Z

Changed files (3) +38 -53

hyper-wallet.cjs +22 -1
……@@ -23,7 +23,7 @@ const { spawn } = require('bare-subprocess')
2323
2424// Support both Pear runtime and bare/node
2525const isPear = typeof globalThis.Pear !== 'undefined'
26const args = isPear ? (Pear.config.args || []) : process.argv.slice(2)
26const args = isPear ? (Pear.config.args || []) : (typeof Bare !== 'undefined' ? Bare.argv.slice(2) : process.argv.slice(2))
2727
2828const mode = args[0]
2929
……@@ -33,6 +33,27 @@ if (!mode) {
3333 process.exit(0)
3434}
3535
36// Initialize pear-runtime for P2P OTA updates (non-blocking, bare mode only)
37if (!isPear) {
38 try {
39 const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'))
40 if (pkg.upgrade) {
41 import('pear-runtime').then(({ default: PearRuntime }) => {
42 const os = require('bare-os')
43 const p = os.platform()
44 const name = 'hyper-wallet'
45 let dir
46 if (p === 'darwin') dir = path.join(os.homedir(), 'Library', 'Application Support', name)
47 else if (p === 'win32') dir = path.join(os.homedir(), 'AppData', 'Roaming', name)
48 else dir = path.join(os.homedir(), '.config', name)
49
50 const runtime = new PearRuntime({ dir, app: __dirname, version: pkg.version || '1.0.0', upgrade: pkg.upgrade })
51 runtime.on('updated', () => { runtime.applyUpdate().catch(() => {}) })
52 }).catch(() => {})
53 }
54 } catch {}
55}
56
3657// Dispatch based on mode
3758if (mode === 'web') {
3859 // Web server mode
package.json +3 -0
……@@ -2,6 +2,8 @@
22 "name": "hyper-wallet",
33 "type": "module",
44 "main": "hyper-wallet.cjs",
5 "private": true,
6 "upgrade": "pear://nfwe34qee4u6qsb63ewmu46xe5srekg98toku95cqh5gsgitdxfy",
57 "pear": {
68 "name": "hyper-wallet-experimental",
79 "type": "terminal",
……@@ -44,6 +46,7 @@
4446 "hyperswarm": "^4.8.5",
4547 "keet-identity-key": "^3.1.0",
4648 "pear-appdrive": "^1.1.2",
49 "pear-runtime": "^0.4.2",
4750 "protomux": "^3.6.0",
4851 "qrcode": "^1.5.4"
4952 }
wallet-server.cjs +13 -52
……@@ -13,9 +13,8 @@ const fs = require('bare-fs')
1313const path = require('bare-path')
1414const os = require('bare-os')
1515
16// Check if running under Pear
16// Check if running under Pear (legacy path)
1717const isPear = typeof Pear !== 'undefined'
18let appDrive = null
1918
2019const {
2120 initializeStorage,
……@@ -226,7 +225,7 @@ const PORT = parseInt(process.env.PORT || config.server?.port || 4004)
226225const HOST = process.env.WALLET_HOST || config.server?.host || 'localhost'
227226// Use process.cwd() instead of __dirname for Pear compatibility
228227// Pear sets cwd to the app bundle directory, but __dirname is empty
229const UI_DIR = path.join(process.cwd(), 'ui')
228const UI_DIR = path.join(__dirname, 'ui')
230229
231230// Track pending device add sessions
232231// Map<sessionId, { inviteCode, status, deviceName, error }>
……@@ -318,10 +317,7 @@ const ALLOWED_ORIGINS = [
318317 // 'https://your-production-domain.com'
319318]
320319
321// If running under Pear, add the Pear protocol origin
322if (isPear && typeof Pear !== 'undefined' && Pear.config?.key) {
323 ALLOWED_ORIGINS.push(`pear://${Pear.config.key}`)
324}
320// pear:// CORS origin no longer needed — apps run locally
325321
326322/**
327323 * Set CORS headers with origin whitelist
……@@ -1839,44 +1835,23 @@ const mimeTypes = {
18391835 '.svg': 'image/svg+xml'
18401836}
18411837
1842// Serve static files
1838// Serve static files from local filesystem
18431839async function serveStatic(req, res) {
18441840 const fileName = req.url === '/' ? 'index.html' : req.url.substring(1)
18451841
18461842 try {
18471843 let content
18481844
1849 if (appDrive) {
1850 // Running from pear:// - read from app bundle using pear-appdrive
1851 const bundlePath = '/ui/' + fileName
1852 console.log(`[STATIC] Reading from Pear bundle: ${bundlePath}`)
1853 content = await appDrive.get(bundlePath)
1845 const filePath = path.join(UI_DIR, fileName)
18541846
1855 if (!content) {
1856 console.error(`[STATIC] File not found in bundle: ${bundlePath}`)
1857 res.statusCode = 404
1858 res.end('Not found')
1859 return
1860 }
1861 } else {
1862 // Local development - read from filesystem
1863 const filePath = path.join(UI_DIR, fileName)
1864
1865 // Security: prevent directory traversal
1866 if (!filePath.startsWith(UI_DIR)) {
1867 console.error(`Security error: ${filePath} is outside ${UI_DIR}`)
1868 res.statusCode = 403
1869 res.end('Forbidden')
1870 return
1871 }
1847 // Security: prevent directory traversal
1848 if (!filePath.startsWith(UI_DIR)) {
1849 res.statusCode = 403
1850 res.end('Forbidden')
1851 return
1852 }
18721853
1873 // Check if file exists
1874 if (!fs.existsSync(filePath)) {
1875 console.error(`File not found: ${filePath}`)
1876 console.error(` UI_DIR: ${UI_DIR}`)
1877 console.error(` __dirname: ${__dirname}`)
1878 console.error(` process.cwd(): ${process.cwd()}`)
1879 console.error(` Requested URL: ${req.url}`)
1854 if (!fs.existsSync(filePath)) {
18801855
18811856 res.statusCode = 404
18821857 res.end('Not found')
……@@ -1903,22 +1878,8 @@ async function serveStatic(req, res) {
19031878// Server instance (created in startServer function)
19041879let server = null
19051880
1906// Start server function (wrapping prevents Pear port remapping)
1881// Start server function
19071882async function startServer() {
1908 // Initialize Pear appDrive for reading bundled UI files
1909 if (isPear && !appDrive) {
1910 try {
1911 // Use dynamic import for ESM module in CommonJS
1912 const AppDriveModule = await import('pear-appdrive')
1913 const AppDrive = AppDriveModule.default || AppDriveModule
1914 appDrive = new AppDrive()
1915 await appDrive.ready()
1916 console.log('[DEBUG] pear-appdrive initialized - can read bundled UI files')
1917 } catch (err) {
1918 console.error('[DEBUG] pear-appdrive failed:', err.message)
1919 console.error(' UI files will be served from filesystem (may fail in Pear)')
1920 }
1921 }
19221883
19231884 // Main HTTP server
19241885 server = http.createServer(async (req, res) => {