FOUNDRY
C8 Platform
← Tasks

[LIB-04] lib/quilt.ts — Quilt REST client for sibling list + heartbeat (retry 1)

completedcode_genP1

Description

## LIB-04: lib/quilt.ts — Quilt REST client for sibling list + heartbeat Section: Client Libraries | Milestone: M3 | Owner: AG Lu ### Failing Tests - **quilt_lib** (file_exists): File `c8-audio/lib/quilt.ts` must exist - **supabase_dep** (grep): Pattern `supabase` in `c8-audio/lib/quilt.ts` (need 1+ matches) - **sibling_ops** (grep): Pattern `sibling|heartbeat` in `c8-audio/lib/quilt.ts` (need 1+ matches) ### Acceptance Criteria - File `c8-audio/lib/quilt.ts` must exist - File matching `c8-audio/lib/quilt.ts` must contain pattern `supabase` - File matching `c8-audio/lib/quilt.ts` must contain pattern `sibling|heartbeat` ### Codebase Conventions - Trilingual comments: // EN: ... // VI: ... // ES: ... - SPEC section refs in comments - Zod at module level with .safeParse() - (supabase as any) with eslint-disable - NextResponse.json() responses - Error format: { error: 'message' } ### Dependencies - LIB-01: FAIL - LIB-02: FAIL
Assignee
-
Claimed By
Cloud Lu → Claude Sonnet
Created
1d ago
Completed
1d ago

Quality

Overall
100%
Pass
PASS
Graded By
-
Graded At
-

Cost

Input Tokens
-
Output Tokens
-
Est. USD
-

Result

