Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build Mastra AI agents and workflows with guidance on current API lookup, tools, memory, and RAG patterns.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/provider-registry.mjs
1#!/usr/bin/env node23import { readFileSync } from "node:fs";4import { join, dirname } from "node:path";5import { fileURLToPath } from "node:url";67const __dirname = dirname(fileURLToPath(import.meta.url));89function findRegistryPath() {10const rel = join('node_modules', '@mastra', 'core', 'dist', 'provider-registry.json');11// Walk up from script location to find project root with node_modules12let dir = __dirname;13for (let i = 0; i < 10; i++) {14try {15const p = join(dir, rel);16readFileSync(p, "utf-8");17return p;18} catch {19dir = dirname(dir);20}21}22// Fall back to cwd23return join(process.cwd(), rel);24}2526function loadRegistry() {27const path = findRegistryPath();28try {29return JSON.parse(readFileSync(path, "utf-8"));30} catch (e) {31console.error(`Error: Could not load provider registry at ${path}`);32console.error(e.message);33process.exit(1);34}35}3637/**38* Extract version numbers from a model name for sorting.39* Returns an array of numeric segments, e.g. "gpt-5.4" → [5, 4].40* Handles dot-separated (3.5), hyphen-separated (3-7), and mixed formats.41* Models without detectable version numbers return null.42*/43function extractVersion(name) {44// Use named capture to grab version-like sequences along with their context.45// We capture digits separated by dots/hyphens, plus any trailing letter for filtering.46const regex = /(\d+(?:[.\-]\d+)*)([a-zA-Z])?/g;47const candidates = [];48let match;49while ((match = regex.exec(name)) !== null) {50const numStr = match[1];51const suffix = match[2] || "";52candidates.push({ numStr, suffix, index: match.index });53}54if (candidates.length === 0) return null;5556// Process candidates: filter and clean up non-version parts57const processed = [];58for (const c of candidates) {59let parts = c.numStr.split(/[.\-]/).map(Number);60// If followed by a size suffix (b/B/k/K/m/M/t/T) — e.g. "8b", "70B", "1t" —61// strip the last numeric part (param count) but keep earlier parts as the version62if (/^[bBkKmMtT]$/.test(c.suffix)) {63parts = parts.slice(0, -1);64if (parts.length === 0) continue;65}66// Strip date-like segments (>= 2020 or YYYYMMDD-style 8-digit numbers)67parts = parts.filter((p) => p < 2020);68if (parts.length === 0) continue;69// Skip very large standalone numbers (parameter counts, IDs)70if (parts.length === 1 && parts[0] >= 100 && candidates.length > 1) continue;71// Skip trailing date-like patterns (MM-DD) in the latter half of the name72if (73parts.length === 2 &&74parts[0] >= 1 && parts[0] <= 12 &&75parts[1] >= 1 && parts[1] <= 31 &&76c.index > name.length / 2 &&77candidates.length > 178) continue;79processed.push(parts);80}8182if (processed.length === 0) return null;8384// Return the first valid version candidate (versions appear early in model names)85return processed[0];86}8788function compareVersionsDesc(a, b) {89const va = extractVersion(a);90const vb = extractVersion(b);9192// Models without versions go to the end93if (!va && !vb) return a.localeCompare(b);94if (!va) return 1;95if (!vb) return -1;9697// Compare version tuples numerically, descending98const len = Math.max(va.length, vb.length);99for (let i = 0; i < len; i++) {100const ai = va[i] ?? 0;101const bi = vb[i] ?? 0;102if (bi !== ai) return bi - ai;103}104// Same version — secondary sort by full name descending105return b.localeCompare(a);106}107108function printUsage() {109console.log(`Usage: provider-registry.mjs [options]110111Options:112--list List all available model providers113--provider <name> List all models for a provider (sorted newest first)114--help Show this help message115116Examples:117node provider-registry.mjs --list118node provider-registry.mjs --provider openai119node provider-registry.mjs --provider anthropic`);120}121122function listProviders(registry) {123const entries = Object.entries(registry.providers)124.map(([key, val]) => ({ key, name: val.name || key }))125.sort((a, b) => a.key.localeCompare(b.key));126127const maxKey = Math.max(...entries.map((e) => e.key.length));128const maxName = Math.max(...entries.map((e) => e.name.length));129130console.log(`${"PROVIDER".padEnd(maxKey)} ${"NAME".padEnd(maxName)} MODELS`);131console.log(`${"─".repeat(maxKey)} ${"─".repeat(maxName)} ${"─".repeat(6)}`);132for (const entry of entries) {133const modelCount = registry.providers[entry.key].models.length;134console.log(`${entry.key.padEnd(maxKey)} ${entry.name.padEnd(maxName)} ${modelCount}`);135}136console.log(`\n${entries.length} providers`);137}138139function listModels(registry, providerName) {140const provider = registry.providers[providerName];141if (!provider) {142console.error(`Error: Provider "${providerName}" not found.`);143console.error(`Run with --list to see available providers.`);144process.exit(1);145}146147const models = [...provider.models].sort(compareVersionsDesc);148149console.log(`${provider.name || providerName} — ${models.length} models\n`);150for (const model of models) {151console.log(` ${model}`);152}153}154155const args = process.argv.slice(2);156157if (args.includes("--help") || args.length === 0) {158printUsage();159process.exit(0);160}161162if (args.includes("--list")) {163listProviders(loadRegistry());164process.exit(0);165}166167const providerIdx = args.indexOf("--provider");168if (providerIdx !== -1) {169const name = args[providerIdx + 1];170if (!name) {171console.error("Error: --provider requires a provider name.");172process.exit(1);173}174listModels(loadRegistry(), name);175process.exit(0);176}177178console.error("Error: Unknown arguments:", args.join(" "));179printUsage();180process.exit(1);181