scripts/resolve-latest-model-info.cjs
scripts/resolve-latest-model-info.cjsBrowse 17 files
1,089 tokens
3,937 bytes
Token encoding: o200k_base
Snapshot 18d7ace
← Back to SKILL.md
1#!/usr/bin/env node2 3// Keep this entrypoint CommonJS-safe when the skill is copied into a type=module repo.4 5const fs = require("node:fs/promises");6const path = require("node:path");7 8const DEFAULT_URL =9 "https://developers.openai.com/api/docs/guides/latest-model.md";10const DEFAULT_BASE_URL = "https://developers.openai.com";11 12function parseArgs(argv) {13 const args = {14 source: process.env.LATEST_MODEL_URL || DEFAULT_URL,15 baseUrl: process.env.LATEST_MODEL_BASE_URL || DEFAULT_BASE_URL,16 };17 18 for (let i = 2; i < argv.length; i += 1) {19 const arg = argv[i];20 if (arg === "--source" || arg === "--url") {21 args.source = argv[i + 1];22 i += 1;23 } else if (arg === "--base-url") {24 args.baseUrl = argv[i + 1];25 i += 1;26 }27 }28 29 return args;30}31 32async function readSource(source) {33 if (source.startsWith("file://")) {34 return fs.readFile(new URL(source), "utf8");35 }36 37 if (!/^https?:\/\//.test(source)) {38 return fs.readFile(path.resolve(source), "utf8");39 }40 41 let lastError;42 for (let attempt = 1; attempt <= 3; attempt += 1) {43 try {44 const response = await fetch(source, {45 headers: { accept: "text/markdown,text/plain,*/*" },46 });47 48 if (response.ok) {49 return response.text();50 }51 52 lastError = new Error("failed to fetch " + source + ": " + response.status);53 if (response.status < 500 && response.status !== 429) {54 break;55 }56 } catch (error) {57 lastError = error;58 }59 60 if (attempt < 3) {61 await new Promise((resolve) => setTimeout(resolve, 250 * attempt));62 }63 }64 65 throw lastError;66}67 68function parseIndentedInfo(lines, startIndex) {69 const info = {};70 71 for (let i = startIndex + 1; i < lines.length; i += 1) {72 const line = lines[i];73 if (!line.trim()) {74 continue;75 }76 77 const match = line.match(/^ {2}([A-Za-z][A-Za-z0-9_-]*):\s*(.+?)\s*$/);78 if (!match) {79 break;80 }81 82 info[match[1]] = match[2].replace(/^["']|["']$/g, "");83 }84 85 return info;86}87 88function parseFlatInfo(block) {89 const info = {};90 91 for (const line of block.split(/\r?\n/)) {92 const match = line.match(/^\s*([A-Za-z][A-Za-z0-9_-]*):\s*(.+?)\s*$/);93 if (match) {94 info[match[1]] = match[2].replace(/^["']|["']$/g, "");95 }96 }97 98 return info;99}100 101function extractLatestModelInfo(markdown) {102 const lines = markdown.split(/\r?\n/);103 const latestModelInfoIndex = lines.findIndex((line) =>104 /^latestModelInfo:\s*$/.test(line)105 );106 107 if (latestModelInfoIndex >= 0) {108 return parseIndentedInfo(lines, latestModelInfoIndex);109 }110 111 const commentMatch = markdown.match(112 /<!--\s*latestModelInfo\s*\n([\s\S]*?)\n\s*-->/m113 );114 if (commentMatch) {115 return parseFlatInfo(commentMatch[1]);116 }117 118 return undefined;119}120 121function modelToSkillSlug(model) {122 return model.trim().replace(/\./g, "p");123}124 125function absoluteUrl(baseUrl, value) {126 return new URL(value, baseUrl).toString();127}128 129function normalizeInfo(info, baseUrl) {130 const model = info?.model?.trim();131 const migrationGuide = info?.migrationGuide?.trim();132 const promptingGuide = info?.promptingGuide?.trim();133 134 if (!model || !migrationGuide || !promptingGuide) {135 throw new Error(136 "latestModelInfo must include model, migrationGuide, and promptingGuide"137 );138 }139 140 return {141 model,142 modelSlug: modelToSkillSlug(model),143 migrationGuideUrl: absoluteUrl(baseUrl, migrationGuide),144 promptingGuideUrl: absoluteUrl(baseUrl, promptingGuide),145 };146}147 148async function main() {149 const { source, baseUrl } = parseArgs(process.argv);150 const markdown = await readSource(source);151 const info = extractLatestModelInfo(markdown);152 153 if (!info) {154 throw new Error(`latestModelInfo block not found in ${source}`);155 }156 157 process.stdout.write(158 `${JSON.stringify(normalizeInfo(info, baseUrl), null, 2)}\n`159 );160}161 162main().catch((error) => {163 console.error(error.message);164 process.exit(1);165});166 