OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-proto-peer / 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, deliberately. This file was copied from atproto-bridge, which uses the
// `#node:*` import-map indirection — hyper-proto-peer has NO `imports` map, so those specifiers throw
// PACKAGE_IMPORT_NOT_DEFINED at runtime. The failure was invisible because registration is wrapped in a
// non-fatal try/catch: the daemon booted fine and simply never registered.
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) }
}