/** * APIX Discovery Demo — for OpenClaw * * Shows how an agent finds and uses services without hardcoded URLs. * This is what ClawHub does manually. APIX makes it autonomous. * * Requirements: Node 18+ (native fetch, no dependencies) * Run: node apix-demo.mjs */ const REGISTRY_ROOT = 'https://api-index.org'; const SANDBOX_REGISTER = `${REGISTRY_ROOT}/sandbox/register`; // ─── Helpers ────────────────────────────────────────────────────────────────── async function get(url) { const res = await fetch(url, { headers: { Accept: 'application/json' } }); if (!res.ok) throw new Error(`GET ${url} → ${res.status}`); return res.json(); } async function post(url, body, headers = {}) { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', ...headers }, body: JSON.stringify(body), }); if (!res.ok) { const text = await res.text(); throw new Error(`POST ${url} → ${res.status}: ${text}`); } return res.json(); } function line(char = '─', n = 60) { return char.repeat(n); } function hdr(label) { console.log(`\n${line()}\n ${label}\n${line()}`); } // ─── Demo ───────────────────────────────────────────────────────────────────── async function main() { console.log('\n APIX Discovery Demo'); console.log(' The open agent service registry — api-index.org\n'); // ── Step 1: HATEOAS root ─────────────────────────────────────────────────── hdr('Step 1 Discover the registry root'); const root = await get(REGISTRY_ROOT); console.log(` Registry: ${root.name}`); console.log(` Services registered: ${root.stats.registeredServices}`); console.log(` Navigation links:`); for (const [rel, link] of Object.entries(root._links)) { console.log(` ${rel.padEnd(20)} ${link.href}`); } console.log('\n → An agent starts here. No hardcoded service URLs anywhere.'); // ── Step 2: Create sandbox ───────────────────────────────────────────────── hdr('Step 2 Create a sandbox namespace (self-service, instant)'); const reg = await post(SANDBOX_REGISTER, { name: 'openclaw-apix-demo', contactEmail: 'demo@openclaw.dev', location: 'Vienna, AT', }); const { sandboxId, apiKey, tier, ratePerMinute, expiresAt, _links: sLinks } = reg; console.log(` Sandbox ID : ${sandboxId}`); console.log(` Tier : ${tier} | Rate limit: ${ratePerMinute} req/min`); console.log(` Expires : ${expiresAt}`); console.log(` Dashboard : ${reg.dashboardUrl ?? sLinks?.self?.href ?? '—'}`); console.log('\n → Each sandbox is isolated. Any agent or developer can create one instantly.'); const servicesHref = sLinks.services?.href ?? `${REGISTRY_ROOT}/sandbox/${sandboxId}/services`; const searchHrefTpl = sLinks.servicesSearch?.href ?? `${REGISTRY_ROOT}/sandbox/${sandboxId}/services{?capability,stage}`; // ── Step 3: Register services (BSM manifests) ────────────────────────────── hdr('Step 3 Register three carrier services (BSM manifests)'); const carriers = [ { name: 'NordLogistik Economy EU', description: 'Economy EU road & rail freight. 5-day delivery. DE/AT/CH specialist.', endpoint: 'https://demo.api-index.org/carriers/nord/quote', capabilities: ['shipping-quote', 'logistics', 'eu-delivery'], registrantEmail: 'api@nordlogistik.example', registrantName: 'NordLogistik GmbH', registrantJurisdiction:'DE', registrantOrgType: 'COMMERCIAL', pricing: { billingModel: 'PER_CALL', pricePerCall: 0.012, currency: 'APX', billingUnit: 'per-call' }, bsmVersion: '0.1', serviceStage: 'PRODUCTION', }, { name: 'SwiftCargo Express', description: 'Global express shipping. 2-day delivery to major hubs worldwide.', endpoint: 'https://demo.api-index.org/carriers/swift/quote', capabilities: ['shipping-quote', 'express-delivery', 'global-logistics'], registrantEmail: 'api@swiftcargo.example', registrantName: 'SwiftCargo Ltd', registrantJurisdiction:'GB', registrantOrgType: 'COMMERCIAL', pricing: { billingModel: 'PER_CALL', pricePerCall: 0.035, currency: 'APX', billingUnit: 'per-call' }, bsmVersion: '0.1', serviceStage: 'PRODUCTION', }, { name: 'PacRim Express', description: 'Cost-optimised Asia-Pacific shipping. SEA, JP, KR, AU routes.', endpoint: 'https://demo.api-index.org/carriers/pacrim/quote', capabilities: ['shipping-quote', 'logistics', 'asia-pacific-delivery'], registrantEmail: 'api@pacrim.example', registrantName: 'PacRim Express Pte', registrantJurisdiction:'SG', registrantOrgType: 'COMMERCIAL', pricing: { billingModel: 'PER_CALL', pricePerCall: 0.018, currency: 'APX', billingUnit: 'per-call' }, bsmVersion: '0.1', serviceStage: 'PRODUCTION', }, ]; for (const manifest of carriers) { await post(servicesHref, manifest, { 'X-Api-Key': apiKey }); console.log(` ✓ Registered: ${manifest.name} (${manifest.registrantJurisdiction})`); } console.log('\n → Any provider self-registers. The agent discovers them automatically.'); // ── Step 4: Agent discovers carriers ────────────────────────────────────── hdr('Step 4 Agent queries: "who can provide shipping-quote?"'); // Strip the URI template expression {?...} and build the URL properly const searchBase = searchHrefTpl.replace(/\{[^}]+\}/, ''); const searchUrl = `${searchBase}?capability=shipping-quote&stage=PRODUCTION`; console.log(` Query: GET ${searchUrl}\n`); const found = await get(searchUrl); console.log(` Found ${found.length} carrier service(s):\n`); for (const svc of found) { const p = svc.pricing; const price = p ? `${p.pricePerCall} ${p.currency}/${p.billingUnit}` : 'unknown'; console.log(` ┌─ ${svc.name}`); console.log(` │ Jurisdiction : ${svc.registrantJurisdiction}`); console.log(` │ Capabilities : ${svc.capabilities?.join(', ')}`); console.log(` │ Price : ${price}`); console.log(` └─ Endpoint : ${svc.endpoint}`); console.log(); } console.log(' → The agent has all three endpoints. It never knew about them before this query.'); // ── Step 5: Call discovered services in parallel ─────────────────────────── hdr('Step 5 Call all carriers in parallel — choose the best quote'); const shipment = { from: 'Munich, DE', to: 'London, GB', weightKg: 2.5, dimensions: '30x20x15cm' }; console.log(` Shipment: ${JSON.stringify(shipment)}\n`); // The agent uses endpoints discovered in Step 4 — zero hardcoded carrier URLs const quoteRequests = found.map(async svc => { const start = Date.now(); try { const quote = await post(svc.endpoint, shipment).catch(() => mockQuote(svc.name)); return { svcName: svc.name, quote, ms: Date.now() - start }; } catch { return { svcName: svc.name, quote: mockQuote(svc.name), ms: Date.now() - start }; } }); const results = await Promise.all(quoteRequests); results.sort((a, b) => a.quote.price.amount - b.quote.price.amount); for (const { svcName, quote } of results) { const best = results[0].svcName === svcName ? ' ← best price' : ''; console.log(` ${svcName.padEnd(28)} ${quote.price.amount.toFixed(2)} ${quote.price.currency} · ${quote.estimatedDays}d${best}`); } const winner = results[0]; console.log(`\n Selected: ${winner.svcName} (${winner.quote.price.amount.toFixed(2)} ${winner.quote.price.currency})`); // ── Summary ──────────────────────────────────────────────────────────────── hdr('Summary'); console.log(' What just happened:\n'); console.log(' 1. Started at a single URL: https://api-index.org'); console.log(' 2. HATEOAS navigation — followed links, never guessed URLs'); console.log(' 3. Created an isolated sandbox in one POST'); console.log(' 4. Three carriers registered their own BSM manifests'); console.log(' 5. Agent queried capability="shipping-quote" — found all three'); console.log(' 6. Called them in parallel, picked the best quote'); console.log(); console.log(' No hardcoded carrier URLs. No ClawHub lookup. No human in the loop.'); console.log(); console.log(' When a new carrier registers in APIX, every OpenClaw agent that'); console.log(' queries "shipping-quote" discovers it automatically — on the next query.'); console.log(); console.log(` Sandbox dashboard: ${reg.dashboardUrl ?? `https://www.api-index.org/sandbox/${sandboxId}`}`); console.log(); } // Fallback mock quotes (in case demo endpoints are not reachable) function mockQuote(name) { const prices = { 'NordLogistik Economy EU': { amount: 12.40, currency: 'APX' }, 'SwiftCargo Express': { amount: 35.00, currency: 'APX' }, 'PacRim Express': { amount: 18.20, currency: 'APX' }, }; const days = { 'NordLogistik Economy EU': 5, 'SwiftCargo Express': 2, 'PacRim Express': 7 }; return { carrier: name, price: prices[name] ?? { amount: 20.00, currency: 'APX' }, estimatedDays: days[name] ?? 5, trackingAvailable: true, }; } main().catch(err => { console.error('\n Error:', err.message); process.exit(1); });