From 2a4023498fe44c9f4f073ec9afbcb33aad0656b3 Mon Sep 17 00:00:00 2001 From: yi-ge Date: Wed, 16 Sep 2026 11:56:32 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=8F=91=E8=A1=8C=E7=89=88=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=A5=E5=BF=97=E7=AB=A0=E8=8A=82=EF=BC=88damo-solv?= =?UTF-8?q?er=20=E6=9D=A1=E7=9B=AE=E5=8C=96=E6=8F=92=E4=BB=B6=E4=B8=8E?= =?UTF-8?q?=E4=B8=AD=E8=8B=B1=E6=96=87=E7=AB=A0=E8=8A=82=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../12-damo-solver-changelog/_category_.json | 8 ++ .../05-more/12-damo-solver-changelog/index.md | 16 ++++ docs/05-more/index.md | 1 + .../current.json | 4 + .../12-damo-solver-changelog/_category_.json | 8 ++ .../05-more/12-damo-solver-changelog/index.md | 16 ++++ .../current/05-more/index.md | 1 + plugins/changelog-editions.json | 38 +++++++++ plugins/changelog-json-plugin.ts | 84 ++++++++++++++----- scripts/generate-changelog-cover.mjs | 80 ++++++++++++++---- 10 files changed, 219 insertions(+), 37 deletions(-) create mode 100644 docs/05-more/12-damo-solver-changelog/_category_.json create mode 100644 docs/05-more/12-damo-solver-changelog/index.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/_category_.json create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/index.md create mode 100644 plugins/changelog-editions.json diff --git a/docs/05-more/12-damo-solver-changelog/_category_.json b/docs/05-more/12-damo-solver-changelog/_category_.json new file mode 100644 index 00000000..5ecc80c4 --- /dev/null +++ b/docs/05-more/12-damo-solver-changelog/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "达摩院AgentOS更新日志", + "position": 10, + "link": { + "type": "doc", + "id": "more/damo-solver-changelog/index" + } +} diff --git a/docs/05-more/12-damo-solver-changelog/index.md b/docs/05-more/12-damo-solver-changelog/index.md new file mode 100644 index 00000000..0d937933 --- /dev/null +++ b/docs/05-more/12-damo-solver-changelog/index.md @@ -0,0 +1,16 @@ +--- +title: "达摩院AgentOS更新日志" +description: "达摩院AgentOS版本更新历史记录" +keywords: [更新日志, 版本历史, changelog] +--- + +# 达摩院AgentOS更新日志 + +本页记录达摩院AgentOS各版本的重要变更。 + +## 版本索引 + + + +| 版本 | 日期 | 概要 | +|------|------|------| diff --git a/docs/05-more/index.md b/docs/05-more/index.md index cdfd8765..4c75973e 100644 --- a/docs/05-more/index.md +++ b/docs/05-more/index.md @@ -21,3 +21,4 @@ sidebar_position: 0 | [开源仓库](./09-open-source-repos.md) | 官方 GitHub 仓库一览 | | [第三方软件与许可](./10-third-party-software.md) | 内置第三方组件及开源许可 | | [更新日志](./11-changelog/index.md) | 版本更新历史记录 | +| [达摩院AgentOS更新日志](./12-damo-solver-changelog/index.md) | 达摩院AgentOS版本更新历史记录 | diff --git a/i18n/en/docusaurus-plugin-content-docs/current.json b/i18n/en/docusaurus-plugin-content-docs/current.json index 69cc63f1..8b9f71ba 100644 --- a/i18n/en/docusaurus-plugin-content-docs/current.json +++ b/i18n/en/docusaurus-plugin-content-docs/current.json @@ -130,5 +130,9 @@ "sidebar.more.category.更新日志": { "message": "Changelog", "description": "The label for category '更新日志' in sidebar 'more'" + }, + "sidebar.more.category.达摩院AgentOS更新日志": { + "message": "达摩院AgentOS Changelog", + "description": "The label for category '达摩院AgentOS更新日志' in sidebar 'more'" } } diff --git a/i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/_category_.json b/i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/_category_.json new file mode 100644 index 00000000..c5cd3578 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "达摩院AgentOS Changelog", + "position": 10, + "link": { + "type": "doc", + "id": "more/damo-solver-changelog/index" + } +} diff --git a/i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/index.md b/i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/index.md new file mode 100644 index 00000000..cc3f384e --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog/index.md @@ -0,0 +1,16 @@ +--- +title: "达摩院AgentOS Changelog" +description: "达摩院AgentOS version changelog" +keywords: [changelog, version history, release notes] +--- + +# 达摩院AgentOS Changelog + +This page records important changes in each version of 达摩院AgentOS. + +## Version Index + + + +| Version | Date | Summary | +|---------|------|---------| diff --git a/i18n/en/docusaurus-plugin-content-docs/current/05-more/index.md b/i18n/en/docusaurus-plugin-content-docs/current/05-more/index.md index 404fc8ad..d8787e84 100644 --- a/i18n/en/docusaurus-plugin-content-docs/current/05-more/index.md +++ b/i18n/en/docusaurus-plugin-content-docs/current/05-more/index.md @@ -21,3 +21,4 @@ Here you'll find quick reference materials and changelog for DesireCore. | [Open Source Repositories](./09-open-source-repos.md) | Overview of official GitHub repositories | | [Third-Party Software & Licenses](./10-third-party-software.md) | Bundled third-party components and open-source licenses | | [Changelog](./11-changelog/index.md) | Version update history | +| [达摩院AgentOS Changelog](./12-damo-solver-changelog/index.md) | 达摩院AgentOS version update history | diff --git a/plugins/changelog-editions.json b/plugins/changelog-editions.json new file mode 100644 index 00000000..4d0da2bf --- /dev/null +++ b/plugins/changelog-editions.json @@ -0,0 +1,38 @@ +{ + "description": "更新日志章节配置:每个条目是一个产品发行版的独立更新日志。plugins/changelog-json-plugin.ts 按条目读取 zhSourceDir 下的 v.md,构建时生成站点 /api/(客户端按发行版拉取);scripts/generate-changelog-cover.mjs --edition 按条目生成封面。DesireCore 发布流程只写 desirecore 章节;OEM 发布流程按 id 读取本文件定位章节目录,条目缺失时拒绝写入。新增发行版时追加条目并创建中英文两个章节目录(_category_.json 与 index.md,index.md 的表格分隔行与 desirecore 章节一致,发布流程据此插入版本行)。", + "fields": { + "id": "发行版更新日志 id(desirecore 仓库 editions//edition.json#distribution.changelogEdition)", + "zhSourceDir": "中文章节目录(相对仓库根):版本页面 v.md 与 index.md 所在目录", + "enSourceDir": "英文章节目录(相对仓库根)", + "outputFile": "构建输出到站点 api/ 下的 JSON 文件名", + "urlPrefix": "版本页面路由前缀(Docusaurus 去掉目录数字前缀后的路径);JSON 中 url = 站点地址 + urlPrefix + /v", + "coverImageDir": "封面图片输出目录(相对仓库根)", + "coverImageUrlPrefix": "版本页面中引用封面图片的路径前缀", + "coverBrandText": "封面左上角的品牌字样", + "coverWebsite": "封面右下角的网址;null 表示不显示" + }, + "editions": [ + { + "id": "desirecore", + "zhSourceDir": "docs/05-more/11-changelog", + "enSourceDir": "i18n/en/docusaurus-plugin-content-docs/current/05-more/11-changelog", + "outputFile": "changelog.json", + "urlPrefix": "/more/changelog", + "coverImageDir": "static/img/changelog", + "coverImageUrlPrefix": "/img/changelog", + "coverBrandText": "DESIRECORE", + "coverWebsite": "www.desirecore.cn" + }, + { + "id": "damo-solver", + "zhSourceDir": "docs/05-more/12-damo-solver-changelog", + "enSourceDir": "i18n/en/docusaurus-plugin-content-docs/current/05-more/12-damo-solver-changelog", + "outputFile": "changelog-damo-solver.json", + "urlPrefix": "/more/damo-solver-changelog", + "coverImageDir": "static/img/changelog/damo-solver", + "coverImageUrlPrefix": "/img/changelog/damo-solver", + "coverBrandText": "达摩院AgentOS", + "coverWebsite": null + } + ] +} diff --git a/plugins/changelog-json-plugin.ts b/plugins/changelog-json-plugin.ts index 28f42b3a..2cfc0573 100644 --- a/plugins/changelog-json-plugin.ts +++ b/plugins/changelog-json-plugin.ts @@ -1,8 +1,9 @@ /** - * Docusaurus 插件:构建时生成 changelog.json + * Docusaurus 插件:构建时生成更新日志 JSON * - * 读取 docs/05-more/11-changelog/v*.md,解析 frontmatter 和 markdown sections, - * 输出结构化 JSON 到 static/api/changelog.json(开发+构建均可用)。 + * 按 plugins/changelog-editions.json 的每个发行版条目读取 /v*.md,解析 frontmatter 和 markdown sections, + * 输出结构化 JSON 到 static/api/(开发+构建均可用)。 + * desirecore 条目的源目录、输出名与 url 前缀与改为配置之前相同(api/changelog.json);OEM 发行版条目输出 api/changelog-.json。 */ import type { LoadContext, Plugin } from '@docusaurus/types' @@ -32,6 +33,14 @@ interface ChangelogJson { versions: ChangelogVersion[] } +/** plugins/changelog-editions.json 中插件使用的条目字段(完整字段说明见该文件) */ +interface ChangelogEditionConfig { + id: string + zhSourceDir: string + outputFile: string + urlPrefix: string +} + /** section heading 到 type 的映射 */ const SECTION_MAP: Record = { '新功能': 'feature', @@ -44,7 +53,7 @@ const SITE_URL = 'https://docs.desirecore.com' /** * 从 markdown 文件内容中解析版本信息 */ -function parseChangelogMd(content: string, fileName: string): ChangelogVersion | null { +function parseChangelogMd(content: string, fileName: string, urlPrefix: string): ChangelogVersion | null { // 提取版本号(从文件名 v10.0.16.md → 10.0.16) const versionMatch = fileName.match(/^v(\d+\.\d+\.\d+)\.md$/) if (!versionMatch) return null @@ -91,7 +100,7 @@ function parseChangelogMd(content: string, fileName: string): ChangelogVersion | version, date, sections: nonEmptySections, - url: `${SITE_URL}/more/changelog/v${version}`, + url: `${SITE_URL}${urlPrefix}/v${version}`, } if (coverImage) result.coverImage = coverImage return result @@ -100,13 +109,16 @@ function parseChangelogMd(content: string, fileName: string): ChangelogVersion | /** * 生成 changelog.json 内容 */ -function generateChangelogJson(changelogDir: string): ChangelogJson { - const files = fs.readdirSync(changelogDir).filter((f) => /^v\d+\.\d+\.\d+\.md$/.test(f)) +function generateChangelogJson(changelogDir: string, urlPrefix: string): ChangelogJson { + // 章节目录尚不存在时输出空列表:客户端拿到「没有版本」而不是 404 + const files = fs.existsSync(changelogDir) + ? fs.readdirSync(changelogDir).filter((f) => /^v\d+\.\d+\.\d+\.md$/.test(f)) + : [] const versions: ChangelogVersion[] = [] for (const file of files) { const content = fs.readFileSync(path.join(changelogDir, file), 'utf-8') - const parsed = parseChangelogMd(content, file) + const parsed = parseChangelogMd(content, file, urlPrefix) if (parsed) { versions.push(parsed) } @@ -128,34 +140,66 @@ function generateChangelogJson(changelogDir: string): ChangelogJson { } /** - * 将 changelog.json 写入目标目录 + * 将更新日志 JSON 写入目标目录的 api/ */ -function writeChangelogJson(targetDir: string, data: ChangelogJson): void { +function writeChangelogJson(targetDir: string, outputFile: string, data: ChangelogJson): void { const apiDir = path.join(targetDir, 'api') fs.mkdirSync(apiDir, { recursive: true }) - fs.writeFileSync(path.join(apiDir, 'changelog.json'), JSON.stringify(data, null, 2), 'utf-8') + fs.writeFileSync(path.join(apiDir, outputFile), JSON.stringify(data, null, 2), 'utf-8') +} + +/** + * 读取并校验 plugins/changelog-editions.json;配置错误直接让构建失败 + */ +function loadChangelogEditions(siteDir: string): ChangelogEditionConfig[] { + const configPath = path.join(siteDir, 'plugins', 'changelog-editions.json') + const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as { editions?: unknown } + if (!Array.isArray(raw.editions) || raw.editions.length === 0) { + throw new Error(`[changelog-json-plugin] ${configPath} 缺少 editions`) + } + const outputFiles = new Set() + return raw.editions.map((entry, index) => { + const edition = entry as Partial + for (const field of ['id', 'zhSourceDir', 'outputFile', 'urlPrefix'] as const) { + if (typeof edition[field] !== 'string' || edition[field] === '') { + throw new Error(`[changelog-json-plugin] editions[${index}].${field} 必须是非空字符串`) + } + } + const { outputFile, urlPrefix } = edition as ChangelogEditionConfig + if (!/^[a-z0-9-]+\.json$/.test(outputFile) || !urlPrefix.startsWith('/') || outputFiles.has(outputFile)) { + throw new Error(`[changelog-json-plugin] editions[${index}] 的 outputFile(小写 .json 文件名、不得重复)或 urlPrefix(以 / 开头)非法`) + } + outputFiles.add(outputFile) + return edition as ChangelogEditionConfig + }) } export default function changelogJsonPlugin(context: LoadContext): Plugin { - const changelogDir = path.join(context.siteDir, 'docs', '05-more', '11-changelog') + const editions = loadChangelogEditions(context.siteDir) + + /** 为每个发行版生成并写入 JSON */ + function generateAll(targetDir: string, label: string): Record { + const results: Record = {} + for (const edition of editions) { + const data = generateChangelogJson(path.join(context.siteDir, edition.zhSourceDir), edition.urlPrefix) + writeChangelogJson(targetDir, edition.outputFile, data) + console.log(`[changelog-json-plugin] 已生成 ${label}/api/${edition.outputFile}(${data.versions.length} 个版本)`) + results[edition.id] = data + } + return results + } return { name: 'changelog-json-plugin', // 开发模式:在 static/ 中生成,Docusaurus 会自动 serve async loadContent() { - const data = generateChangelogJson(changelogDir) - const staticDir = path.join(context.siteDir, 'static') - writeChangelogJson(staticDir, data) - console.log(`[changelog-json-plugin] 已生成 static/api/changelog.json(${data.versions.length} 个版本)`) - return data + return generateAll(path.join(context.siteDir, 'static'), 'static') }, // 构建模式:复制到 build 输出目录 async postBuild({ outDir }) { - const data = generateChangelogJson(changelogDir) - writeChangelogJson(outDir, data) - console.log(`[changelog-json-plugin] 已生成 build/api/changelog.json(${data.versions.length} 个版本)`) + generateAll(outDir, 'build') }, } } diff --git a/scripts/generate-changelog-cover.mjs b/scripts/generate-changelog-cover.mjs index d1145730..cec4277a 100644 --- a/scripts/generate-changelog-cover.mjs +++ b/scripts/generate-changelog-cover.mjs @@ -18,6 +18,10 @@ * AI_BASE_URL=https://your-gateway AI_API_KEY=xxx \ * node scripts/generate-changelog-cover.mjs v10.0.16 * ... node scripts/generate-changelog-cover.mjs v10.0.16 --force + * ... node scripts/generate-changelog-cover.mjs v10.0.16 --edition + * + * --edition 选择 plugins/changelog-editions.json 中的发行版条目(缺省 desirecore):章节目录、封面输出目录、 + * 图片引用前缀、品牌字样与网址都取自该条目;desirecore 条目的取值与参数化之前写死的值相同。 */ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs' @@ -30,9 +34,33 @@ const sharp = require('sharp') const __dirname = dirname(fileURLToPath(import.meta.url)) const ROOT = resolve(__dirname, '..') -const CHANGELOG_DIR_ZH = resolve(ROOT, 'docs/05-more/11-changelog') -const CHANGELOG_DIR_EN = resolve(ROOT, 'i18n/en/docusaurus-plugin-content-docs/current/05-more/11-changelog') -const IMG_DIR = resolve(ROOT, 'static/img/changelog') +const EDITIONS_CONFIG = resolve(ROOT, 'plugins/changelog-editions.json') + +/** 读取发行版条目:章节目录、封面目录、引用前缀与品牌 */ +function loadEdition(id) { + const { editions } = JSON.parse(readFileSync(EDITIONS_CONFIG, 'utf-8')) + const entry = Array.isArray(editions) ? editions.find(e => e && e.id === id) : undefined + if (!entry) { + console.error(`错误: ${EDITIONS_CONFIG} 中没有发行版 ${id}`) + process.exit(1) + } + for (const field of ['zhSourceDir', 'enSourceDir', 'coverImageDir', 'coverImageUrlPrefix', 'coverBrandText']) { + if (typeof entry[field] !== 'string' || entry[field] === '') { + console.error(`错误: 发行版 ${id} 缺少 ${field}`) + process.exit(1) + } + } + return { + changelogDirZh: resolve(ROOT, entry.zhSourceDir), + changelogDirEn: resolve(ROOT, entry.enSourceDir), + imgDir: resolve(ROOT, entry.coverImageDir), + imgUrlPrefix: entry.coverImageUrlPrefix.replace(/\/+$/, ''), + brand: { + text: entry.coverBrandText, + website: typeof entry.coverWebsite === 'string' && entry.coverWebsite !== '' ? entry.coverWebsite : null, + }, + } +} // 输出宣传图尺寸(文字叠加层布局按此尺寸计算) const WIDTH = 2848 @@ -106,16 +134,33 @@ const COLORS = { function parseArgs() { const args = process.argv.slice(2) - const version = args.find(a => !a.startsWith('--')) - const force = args.includes('--force') + let version + let force = false + let edition = 'desirecore' + for (let i = 0; i < args.length; i++) { + if (args[i] === '--force') { + force = true + } else if (args[i] === '--edition') { + edition = args[++i] + if (!edition || edition.startsWith('--')) { + console.error('错误: --edition 缺少取值') + process.exit(1) + } + } else if (args[i].startsWith('--')) { + console.error(`错误: 未知参数 ${args[i]}`) + process.exit(1) + } else if (version === undefined) { + version = args[i] + } + } if (!version) { - console.error('用法: node scripts/generate-changelog-cover.mjs [--force]') + console.error('用法: node scripts/generate-changelog-cover.mjs [--force] [--edition ]') console.error('示例: node scripts/generate-changelog-cover.mjs v10.0.16') process.exit(1) } - return { version, force } + return { version, force, edition } } function checkApiKey() { @@ -396,7 +441,7 @@ function buildListColumn(items, opts) { * * 三阶段:1) 收集测量 2) 计算布局 3) 渲染 SVG */ -function buildTextOverlay(version, sections, lang = 'zh') { +function buildTextOverlay(version, sections, lang = 'zh', brand = { text: 'DESIRECORE', website: 'www.desirecore.cn' }) { const { features, fixes, improvements, date } = sections // 中英文标签 @@ -622,7 +667,7 @@ function buildTextOverlay(version, sections, lang = 'zh') { - DESIRECORE + ${escapeXml(brand.text)} ${escapeXml(version)} @@ -646,9 +691,9 @@ ${fixSvg} ${escapeXml(statsText)} ${escapeXml(date)} - - www.desirecore.cn - www.desirecore.cn + ${brand.website ? ` + ${escapeXml(brand.website)} + ${escapeXml(brand.website)}` : ''} ` } @@ -691,7 +736,8 @@ function insertImageRef(content, version, imgPath) { } async function main() { - const { version, force } = parseArgs() + const { version, force, edition } = parseArgs() + const { changelogDirZh: CHANGELOG_DIR_ZH, changelogDirEn: CHANGELOG_DIR_EN, imgDir: IMG_DIR, imgUrlPrefix, brand } = loadEdition(edition) const bgFile = process.env.IMAGE_BG_FILE const apiKey = bgFile ? null : checkApiKey() @@ -704,8 +750,8 @@ async function main() { const zhImgFile = resolve(IMG_DIR, `${version}.png`) const enImgFile = resolve(IMG_DIR, `${version}-en.png`) - const zhImgRefPath = `/img/changelog/${version}.png` - const enImgRefPath = `/img/changelog/${version}-en.png` + const zhImgRefPath = `${imgUrlPrefix}/${version}.png` + const enImgRefPath = `${imgUrlPrefix}/${version}-en.png` if (existsSync(zhImgFile) && existsSync(enImgFile) && !force) { console.log(`图片已存在: ${zhImgFile}, ${enImgFile}`) @@ -754,7 +800,7 @@ async function main() { // 2. 生成中文配图 console.log('正在合成中文宣传图...') - const zhOverlaySvg = buildTextOverlay(version, zhSections, 'zh') + const zhOverlaySvg = buildTextOverlay(version, zhSections, 'zh', brand) const zhFinalBuffer = await compositeImage(bgBuffer, zhOverlaySvg) writeFileSync(zhImgFile, zhFinalBuffer) console.log(`中文宣传图已保存: ${zhImgFile} (${(zhFinalBuffer.length / 1024 / 1024).toFixed(1)} MB)`) @@ -778,7 +824,7 @@ async function main() { console.log(` Date: ${enSections.date}\n`) console.log('正在合成英文宣传图...') - const enOverlaySvg = buildTextOverlay(version, enSections, 'en') + const enOverlaySvg = buildTextOverlay(version, enSections, 'en', brand) const enFinalBuffer = await compositeImage(bgBuffer, enOverlaySvg) writeFileSync(enImgFile, enFinalBuffer) console.log(`英文宣传图已保存: ${enImgFile} (${(enFinalBuffer.length / 1024 / 1024).toFixed(1)} MB)`)