OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet / hyper-wallet.cjs

168 lines · javascript ref:
#!/usr/bin/env bare

/**
 * Hyper-Wallet - Unified Entry Point
 * 
 * Supports both CLI and Web Server modes:
 * - Web mode: bare hyper-wallet.cjs web [port]
 * - CLI mode: bare hyper-wallet.cjs <command> [args]
 * 
 * Pear compatible:
 * - pear run pear://hash web 3003
 * - pear run pear://hash init
 */

require('bare-node-runtime/global')
// WORKAROUND: Pear doesn't support import attributes with ESM yet
// Manually load imports.json into bundle for Bare 1.24/1.26 compatibility
require('bare-node-runtime/imports')

const fs = require('bare-fs')
const path = require('bare-path')
const { spawn } = require('bare-subprocess')

// Support both Pear runtime and bare/node
const isPear = typeof globalThis.Pear !== 'undefined'
const args = isPear ? (Pear.config.args || []) : (typeof Bare !== 'undefined' ? Bare.argv.slice(2) : process.argv.slice(2))

const mode = args[0]

// Show usage if no arguments
if (!mode) {
  showUsage()
  process.exit(0)
}

// Background OTA via pgh — content-addressed store, NO Hyperdrive/blobs.
// `pgh app update <name>` resolves the latest release from package.json's
// pgh.upgrade and ATOMICALLY materializes the new tree (safe while running;
// next launch uses it). Replaces the former pear-runtime Hyperdrive updater,
// which kept an unbounded append-only blob corestore under
// ~/.config/hyper-wallet/pear-runtime that grew on every release.
const _argv = typeof Bare !== 'undefined' ? Bare.argv : process.argv
if (!isPear && !_argv.includes('--no-updates')) {
  try {
    const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'))
    const upgrade = pkg.pgh && pkg.pgh.upgrade
    if (upgrade) {
      const os = require('bare-os')
      const appName = (pkg.pgh && pkg.pgh.bin) || pkg.name || 'hyper-wallet'
      // Resolve the pgh CLI: prefer ~/.local/bin/pgh, else the store-installed
      // bare runtime + entry. Bail if pgh isn't on this machine.
      let argv = null
      const launcher = path.join(os.homedir(), '.local', 'bin', 'pgh')
      if (fs.existsSync(launcher)) argv = [launcher]
      else {
        const storeRoot = process.env.PGH_STORE_ROOT || path.join(os.homedir(), '.local', 'share', 'store')
        const bareBin = path.join(storeRoot, 'apps', 'pgh', 'runtime', 'bare')
        const entry = path.join(storeRoot, 'apps', 'pgh', 'bin', 'pgh.bare.js')
        if (fs.existsSync(bareBin) && fs.existsSync(entry)) argv = [bareBin, entry]
      }
      if (argv) {
        // Detached + best-effort: never blocks the wallet command.
        const proc = spawn(argv[0], [...argv.slice(1), 'app', 'update', appName], {
          stdio: 'ignore',
          detached: true
        })
        proc.on('error', () => {})
        if (proc.unref) proc.unref()
      }
    }
  } catch {}
}

// Dispatch based on mode
if (mode === 'web') {
  // Web server mode
  // Check for --network flag (allow network access)
  const networkFlag = args.includes('--network')
  const port = args.find(arg => arg.match(/^\d+$/)) || process.env.PORT || '4004'

  startWebServer(port, networkFlag)
} else {
  // CLI mode - delegate to hyper-wallet-cli.cjs
  runCliCommand(args)
}

function showUsage() {
  console.log(`
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  hyper-wallet - Self-custodial P2P Cryptocurrency Wallet
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

USAGE:
  bare hyper-wallet.cjs <mode> [options]
  pear run pear://hash <mode> [options]

MODES:

  web [port] [--network]
                    Start web server (default: 4004)
                    Default: localhost only (secure)
                    --network: Allow network access (0.0.0.0)

                    Examples:
                    bare hyper-wallet.cjs web 3003
                    bare hyper-wallet.cjs web 3003 --network
                    pear run pear://hash web 3003

  CLI COMMANDS:
  init              Generate new wallet
  init --import     Import wallet from mnemonic
  whoami            Show identity and addresses
  balance [chain]   Check balances (all, btc, or eth)
  receive <chain>   Generate receiving address (btc or eth)
  send <chain> <address> <amount>   Send transaction
  history [chain]   View transaction history
  help              Show this help

EXAMPLES:

  # Start web server (localhost only - secure)
  bare hyper-wallet.cjs web
  bare hyper-wallet.cjs web 3003

  # Start web server with network access (for testing)
  bare hyper-wallet.cjs web 3003 --network
  pear run pear://hash web 4004 --network

  # CLI commands
  bare hyper-wallet.cjs init
  bare hyper-wallet.cjs whoami
  bare hyper-wallet.cjs balance btc
  bare hyper-wallet.cjs send btc tb1q... 0.001

  # With Pear
  pear run pear://hash web 3003
  pear run pear://hash init
  pear run pear://hash whoami

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
`)
}

async function startWebServer(port, allowNetwork = false) {
  console.log(`🚀 Starting hyper-wallet web server on port ${port}...`)
  if (allowNetwork) {
    console.log('⚠️  Network access enabled (--network flag)')
  }
  console.log('')

  // Load and start wallet-server.cjs
  // Set PORT and HOST env vars for wallet-server to use
  process.env.PORT = port
  if (allowNetwork) {
    process.env.WALLET_HOST = '0.0.0.0'
  }

  // Require the wallet server module and call its startServer function
  const { startServer } = require('./wallet-server.cjs')
  await startServer()
}

function runCliCommand(args) {
  // Delegate to hyper-wallet-advanced-cli.cjs CLI handler
  // Provides full feature set: device management, daemon mode, UTXO management, etc.
  require('./hyper-wallet-advanced-cli.cjs')
}