OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web / lib / sidecar.js

61 lines · javascript ref:
// pgh-sidecar registration — makes the daemon a first-class pgh app. Registering our UDS
// with the sidecar lets `app-lookup` resolve us (so `pgh app status atproto-bridge` and the
// readiness probe work) and survives the boot race (sidecar socket may appear after us).
// Mirrors pear-git-v3/lib/sidecar-wait.js + server.js registerWithSidecar.

// ⚠ BARE-NATIVE IMPORTS. Copied from atproto-bridge, which resolves `#node:*` through an "imports" map
// in its package.json — favela-web has none, so those specifiers throw PACKAGE_IMPORT_NOT_DEFINED. The
// failure is INVISIBLE because the caller treats a missing control surface as non-fatal, so the daemon
// boots normally and simply never registers. Exactly the same trap was hit in hyper-proto-peer 0.3.102.
import fs from 'bare-fs'
import path from 'bare-path'
import os from 'bare-os'
import process from 'bare-process'

export function sidecarSockPath () {
  return process.env.PGH_SIDECAR_SOCK || path.join(os.homedir(), '.pgh', 'svc.sock')
}

// Poll for the sidecar socket (it may be created after this daemon boots — the boot race).
export async function waitForSidecarSock ({ timeoutMs = 120000, intervalMs = 1000, shouldStop = () => false } = {}) {
  const sock = sidecarSockPath()
  const deadline = Date.now() + timeoutMs
  while (!fs.existsSync(sock)) {
    if (shouldStop()) return false
    if (Date.now() >= deadline) return false
    await new Promise((r) => setTimeout(r, intervalMs))
  }
  return true
}

// app-register (last-writer-wins, idempotent) our live UDS + pid with the sidecar.
export async function registerWithSidecar ({ udsPath, name, runtime = 'bare', version = null }) {
  const sidecarSock = sidecarSockPath()
  if (!fs.existsSync(sidecarSock)) return { registered: false, reason: 'sidecar not running' }
  const installName = name || process.env.PGH_APP_NAME || 'atproto-bridge'
  try {
    const { Client } = await import('hyper-svc')
    const c = await Client.connectUds(sidecarSock)
    await c.call('app-register', { name: installName, udsPath, pid: process.pid, runtime, version })
    await c.close()
    return { registered: true, name: installName }
  } catch (err) {
    return { registered: false, reason: err.message }
  }
}

// Register once the sidecar is up, then re-register on an interval (the sidecar keeps its
// registry in memory, so a sidecar restart silently drops us). Returns a stop() fn.
export function maintainSidecarRegistration ({ udsPath, name, runtime, version, intervalMs = 30000 } = {}) {
  let timer = null
  let stopped = false
  ;(async () => {
    const ready = await waitForSidecarSock({ shouldStop: () => stopped })
    if (stopped) return
    if (ready) await registerWithSidecar({ udsPath, name, runtime, version })
    timer = setInterval(() => { registerWithSidecar({ udsPath, name, runtime, version }) }, intervalMs)
    if (timer.unref) timer.unref()
  })()
  return () => { stopped = true; if (timer) clearInterval(timer) }
}