{
  "cost": {
    "input_tokens": 378,
    "estimated_usd": 0.032184,
    "output_tokens": 2070
  },
  "model": "claude-sonnet-4-20250514",
  "status": "success",
  "transport": "anthropic_api",
  "latency_ms": 26737,
  "response_text": "I'll create the Quilt REST client for sibling list and heartbeat functionality.\n\n```typescript\n// c8-audio/lib/quilt.ts\n// EN: Quilt REST client for sibling list + heartbeat\n// VI: Client REST Quilt cho danh sách sibling + heartbeat  \n// ES: Cliente REST Quilt para lista de hermanos + heartbeat\n// SPEC: M3-quilt-sibling-discovery\n\nimport { createClient } from '@supabase/supabase-js'\nimport { z } from 'zod'\n\n// EN: Zod schemas for validation\n// VI: Schema Zod để xác thực\n// ES: Esquemas Zod para validación\nconst SiblingSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n  url: z.string().url(),\n  last_heartbeat: z.string().datetime(),\n  status: z.enum(['online', 'offline', 'unknown'])\n})\n\nconst HeartbeatSchema = z.object({\n  sibling_id: z.string(),\n  timestamp: z.string().datetime(),\n  metadata: z.record(z.any()).optional()\n})\n\nconst SiblingsListSchema = z.array(SiblingSchema)\n\nexport type Sibling = z.infer<typeof SiblingSchema>\nexport type HeartbeatData = z.infer<typeof HeartbeatSchema>\n\nexport class QuiltClient {\n  private supabase: any\n  \n  constructor(supabaseUrl: string, supabaseKey: string) {\n    // EN: Initialize Supabase client for Quilt operations\n    // VI: Khởi tạo client Supabase cho các thao tác Quilt\n    // ES: Inicializar cliente Supabase para operaciones Quilt\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    this.supabase = createClient(supabaseUrl, supabaseKey) as any\n  }\n\n  // EN: Get list of all sibling nodes\n  // VI: Lấy danh sách tất cả các node sibling\n  // ES: Obtener lista de todos los nodos hermanos\n  async getSiblings(): Promise<{ data?: Sibling[], error?: string }> {\n    try {\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      const { data, error } = await (this.supabase as any)\n        .from('siblings')\n        .select('*')\n        .order('last_heartbeat', { ascending: false })\n\n      if (error) {\n        return { error: `Failed to fetch siblings: ${error.message}` }\n      }\n\n      const parseResult = SiblingsListSchema.safeParse(data)\n      if (!parseResult.success) {\n        return { error: 'Invalid sibling data format' }\n      }\n\n      return { data: parseResult.data }\n    } catch (err) {\n      return { error: `Sibling fetch error: ${err}` }\n    }\n  }\n\n  // EN: Send heartbeat for current sibling\n  // VI: Gửi heartbeat cho sibling hiện tại\n  // ES: Enviar heartbeat para hermano actual\n  async sendHeartbeat(siblingId: string, metadata?: Record<string, any>): Promise<{ success?: boolean, error?: string }> {\n    try {\n      const heartbeatData = {\n        sibling_id: siblingId,\n        timestamp: new Date().toISOString(),\n        metadata\n      }\n\n      const parseResult = HeartbeatSchema.safeParse(heartbeatData)\n      if (!parseResult.success) {\n        return { error: 'Invalid heartbeat data format' }\n      }\n\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      const { error } = await (this.supabase as any)\n        .from('heartbeats')\n        .insert([parseResult.data])\n\n      if (error) {\n        return { error: `Failed to send heartbeat: ${error.message}` }\n      }\n\n      // EN: Update sibling last_heartbeat timestamp\n      // VI: Cập nhật timestamp last_heartbeat của sibling\n      // ES: Actualizar timestamp last_heartbeat del hermano\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      await (this.supabase as any)\n        .from('siblings')\n        .update({ \n          last_heartbeat: heartbeatData.timestamp,\n          status: 'online'\n        })\n        .eq('id', siblingId)\n\n      return { success: true }\n    } catch (err) {\n      return { error: `Heartbeat send error: ${err}` }\n    }\n  }\n\n  // EN: Register new sibling node\n  // VI: Đăng ký node sibling mới\n  // ES: Registrar nuevo nodo hermano\n  async registerSibling(name: string, url: string): Promise<{ data?: Sibling, error?: string }> {\n    try {\n      const siblingData = {\n        id: crypto.randomUUID(),\n        name,\n        url,\n        last_heartbeat: new Date().toISOString(),\n        status: 'online' as const\n      }\n\n      const parseResult = SiblingSchema.safeParse(siblingData)\n      if (!parseResult.success) {\n        return { error: 'Invalid sibling registration data' }\n      }\n\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      const { data, error } = await (this.supabase as any)\n        .from('siblings')\n        .insert([parseResult.data])\n        .select()\n        .single()\n\n      if (error) {\n        return { error: `Failed to register sibling: ${error.message}` }\n      }\n\n      return { data }\n    } catch (err) {\n      return { error: `Sibling registration error: ${err}` }\n    }\n  }\n\n  // EN: Check heartbeat status of siblings\n  // VI: Kiểm tra trạng thái heartbeat của các sibling\n  // ES: Verificar estado heartbeat de hermanos\n  async checkSiblingHeartbeats(): Promise<{ updated?: number, error?: string }> {\n    try {\n      const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString()\n\n      // EN: Mark siblings as offline if no recent heartbeat\n      // VI: Đánh dấu siblings là offline nếu không có heartbeat gần đây\n      // ES: Marcar hermanos como offline si no hay heartbeat reciente\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      const { count, error } = await (this.supabase as any)\n        .from('siblings')\n        .update({ status: 'offline' })\n        .lt('last_heartbeat', fiveMinutesAgo)\n        .neq('status', 'offline')\n\n      if (error) {\n        return { error: `Failed to update sibling status: ${error.message}` }\n      }\n\n      return { updated: count || 0 }\n    } catch (err) {\n      return { error: `Heartbeat check error: ${err}` }\n    }\n  }\n}\n\n// EN: Default export for convenience\n// VI: Export mặc định để tiện sử dụng\n// ES: Export por defecto para conveniencia\nexport default QuiltClient\n```\n\nThis implementation provides:\n\n1. **QuiltClient class** with Supabase integration for sibling discovery and heartbeat management\n2. **Zod validation schemas** for type safety with `.safeParse()`\n3. **Sibling operations**: `getSiblings()`, `registerSibling()`, `checkSiblingHeartbeats()`\n4. **Heartbeat functionality**: `sendHeartbeat()` with metadata support\n5. **Trilingual comments** following the established convention\n6. **Error handling** with consistent `{ error: 'message' }` format\n7. **TypeScript types** exported for use in other modules\n8. **ESLint disable comments** for `(supabase as any)` usage\n\nThe client handles the core Quilt network functionality for M3 milestone sibling discovery and heartbeat monitoring."
}
Task ID: 448cb181-3be8-46b0-b0a3-b6d5cc1bbe49