Files
dotfiles/dotfiles/agents/skills/.system/openai-docs/scripts/resolve-latest-model-info.js

148 lines
3.4 KiB
JavaScript

#!/usr/bin/env node
const fs = require("node:fs/promises");
const path = require("node:path");
const DEFAULT_URL =
"https://developers.openai.com/api/docs/guides/latest-model.md";
const DEFAULT_BASE_URL = "https://developers.openai.com";
function parseArgs(argv) {
const args = {
source: process.env.LATEST_MODEL_URL || DEFAULT_URL,
baseUrl: process.env.LATEST_MODEL_BASE_URL || DEFAULT_BASE_URL,
};
for (let i = 2; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === "--source" || arg === "--url") {
args.source = argv[i + 1];
i += 1;
} else if (arg === "--base-url") {
args.baseUrl = argv[i + 1];
i += 1;
}
}
return args;
}
async function readSource(source) {
if (source.startsWith("file://")) {
return fs.readFile(new URL(source), "utf8");
}
if (!/^https?:\/\//.test(source)) {
return fs.readFile(path.resolve(source), "utf8");
}
const response = await fetch(source, {
headers: { accept: "text/markdown,text/plain,*/*" },
});
if (!response.ok) {
throw new Error(`failed to fetch ${source}: ${response.status}`);
}
return response.text();
}
function parseIndentedInfo(lines, startIndex) {
const info = {};
for (let i = startIndex + 1; i < lines.length; i += 1) {
const line = lines[i];
if (!line.trim()) {
continue;
}
const match = line.match(/^ {2}([A-Za-z][A-Za-z0-9_-]*):\s*(.+?)\s*$/);
if (!match) {
break;
}
info[match[1]] = match[2].replace(/^["']|["']$/g, "");
}
return info;
}
function parseFlatInfo(block) {
const info = {};
for (const line of block.split(/\r?\n/)) {
const match = line.match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.+?)\s*$/);
if (match) {
info[match[1]] = match[2].replace(/^["']|["']$/g, "");
}
}
return info;
}
function extractLatestModelInfo(markdown) {
const lines = markdown.split(/\r?\n/);
const latestModelInfoIndex = lines.findIndex((line) =>
/^latestModelInfo:\s*$/.test(line)
);
if (latestModelInfoIndex >= 0) {
return parseIndentedInfo(lines, latestModelInfoIndex);
}
const commentMatch = markdown.match(
/<!--\s*latestModelInfo\s*\n([\s\S]*?)\n\s*-->/m
);
if (commentMatch) {
return parseFlatInfo(commentMatch[1]);
}
return undefined;
}
function modelToSkillSlug(model) {
return model.trim().replace(/\./g, "p");
}
function absoluteUrl(baseUrl, value) {
return new URL(value, baseUrl).toString();
}
function normalizeInfo(info, baseUrl) {
const model = info?.model?.trim();
const migrationGuide = info?.migrationGuide?.trim();
const promptingGuide = info?.promptingGuide?.trim();
if (!model || !migrationGuide || !promptingGuide) {
throw new Error(
"latestModelInfo must include model, migrationGuide, and promptingGuide"
);
}
return {
model,
modelSlug: modelToSkillSlug(model),
migrationGuideUrl: absoluteUrl(baseUrl, migrationGuide),
promptingGuideUrl: absoluteUrl(baseUrl, promptingGuide),
};
}
async function main() {
const { source, baseUrl } = parseArgs(process.argv);
const markdown = await readSource(source);
const info = extractLatestModelInfo(markdown);
if (!info) {
throw new Error(`latestModelInfo block not found in ${source}`);
}
process.stdout.write(
`${JSON.stringify(normalizeInfo(info, baseUrl), null, 2)}\n`
);
}
main().catch((error) => {
console.error(error.message);
process.exit(1);
});