repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
The Living OS cockpit
stars
latest
clone command
git clone gitlawb://did:key:z6Mku78K...XywC/living-os-cockp...git clone gitlawb://did:key:z6Mku78K.../living-os-cockp...59751530feat: surface worker supervisor health in live work8h ago| #1 | import { NextResponse } from 'next/server'; |
| #2 | import { promises as fs } from 'fs'; |
| #3 | import { execFile } from 'child_process'; |
| #4 | import { promisify } from 'util'; |
| #5 | |
| #6 | export const runtime = 'nodejs'; |
| #7 | |
| #8 | const execFileAsync = promisify(execFile); |
| #9 | const BALANCE_CACHE_MS = 120_000; |
| #10 | |
| #11 | interface BalanceSnapshot { |
| #12 | ok: boolean; |
| #13 | fetchedAt: string; |
| #14 | walletAddress?: string; |
| #15 | walletUsdc?: number; |
| #16 | depositsAvailable?: number; |
| #17 | depositsReserved?: number; |
| #18 | totalAvailableUsdc?: number; |
| #19 | error?: string; |
| #20 | } |
| #21 | |
| #22 | let cachedBalance: BalanceSnapshot | null = null; |
| #23 | let cachedBalanceAt = 0; |
| #24 | |
| #25 | function parseUsdc(stdout: string, label: string): number | undefined { |
| #26 | const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
| #27 | const match = stdout.match(new RegExp(`${escaped}:\\s*([0-9]+(?:\\.[0-9]+)?)\\s*USDC`, 'i')); |
| #28 | return match ? Number.parseFloat(match[1]) : undefined; |
| #29 | } |
| #30 | |
| #31 | async function readBuyerBalance(): Promise<BalanceSnapshot> { |
| #32 | const now = Date.now(); |
| #33 | if (cachedBalance && now - cachedBalanceAt < BALANCE_CACHE_MS) { |
| #34 | return cachedBalance; |
| #35 | } |
| #36 | |
| #37 | try { |
| #38 | const { stdout } = await execFileAsync('/usr/bin/antseed', ['buyer', 'balance'], { |
| #39 | timeout: 2_000, |
| #40 | maxBuffer: 64 * 1024, |
| #41 | }); |
| #42 | const walletAddress = stdout.match(/Wallet:\s*(\S+)/)?.[1]; |
| #43 | const walletUsdc = parseUsdc(stdout, 'USDC Balance (wallet)'); |
| #44 | const depositsAvailable = parseUsdc(stdout, 'Available'); |
| #45 | const depositsReserved = parseUsdc(stdout, 'Reserved'); |
| #46 | const snapshot: BalanceSnapshot = { |
| #47 | ok: true, |
| #48 | fetchedAt: new Date(now).toISOString(), |
| #49 | walletAddress, |
| #50 | walletUsdc, |
| #51 | depositsAvailable, |
| #52 | depositsReserved, |
| #53 | totalAvailableUsdc: |
| #54 | (walletUsdc ?? 0) + (depositsAvailable ?? 0) + (depositsReserved ?? 0), |
| #55 | }; |
| #56 | cachedBalance = snapshot; |
| #57 | cachedBalanceAt = now; |
| #58 | return snapshot; |
| #59 | } catch (e: any) { |
| #60 | const snapshot: BalanceSnapshot = { |
| #61 | ok: false, |
| #62 | fetchedAt: new Date(now).toISOString(), |
| #63 | error: e?.message ?? 'balance check failed', |
| #64 | }; |
| #65 | cachedBalance = snapshot; |
| #66 | cachedBalanceAt = now; |
| #67 | return snapshot; |
| #68 | } |
| #69 | } |
| #70 | |
| #71 | export async function GET() { |
| #72 | try { |
| #73 | const [raw, buyerBalance] = await Promise.all([ |
| #74 | fs.readFile('/home/kingbau/.antseed/daemon.state.json', 'utf-8'), |
| #75 | readBuyerBalance(), |
| #76 | ]); |
| #77 | const state = JSON.parse(raw); |
| #78 | const activeChannels = state.activeChannels ?? 0; |
| #79 | return NextResponse.json({ |
| #80 | state: state.state ?? 'unknown', |
| #81 | peerCount: state.peerCount ?? 0, |
| #82 | activeChannels, |
| #83 | channelStatus: activeChannels > 0 ? 'active' : 'idle', |
| #84 | tokensToday: state.tokensToday ?? 0, |
| #85 | earningsToday: state.earningsToday ?? '0', |
| #86 | uptime: state.uptime ?? '—', |
| #87 | proxyPort: state.proxyPort, |
| #88 | provider: state.provider, |
| #89 | pricing: state.providerPricing ?? null, |
| #90 | buyerBalance, |
| #91 | interpretation: |
| #92 | state.state === 'seeding' && activeChannels === 0 |
| #93 | ? 'Seller online; no active buyer payment channels at this moment.' |
| #94 | : undefined, |
| #95 | }); |
| #96 | } catch (e: any) { |
| #97 | return NextResponse.json({ state: 'offline', error: e?.message }); |
| #98 | } |
| #99 | } |
| #100 |