import { homedir } from 'node:os'; import { join, dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const BIN_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', 'bin', 'weave.js'); export function serviceOptions(flags = {}, { home = homedir(), cwd = process.cwd(), nodePath = process.execPath } = {}) { const port = Number(flags.port ?? 4400); const label = flags.label && flags.label !== true ? String(flags.label) : `ai.grunion.weave.${port}`; const dataPath = flags.data && flags.data !== true ? resolve(cwd, String(flags.data)) : join(home, '.weave', 'workspace.json'); return { port, label, dataPath, nodePath, binPath: BIN_PATH, plistPath: join(home, 'Library', 'LaunchAgents', `${label}.plist`), logPath: join(home, 'Library', 'Logs', 'weave', `${label}.log`), }; } const escapeXml = (s) => String(s).replace(/&/g, '&').replace(//g, '>'); export function buildPlist({ label, port, dataPath, nodePath, binPath, logPath }) { const args = [nodePath, binPath, 'serve', '--port', String(port), '--data', dataPath]; return ` Label ${escapeXml(label)} ProgramArguments ${args.map((a) => ` ${escapeXml(a)}`).join('\n')} RunAtLoad KeepAlive StandardOutPath ${escapeXml(logPath)} StandardErrorPath ${escapeXml(logPath)} `; } export function parseLaunchctlPrint(text) { if (!text || !String(text).trim()) return { loaded: false }; const s = String(text); const grab = (re) => s.match(re)?.[1] ?? null; const state = grab(/^\s*state = (.+)$/m); const pid = grab(/^\s*pid = (\d+)$/m); const exit = grab(/^\s*last exit code = (\d+)$/m); return { loaded: true, state, pid: pid == null ? null : Number(pid), lastExitCode: exit == null ? null : Number(exit), }; } export async function probeHealth(port, { fetchImpl = fetch, timeoutMs = 2000 } = {}) { try { const res = await fetchImpl(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(timeoutMs) }); const payload = await res.json(); return { reachable: true, ...payload }; } catch { return { reachable: false }; } } export function buildStatus({ label, port, plistPath, plistInstalled, launchctl, health, localVersion }) { const reachable = health?.reachable === true && health?.ok !== false; const versionStale = reachable && localVersion != null && health.version != null && health.version !== localVersion; const buildStale = reachable && health.stale === true; const stale = versionStale || buildStale; let summary; if (reachable) { if (versionStale) summary = `running (stale: server v${health.version}, local v${localVersion} — restart to update)`; else if (buildStale) summary = `running (stale: server booted at ${health.sha}, the checkout it serves is at ${health.diskSha} — restart to update)`; else if (!plistInstalled) summary = 'running (unmanaged — run `weave service install` to auto-start on login)'; else summary = 'running'; } else if (plistInstalled && launchctl?.loaded) { summary = `installed but not responding on port ${port}`; } else if (plistInstalled) { summary = 'installed but not loaded (launchctl)'; } else { summary = 'not installed'; } return { label, port, summary, plist: { installed: Boolean(plistInstalled), path: plistPath }, launchctl: launchctl ?? { loaded: false }, server: { reachable: Boolean(health?.reachable), ok: health?.ok ?? null, version: health?.version ?? null, workspace: health?.workspace ?? null, startedAt: health?.startedAt ?? null, uptime: health?.uptime ?? null, sha: health?.sha ?? null, diskSha: health?.diskSha ?? null, stale, }, }; }