77 lines
3.2 KiB
JavaScript
77 lines
3.2 KiB
JavaScript
import { readFile, readdir, writeFile } from "node:fs/promises";
|
||
import { dirname, join } from "node:path";
|
||
import { fileURLToPath } from "node:url";
|
||
|
||
const root = dirname(fileURLToPath(import.meta.url));
|
||
const contractStart = "<!-- YINGCE_MANIFEST_CONTRACT_START -->";
|
||
const contractEnd = "<!-- YINGCE_MANIFEST_CONTRACT_END -->";
|
||
const recursiveDocumentationPlaceholder =
|
||
"<当前插件的完整 documentation,由 README.md 与 docs/interface.md 拼接而成;为避免 JSON 递归,此处不重复展开正文。>";
|
||
|
||
function normalizeNewlines(document) {
|
||
return document.replace(/\r\n?/g, "\n");
|
||
}
|
||
|
||
function withoutGeneratedContract(document) {
|
||
const start = document.indexOf(contractStart);
|
||
if (start < 0) return document.trimEnd();
|
||
const end = document.indexOf(contractEnd, start);
|
||
if (end < 0) {
|
||
throw new Error("docs/interface.md contains an unterminated generated manifest contract");
|
||
}
|
||
return `${document.slice(0, start)}${document.slice(end + contractEnd.length)}`.trimEnd();
|
||
}
|
||
|
||
function renderContract(manifest) {
|
||
const contract = JSON.parse(JSON.stringify(manifest));
|
||
contract.documentation = recursiveDocumentationPlaceholder;
|
||
return `${contractStart}
|
||
## Manifest 完整接口定义
|
||
|
||
以下 JSON 与插件包内实际 \`manifest.json\` 逐字段一致,覆盖插件身份、权限、配置、鉴权、参数、校验、创建、Agent、查询、取消、结果下载、响应和 Agent 响应映射。\`documentation\` 字段的值就是当前完整文档;为避免文档在自身内部无限递归,JSON 中仅用等义占位文本表示正文。
|
||
|
||
\`\`\`json
|
||
${JSON.stringify(contract, null, 2)}
|
||
\`\`\`
|
||
${contractEnd}`;
|
||
}
|
||
|
||
const entries = await readdir(root, { withFileTypes: true });
|
||
const requestedPackageIDs = new Set(process.argv.slice(2).map((value) => value.trim()).filter(Boolean));
|
||
const packageDirectories = entries
|
||
.filter((entry) => entry.isDirectory())
|
||
.map((entry) => entry.name)
|
||
.filter((name) => requestedPackageIDs.size === 0 || requestedPackageIDs.has(name))
|
||
.sort();
|
||
|
||
let updated = 0;
|
||
for (const packageID of packageDirectories) {
|
||
const packageRoot = join(root, packageID);
|
||
let manifestSource;
|
||
try {
|
||
manifestSource = normalizeNewlines(await readFile(join(packageRoot, "manifest.json"), "utf8"));
|
||
} catch (error) {
|
||
if (error?.code === "ENOENT") continue;
|
||
throw error;
|
||
}
|
||
|
||
const manifest = JSON.parse(manifestSource);
|
||
const readmePath = join(packageRoot, "README.md");
|
||
const readme = normalizeNewlines(await readFile(readmePath, "utf8")).trim();
|
||
const interfacePath = join(packageRoot, "docs", "interface.md");
|
||
const interfaceSource = normalizeNewlines(await readFile(interfacePath, "utf8"));
|
||
const interfaceDocument = `${withoutGeneratedContract(interfaceSource)}\n\n${renderContract(manifest)}\n`;
|
||
manifest.documentation = `${readme}\n\n---\n\n${interfaceDocument.trim()}\n`;
|
||
|
||
await writeFile(readmePath, `${readme}\n`);
|
||
await writeFile(interfacePath, interfaceDocument);
|
||
await writeFile(join(packageRoot, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
|
||
updated += 1;
|
||
}
|
||
|
||
if (updated === 0) {
|
||
throw new Error("no protocol plugin manifests were found");
|
||
}
|
||
|
||
console.log(`embedded complete documentation into ${updated} protocol packages`);
|