#!/usr/bin/env bare
// desktop-app-working.cjs - Correct Pear desktop architecture:
// 1. pear-bridge serves UI files
// 2. pear-pipe handles IPC (UI <-> main process)
// 3. Main process forwards IPC to HTTP server (avoiding cross-origin)
require('bare-node-runtime/global')
require('bare-node-runtime/imports')
const fs = require('bare-fs')
const path = require('bare-path')
const Runtime = require('pear-electron')
const Bridge = require('pear-bridge')
const http = require('bare-http1')
// Import the wallet server
const { startServer } = require('./wallet-server.cjs')
// Initialize pear-runtime for P2P OTA updates (non-blocking)
try {
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'))
if (pkg.upgrade) {
import('pear-runtime').then(({ default: PearRuntime }) => {
const os = require('bare-os')
const p = os.platform()
const name = 'hyper-wallet-desktop'
let dir
if (p === 'darwin') dir = path.join(os.homedir(), 'Library', 'Application Support', name)
else if (p === 'win32') dir = path.join(os.homedir(), 'AppData', 'Roaming', name)
else dir = path.join(os.homedir(), '.config', name)
const runtime = new PearRuntime({ dir, app: __dirname, version: pkg.version || '1.1.0', upgrade: pkg.upgrade })
runtime.on('updated', () => { runtime.applyUpdate().catch(() => {}) })
}).catch(() => {})
}
} catch {}
async function main() {
console.log('🚀 Hyper Wallet Desktop starting...')
// 1. Start HTTP API server (internal use only)
console.log('📡 Starting wallet HTTP server...')
await startServer()
console.log('✓ HTTP server ready on http://localhost:4004')
// 2. Initialize pear-bridge to serve UI files
console.log('🌉 Starting pear-bridge for UI files...')
const bridge = new Bridge({ mount: 'desktop' })
await bridge.ready()
console.log('✓ pear-bridge ready (serving desktop/ files)')
// 3. Initialize pear-electron runtime
const runtime = new Runtime()
console.log('✓ pear-electron runtime ready')
// 4. Start Electron window loading UI from bridge
console.log('🪟 Opening desktop window...')
const pipe = await runtime.start({
bridge,
width: 1200,
height: 800
})
console.log('✓ Desktop window opened')
// 5. Set up IPC handler to receive requests from renderer
console.log('📞 Setting up IPC handler...')
let buffer = ''
pipe.on('data', (data) => {
buffer += data.toString()
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (!line.trim()) continue
try {
const request = JSON.parse(line)
console.log(`[IPC] Received request ${request.id}: ${request.method} ${request.url}`)
// Forward to HTTP server
forwardToHTTP(request)
.then(response => {
const responseStr = JSON.stringify(response) + '\n'
console.log(`[IPC] Sending response ${response.id}: ${response.status}`)
console.log(`[IPC] Response payload:`, responseStr.substring(0, 200))
const written = pipe.write(responseStr)
console.log(`[IPC] pipe.write() returned:`, written)
})
.catch(err => {
console.error(`[IPC] Error forwarding request ${request.id}:`, err)
const errorResponse = {
id: request.id,
status: 500,
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ error: err.message })
}
const errorStr = JSON.stringify(errorResponse) + '\n'
console.log(`[IPC] Sending error response:`, errorStr.substring(0, 200))
pipe.write(errorStr)
})
} catch (err) {
console.error('[IPC] Error parsing request:', err)
}
}
})
pipe.on('error', (err) => {
console.error('[IPC] Pipe error:', err)
})
console.log('✓ IPC handler ready')
console.log('🎉 Hyper Wallet Desktop ready!')
console.log(' UI: pear://dev (via pear-bridge)')
console.log(' API: IPC pipe → localhost:4004')
// 6. Handle cleanup
Pear.teardown(async () => {
console.log('🛑 Shutting down...')
pipe.end()
console.log('✓ Cleanup complete')
})
}
// Forward IPC request to HTTP server
async function forwardToHTTP(request) {
return new Promise((resolve, reject) => {
const options = {
hostname: 'localhost',
port: 4004,
path: request.url,
method: request.method,
headers: request.headers || {}
}
const req = http.request(options, (res) => {
let data = ''
res.on('data', (chunk) => { data += chunk })
res.on('end', () => {
resolve({
id: request.id,
status: res.statusCode,
headers: res.headers,
body: data
})
})
})
req.on('error', (err) => {
reject(err)
})
if (request.body) {
req.write(typeof request.body === 'string' ? request.body : JSON.stringify(request.body))
}
req.end()
})
}
main().catch(err => {
console.error('Fatal error:', err)
process.exit(1)
})