OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-inspector / lib / scanner.js

111 lines · javascript ref:
import { execSync } from 'child_process'
import { existsSync, readdirSync, statSync } from 'fs'
import { join } from 'path'

// Known Hypercore storage locations under a home directory
const KNOWN_PATHS = [
  '.config/pear/corestores',
  '.config/pear/app-storage',
  '.config/pear/by-dkey',
  '.config/Holepunch',
  '.config/hypernote',
  '.config/keet-cli',
  '.config/keet-daemon',
  '.config/godot-keet',
  '.config/hyper-tps',
  '.config/agregore-browser/hyper',
  '.config/Beaker Browser',
  '.config/manyverse',
  '.config/holepunch-runtime',
  '.config/pear-runtime',
  '.config/hyper-social-web'
]

export function findCorestores (homeDir) {
  const results = []

  // Check known paths
  for (const rel of KNOWN_PATHS) {
    const full = join(homeDir, rel)
    if (existsSync(full)) {
      const size = getDirSize(full)
      results.push({ path: full, size, type: 'known', rel })
    }
  }

  // Find CORESTORE marker files
  try {
    const output = execSync(
      `find ${homeDir} -maxdepth 5 -name "CORESTORE" -type f 2>/dev/null`,
      { encoding: 'utf8', timeout: 30000 }
    )
    for (const line of output.trim().split('\n').filter(Boolean)) {
      const dir = line.replace(/\/CORESTORE$/, '')
      // Check if already covered by known paths
      const isKnown = results.some(r => dir.startsWith(r.path))
      if (!isKnown) {
        const size = getDirSize(dir)
        results.push({ path: dir, size, type: 'discovered', marker: line })
      }
    }
  } catch {}

  // Sort by size descending
  results.sort((a, b) => b.size - a.size)

  return results
}

export function getDirSize (dirPath) {
  try {
    const output = execSync(`du -sb "${dirPath}" 2>/dev/null`, { encoding: 'utf8', timeout: 30000 })
    return parseInt(output.split('\t')[0]) || 0
  } catch {
    return 0
  }
}

export function getLastModified (dirPath) {
  try {
    const stat = statSync(dirPath)
    return stat.mtime
  } catch {
    return null
  }
}

export function isLocked (dbPath) {
  try {
    const lockFile = join(dbPath, 'LOCK')
    if (!existsSync(lockFile)) return false
    const output = execSync(`lsof "${lockFile}" 2>/dev/null`, { encoding: 'utf8', timeout: 5000 })
    return output.trim().length > 0
  } catch {
    return false
  }
}

export function getLockHolder (dbPath) {
  try {
    const lockFile = join(dbPath, 'LOCK')
    const output = execSync(`lsof "${lockFile}" 2>/dev/null`, { encoding: 'utf8', timeout: 5000 })
    const lines = output.trim().split('\n')
    if (lines.length > 1) {
      const parts = lines[1].split(/\s+/)
      return { command: parts[0], pid: parts[1] }
    }
  } catch {}
  return null
}

export function hasRocksDB (dirPath) {
  const dbPath = join(dirPath, 'db')
  if (existsSync(dbPath)) {
    try {
      const files = readdirSync(dbPath)
      return files.some(f => f.endsWith('.sst') || f.endsWith('.blob'))
    } catch {}
  }
  return false
}