#!/usr/bin/env npx tsx /** * Orchestrate the full seed pipeline for StemeDB demo. * * This script: * 1. Waits for API health * 2. Runs whitepaper seed script * 3. Runs external sources seed script * 4. Verifies key claims via SkepticLens * * Usage: * npx tsx scripts/seed-all.ts * npx tsx scripts/seed-all.ts --dry-run * npx tsx scripts/seed-all.ts --verify-only * * Environment: * STEMEDB_API_URL - API base URL (default: http://127.0.0.1:18180) */ import { execSync } from "child_process"; const API_URL = process.env.STEMEDB_API_URL || "http://127.0.0.1:18180"; // ============================================================================ // Types // ============================================================================ interface SkepticResponse { subject: string; predicate: string; status: "Unanimous" | "Agreed" | "Contested"; conflict_score: number; claims: Array<{ value: { type: string; value: string | number | boolean }; weight_share: number; assertion_count: number; }>; candidates_count: number; } // ============================================================================ // Health Check // ============================================================================ async function waitForHealth(maxRetries = 30, delayMs = 2000): Promise { console.log(`Waiting for API at ${API_URL}...`); for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(`${API_URL}/v1/health`); if (response.ok) { const data = await response.json(); console.log(`API is healthy: v${data.version}, ${data.assertions_count} assertions`); return true; } } catch { // Retry } if (i < maxRetries - 1) { console.log(` Retry ${i + 1}/${maxRetries}...`); await new Promise((resolve) => setTimeout(resolve, delayMs)); } } console.error(`API not healthy after ${maxRetries} retries`); return false; } // ============================================================================ // Verification // ============================================================================ interface VerificationTarget { subject: string; predicate: string; expectedStatus?: "Unanimous" | "Agreed" | "Contested"; description: string; } const VERIFICATION_TARGETS: VerificationTarget[] = [ // Core StemeDB claims { subject: "StemeDB", predicate: "hash_algorithm", expectedStatus: "Unanimous", description: "BLAKE3 hash algorithm", }, { subject: "StemeDB", predicate: "storage_model", expectedStatus: "Agreed", description: "Append-only Merkle DAG", }, { subject: "RecencyLens", predicate: "time_complexity", expectedStatus: "Unanimous", description: "O(n) complexity", }, // Cryptography claims { subject: "BLAKE3", predicate: "output_size", expectedStatus: "Unanimous", description: "256-bit output", }, { subject: "Ed25519", predicate: "signature_size", expectedStatus: "Unanimous", description: "64 bytes", }, // Potential conflicts { subject: "CRDT", predicate: "preserves_disagreement", description: "CRDT disagreement preservation (potential conflict)", }, { subject: "PostgreSQL", predicate: "conflict_resolution", description: "PostgreSQL conflict handling (potential conflict)", }, { subject: "EigenTrust", predicate: "initial_trust_score", description: "Trust score initialization (potential conflict)", }, // Database comparisons { subject: "PostgreSQL", predicate: "storage_model", description: "PostgreSQL storage model", }, { subject: "MongoDB", predicate: "storage_model", description: "MongoDB storage model", }, ]; async function verifySkeptic(target: VerificationTarget): Promise { const url = `${API_URL}/v1/skeptic?subject=${encodeURIComponent(target.subject)}&predicate=${encodeURIComponent(target.predicate)}&include_source_metadata=true`; try { const response = await fetch(url); if (!response.ok) { console.log(` [SKIP] ${target.subject}/${target.predicate}: No data`); return true; // Not a failure, just no data } const data: SkepticResponse = await response.json(); const statusIcon = data.status === "Unanimous" ? "[UNANIMOUS]" : data.status === "Agreed" ? "[AGREED] " : "[CONTESTED]"; const expectedMatch = !target.expectedStatus || data.status === target.expectedStatus; const icon = expectedMatch ? "OK" : "!!"; console.log( ` [${icon}] ${statusIcon} ${target.subject}/${target.predicate}: ` + `${data.claims.length} claims, conflict=${data.conflict_score.toFixed(2)}` ); if (data.claims.length > 0) { const topClaim = data.claims[0]; console.log(` Top: "${String(topClaim.value.value).slice(0, 40)}..." (${(topClaim.weight_share * 100).toFixed(0)}%)`); } return expectedMatch; } catch (error) { console.error(` [ERR] ${target.subject}/${target.predicate}: ${error}`); return false; } } async function runVerification(): Promise { console.log("\n=== Verification via SkepticLens ===\n"); let passed = 0; let failed = 0; let skipped = 0; for (const target of VERIFICATION_TARGETS) { const result = await verifySkeptic(target); if (result) { passed++; } else { failed++; } } console.log(`\nVerification: ${passed} passed, ${failed} failed, ${skipped} skipped`); return failed === 0; } // ============================================================================ // Script Execution // ============================================================================ function runScript(scriptName: string, dryRun: boolean): void { const args = dryRun ? "--dry-run" : ""; const command = `npx tsx scripts/${scriptName} ${args}`; console.log(`\n${"=".repeat(60)}`); console.log(`Running: ${scriptName}${dryRun ? " (dry run)" : ""}`); console.log("=".repeat(60) + "\n"); try { execSync(command, { stdio: "inherit", cwd: process.cwd(), env: { ...process.env, STEMEDB_API_URL: API_URL }, }); } catch (error) { console.error(`Script ${scriptName} failed:`, error); throw error; } } // ============================================================================ // Main // ============================================================================ async function main(): Promise { const args = process.argv.slice(2); const dryRun = args.includes("--dry-run") || args.includes("-d"); const verifyOnly = args.includes("--verify-only") || args.includes("-v"); console.log("StemeDB Full Seed Pipeline"); console.log("=========================="); console.log(`API URL: ${API_URL}`); if (dryRun) console.log("Mode: DRY RUN"); if (verifyOnly) console.log("Mode: VERIFY ONLY"); console.log(); // Health check const healthy = await waitForHealth(); if (!healthy) { console.error("\nAPI is not available. Please start stemedb-api first:"); console.error(" cargo run --bin stemedb-api"); process.exit(1); } console.log(); if (!verifyOnly) { // Phase 1: Whitepaper claims runScript("seed-whitepaper.ts", dryRun); // Phase 2: External sources runScript("seed-external.ts", dryRun); if (!dryRun) { // Wait for materialization console.log("\nWaiting for final materialization..."); await new Promise((resolve) => setTimeout(resolve, 3000)); } } // Phase 3: Verification if (!dryRun) { const verificationPassed = await runVerification(); if (!verificationPassed) { console.warn("\nSome verifications failed. Check the output above."); } } console.log("\n" + "=".repeat(60)); console.log("Seed pipeline complete!"); console.log("=".repeat(60)); console.log("\nNext steps:"); console.log(" 1. Start the community app: cd community && npm run dev"); console.log(" 2. Visit http://localhost:18187"); console.log(" 3. Click on annotated claims to see conflict analysis"); } main().catch((error) => { console.error("Pipeline failed:", error.message); process.exit(1); });