Skip to main content
Glama
generateSiSpecs.ts4.76 kB
import { emptyDirectory } from "../util.ts"; import _logger from "../logger.ts"; import { PkgSpec } from "../bindings/PkgSpec.ts"; import { SchemaVariantSpec } from "../bindings/SchemaVariantSpec.ts"; import { SchemaSpec } from "../bindings/SchemaSpec.ts"; import { bfsPropTree, ExpandedPropSpec } from "../spec/props.ts"; import { ExpandedPkgSpec, ExpandedSchemaSpec, ExpandedSchemaVariantSpec, } from "../spec/pkgs.ts"; import { PropSpec } from "../bindings/PropSpec.ts"; import { PipelineOptions, selectedProviders } from "../pipelines/types.ts"; // Import all providers to ensure they register themselves import "../pipelines/aws/spec.ts"; import "../pipelines/hetzner/provider.ts"; import "../pipelines/msgraph/provider.ts"; import "../pipelines/digitalocean/provider.ts"; import "../pipelines/gcp/provider.ts"; import "../pipelines/dummy/spec.ts"; // set this boolean to true to print out duplicates instead of throwing an error const PRINT_DUPLICATES = false; const logger = _logger.ns("siSpecs").seal(); const SI_SPEC_DIR = "si-specs"; const MAX_SPEC_SIZE_MB = 20; export async function generateSiSpecs(options: PipelineOptions) { const specs: ExpandedPkgSpec[] = []; for (const config of selectedProviders(options)) { logger.info(`Generating specs for provider: ${config.name}`); const providerSpecs = await config.loadSchemas(options); specs.push(...providerSpecs); } // WRITE OUT SPECS await emptyDirectory(SI_SPEC_DIR); let imported = 0; const wroteSchemas = {} as Record<string, string[]>; const categories = {} as Record<string, string>; const multipleSchemas = {} as Record<string, string>; const multipleCategories = [] as Array<string>; for (const spec of specs) { const specJson = JSON.stringify(unexpandPackageSpec(spec), null, 2); const name = spec.name.replaceAll("/", "."); const schema = spec.schemas[0]; // two schemas cannot have the same name, case-insensitive if (wroteSchemas[schema.name.toLowerCase()]) { if (!PRINT_DUPLICATES) { throw new Error( `Multiple ${schema.name} schemas, possibly with different case`, ); } wroteSchemas[schema.name.toLowerCase()].push(schema.name); multipleSchemas[schema.name.toLowerCase()] = `${ wroteSchemas[schema.name.toLowerCase()] }`; } else { wroteSchemas[schema.name.toLowerCase()] = [ schema.name, ]; } // two categories cannot have the same name with different casing if (schema.data?.category) { const category = schema.data.category; const categoryLower = category.toLowerCase(); categories[categoryLower] ??= category; const s = `${schema.name} - ${categories[categoryLower]} and ${category}`; if ( categories[categoryLower] !== category && !multipleCategories.includes(s) ) { if (!PRINT_DUPLICATES) { throw new Error( `Multiple categories with the same name but different case found for schema ${s}`, ); } multipleCategories.push(s); } } logger.debug(`Writing ${name}.json`); const blob = new Blob([specJson]); if (blob.size > MAX_SPEC_SIZE_MB * 1024 * 1024) { const sizeMB = (blob.size / (1024 * 1024)).toFixed(1); console.warn( `${spec.name} is ${sizeMB}MB (bigger than ${MAX_SPEC_SIZE_MB}MB). Skipping ...`, ); continue; } await Deno.writeFile(`${SI_SPEC_DIR}/${name}.json`, blob.stream()); imported += 1; } if (PRINT_DUPLICATES) { console.warn("Duplicate schema names -"); console.warn(multipleSchemas); console.warn("Duplicate category names -"); console.warn(multipleCategories); } console.log(`built ${imported} out of ${specs.length}`); } function unexpandPackageSpec(expandedSpec: ExpandedPkgSpec): PkgSpec { // Take out cfSchema and other props we don't want in the final spec return { ...expandedSpec, schemas: expandedSpec.schemas.map(unexpandSchema), }; } function unexpandSchema(expanded: ExpandedSchemaSpec): SchemaSpec { return { ...expanded, variants: expanded.variants.map(unexpandVariant), }; } function unexpandVariant( expanded: ExpandedSchemaVariantSpec, ): SchemaVariantSpec { const { superSchema: _, ...variant } = expanded; bfsPropTree( [ variant.domain, variant.resourceValue, variant.secrets, variant.secretDefinition, ], unexpandProperty, ); return { ...variant, sockets: [] }; } function unexpandProperty(expanded: ExpandedPropSpec): PropSpec { const deleteable = expanded as Partial<ExpandedPropSpec>; delete deleteable.metadata; delete deleteable.joiValidation; delete deleteable.cfProp; return expanded; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server