Setting the file. One moment. Resolve Latest Model Info · OpenAI Docs · openai/skills · Skills Docs42
Plugin Creator
scripts/resolve-latest-model-info.js
JavaScript·147 lines·3 KB
(
argv
) {
11 const args = {
12 source: process.env.LATEST_MODEL_URL || DEFAULT_URL,
13 baseUrl: process.env.LATEST_MODEL_BASE_URL || DEFAULT_BASE_URL,
14 };
15
16 for (let i = 2; i < argv.length; i += 1) {
17 const arg = argv[i];
18 if (arg === "--source" || arg === "--url") {
19 args.source = argv[i + 1];
20 i += 1;
21 } else if (arg === "--base-url") {
22 args.baseUrl = argv[i + 1];
23 i += 1;
24 }
25 }
26
27 return args;
28}
29
30async function readSource(source) {
31 if (source.startsWith("file://")) {
32 return fs.readFile(new URL(source), "utf8");
33 }
34
35 if (!/^https?:\/\//.test(source)) {
36 return fs.readFile(path.resolve(source), "utf8");
37 }
38
39 const response = await fetch(source, {
40 headers: { accept: "text/markdown,text/plain,*/*" },
41 });
42
43 if (!response.ok) {
44 throw new Error(`failed to fetch ${source}: ${response.status}`);
45 }
46
47 return response.text();
48}
49
50function parseIndentedInfo(lines, startIndex) {
51 const info = {};
52
53 for (let i = startIndex + 1; i < lines.length; i += 1) {
54 const line = lines[i];
55 if (!line.trim()) {
56 continue;
57 }
58
59 const match = line.match(/^ {2}([A-Za-z][A-Za-z0-9_-]*):\s*(.+?)\s*$/);
60 if (!match) {
61 break;
62 }
63
64 info[match[1]] = match[2].replace(/^["']|["']$/g, "");
65 }
66
67 return info;
68}
69
70function parseFlatInfo(block) {
71 const info = {};
72
73 for (const line of block.split(/\r?\n/)) {
74 const match = line.match(/^\s*([A-Za-z][A-Za-z0-9_-]*):\s*(.+?)\s*$/);
75 if (match) {
76 info[match[1]] = match[2].replace(/^["']|["']$/g, "");
77 }
78 }
79
80 return info;
81}
82
83function extractLatestModelInfo(markdown) {
84 const lines = markdown.split(/\r?\n/);
85 const latestModelInfoIndex = lines.findIndex((line) =>
86 /^latestModelInfo:\s*$/.test(line)
87 );
88
89 if (latestModelInfoIndex >= 0) {
90 return parseIndentedInfo(lines, latestModelInfoIndex);
91 }
92
93 const commentMatch = markdown.match(
94 /<!--\s*latestModelInfo\s*\n([\s\S]*?)\n\s*-->/m
95 );
96 if (commentMatch) {
97 return parseFlatInfo(commentMatch[1]);
98 }
99
100 return undefined;
101}
102
103function modelToSkillSlug(model) {
104 return model.trim().replace(/\./g, "p");
105}
106
107function absoluteUrl(baseUrl, value) {
108 return new URL(value, baseUrl).toString();
109}
110
111function normalizeInfo(info, baseUrl) {
112 const model = info?.model?.trim();
113 const migrationGuide = info?.migrationGuide?.trim();
114 const promptingGuide = info?.promptingGuide?.trim();
115
116 if (!model || !migrationGuide || !promptingGuide) {
117 throw new Error(
118 "latestModelInfo must include model, migrationGuide, and promptingGuide"
119 );
120 }
121
122 return {
123 model,
124 modelSlug: modelToSkillSlug(model),
125 migrationGuideUrl: absoluteUrl(baseUrl, migrationGuide),
126 promptingGuideUrl: absoluteUrl(baseUrl, promptingGuide),
127 };
128}
129
130async function main() {
131 const { source, baseUrl } = parseArgs(process.argv);
132 const markdown = await readSource(source);
133 const info = extractLatestModelInfo(markdown);
134
135 if (!info) {
136 throw new Error(`latestModelInfo block not found in ${source}`);
137 }
138
139 process.stdout.write(
140 `${JSON.stringify(normalizeInfo(info, baseUrl), null, 2)}\n`
141 );
142}
143
144main().catch((error) => {
145 console.error(error.message);
146 process.exit(1);
147});