OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet-desktop / index.cjs

166 lines · javascript ref:
#!/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)
})