An image the manifest does not publish was rewritten to a raw.githubusercontent URL, which serves a private repository to nobody: raw answers 404 unauthenticated, and a github.com session does not authenticate it. Every image on the site was broken for every reader, and no gate could say so — link checking asks whether the file exists in the repository, not whether a site reader can fetch it. The projector now copies a referenced image into the generated tree beside the page and points the reference at it, so Vite bundles it like any other site asset and repository visibility cannot reach the published page. The copy lands beside the page so one relative URL is right from both locale trees and a page's assets leave with the page; two sources projecting onto one path throw rather than letting the last copy win. Canonical Markdown keeps writing ordinary repository-relative paths, so the same file still renders when read in the repository. The guide this was found through is renamed to its subject: 配置模型.
367 lines
14 KiB
TypeScript
367 lines
14 KiB
TypeScript
/**
|
|
* Canonical publication manifest for the documentation website.
|
|
*
|
|
* Markdown stays in its owning repository tier. This manifest maps each
|
|
* canonical source into matching route trees for both site locales; when a
|
|
* translation is absent, both routes intentionally project the available
|
|
* source instead of copying Markdown.
|
|
*/
|
|
|
|
/** Locale key used by the VitePress site. */
|
|
export type DocsLocale = 'root' | 'en'
|
|
|
|
/** Sidebar collection rendered for one locale and top-level module. */
|
|
type DocsSidebar =
|
|
| 'zh-guide'
|
|
| 'zh-develop'
|
|
| 'zh-reference'
|
|
| 'en-guide'
|
|
| 'en-develop'
|
|
| 'en-reference'
|
|
|
|
/** A page projected into the VitePress source tree. */
|
|
export interface DocsPage {
|
|
/** VitePress locale whose route tree owns this projection. */
|
|
locale: DocsLocale
|
|
/** Language of the canonical source currently projected at this route. */
|
|
contentLocale: 'zh-CN' | 'en-US'
|
|
/** Repository-relative canonical Markdown source. */
|
|
source: string
|
|
/** VitePress route, including the `.md` suffix. */
|
|
route: string
|
|
/** Navigation label shown in the sidebar. */
|
|
label: string
|
|
/** Sidebar collection that owns the page, or null for a locale home page. */
|
|
sidebar: DocsSidebar | null
|
|
/** Section label within the sidebar. */
|
|
section: string
|
|
/** Stable order within the section. */
|
|
order: number
|
|
/** Heading levels included in this page's VitePress outline. */
|
|
outline?: number | readonly [number, number] | 'deep' | false
|
|
/** Additional repository paths that resolve to this page. */
|
|
sourceAliases?: string[]
|
|
}
|
|
|
|
interface MirroredPage {
|
|
source: string | Record<DocsLocale, string>
|
|
route: string
|
|
contentLocale: DocsPage['contentLocale'] | Record<DocsLocale, DocsPage['contentLocale']>
|
|
label: Record<DocsLocale, string>
|
|
sidebar: Record<DocsLocale, DocsSidebar | null>
|
|
section: Record<DocsLocale, string>
|
|
order: number
|
|
outline?: DocsPage['outline']
|
|
sourceAliases?: string[] | Partial<Record<DocsLocale, string[]>>
|
|
}
|
|
|
|
type PairedPage = Omit<MirroredPage, 'source' | 'contentLocale' | 'sourceAliases'> & {
|
|
/** English side of a sibling `foo.md` / `foo.zh.md` pair. */
|
|
source: string
|
|
/** Language-neutral repository aliases, such as the directory of an index page. */
|
|
sourceAliases?: string[]
|
|
}
|
|
|
|
function localized<T>(value: T | Record<DocsLocale, T>, locale: DocsLocale): T {
|
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
? (value as Record<DocsLocale, T>)[locale]
|
|
: value
|
|
}
|
|
|
|
function mirroredPages(pages: MirroredPage[]): DocsPage[] {
|
|
return pages.flatMap(page => (['root', 'en'] as const).map((locale) => {
|
|
const aliases = page.sourceAliases === undefined
|
|
? undefined
|
|
: Array.isArray(page.sourceAliases) ? page.sourceAliases : page.sourceAliases[locale]
|
|
return {
|
|
locale,
|
|
contentLocale: localized(page.contentLocale, locale),
|
|
source: localized(page.source, locale),
|
|
route: locale === 'root' ? page.route : `en/${page.route}`,
|
|
label: page.label[locale],
|
|
sidebar: page.sidebar[locale],
|
|
section: page.section[locale],
|
|
order: page.order,
|
|
...(page.outline === undefined ? {} : { outline: page.outline }),
|
|
...(aliases === undefined ? {} : { sourceAliases: aliases }),
|
|
}
|
|
}))
|
|
}
|
|
|
|
function pairedPages(pages: PairedPage[]): DocsPage[] {
|
|
return mirroredPages(pages.map((page) => {
|
|
const chineseSource = page.source.replace(/\.md$/, '.zh.md')
|
|
const sharedAliases = page.sourceAliases ?? []
|
|
return {
|
|
...page,
|
|
source: { root: chineseSource, en: page.source },
|
|
contentLocale: { root: 'zh-CN', en: 'en-US' },
|
|
sourceAliases: {
|
|
root: [...sharedAliases, page.source],
|
|
en: [...sharedAliases, chineseSource],
|
|
},
|
|
}
|
|
}))
|
|
}
|
|
|
|
const homeAndGuide = pairedPages([
|
|
{
|
|
source: 'docs/user/index.md',
|
|
route: 'index.md',
|
|
label: { root: 'DeepSeek Harness', en: 'DeepSeek Harness' },
|
|
sidebar: { root: null, en: null },
|
|
section: { root: '首页', en: 'Home' },
|
|
order: 0,
|
|
},
|
|
{
|
|
source: 'docs/user/guide/index.md',
|
|
route: 'guide/index.md',
|
|
label: { root: '介绍', en: 'Introduction' },
|
|
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
|
section: { root: '入门', en: 'Guide' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/guide'],
|
|
},
|
|
{
|
|
source: 'docs/user/guide/quickstart.md',
|
|
route: 'guide/quickstart.md',
|
|
label: { root: '快速开始', en: 'Quick start' },
|
|
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
|
section: { root: '入门', en: 'Guide' },
|
|
order: 2,
|
|
},
|
|
{
|
|
source: 'docs/user/guide/providers.md',
|
|
route: 'guide/providers.md',
|
|
label: { root: '配置模型', en: 'Configure models' },
|
|
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
|
section: { root: '入门', en: 'Guide' },
|
|
order: 3,
|
|
},
|
|
{
|
|
source: 'docs/user/guide/config.md',
|
|
route: 'guide/config.md',
|
|
label: { root: '配置文件', en: 'Configuration' },
|
|
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
|
section: { root: '入门', en: 'Guide' },
|
|
order: 4,
|
|
},
|
|
])
|
|
|
|
const develop = pairedPages([
|
|
{
|
|
source: 'docs/user/develop/basic/index.md',
|
|
route: 'develop/basic/index.md',
|
|
label: { root: '第一个插件', en: 'First plugin' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '基础', en: 'Basics' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/develop/basic'],
|
|
},
|
|
{
|
|
source: 'docs/user/develop/basic/tool.md',
|
|
route: 'develop/basic/tool.md',
|
|
label: { root: '开发一个 Tool', en: 'Build a tool' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '基础', en: 'Basics' },
|
|
order: 2,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/basic/config.md',
|
|
route: 'develop/basic/config.md',
|
|
label: { root: '插件配置', en: 'Plugin configuration' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '基础', en: 'Basics' },
|
|
order: 3,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/framework/index.md',
|
|
route: 'develop/framework/index.md',
|
|
label: { root: '插件与生命周期', en: 'Plugin lifecycle' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '框架能力', en: 'Framework' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/develop/framework'],
|
|
},
|
|
{
|
|
source: 'docs/user/develop/framework/service.md',
|
|
route: 'develop/framework/service.md',
|
|
label: { root: '服务与依赖', en: 'Services and dependencies' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '框架能力', en: 'Framework' },
|
|
order: 2,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/framework/events.md',
|
|
route: 'develop/framework/events.md',
|
|
label: { root: '事件系统', en: 'Event system' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '框架能力', en: 'Framework' },
|
|
order: 3,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/practice/index.md',
|
|
route: 'develop/practice/index.md',
|
|
label: { root: '能力的三层拆分', en: 'Capability layering' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '实战', en: 'Practice' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/develop/practice'],
|
|
},
|
|
{
|
|
source: 'docs/user/develop/practice/llm-adapter.md',
|
|
route: 'develop/practice/llm-adapter.md',
|
|
label: { root: 'LLM 适配器', en: 'LLM adapter' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '实战', en: 'Practice' },
|
|
order: 2,
|
|
},
|
|
])
|
|
|
|
const cordisTutorial = mirroredPages(([
|
|
['index.md', 'Cordis 教程', 'Cordis tutorial'],
|
|
['01-first-plugin.md', '1. 第一个插件', '1. Your first plugin'],
|
|
['02-lifecycle-and-effects.md', '2. 生命周期与副作用', '2. Lifecycle and effects'],
|
|
['03-services.md', '3. 服务', '3. Services'],
|
|
['04-events.md', '4. 事件', '4. Events'],
|
|
['05-config.md', '5. 配置', '5. Configuration'],
|
|
['06-composition-and-hmr.md', '6. 组合与热重载', '6. Composition and HMR'],
|
|
['07-into-the-harness.md', '7. 进入 Harness', '7. Into the harness'],
|
|
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
|
|
source: `docs/cordis-tutorial/${file}`,
|
|
route: `develop/cordis-tutorial/${file}`,
|
|
contentLocale: 'en-US',
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: 'Cordis 教程', en: 'Cordis tutorial' },
|
|
order,
|
|
...(file === 'index.md' ? { sourceAliases: ['docs/cordis-tutorial'] } : {}),
|
|
})))
|
|
|
|
const cordisPrimerReference = pairedPages([
|
|
{
|
|
source: 'docs/cordis-primer.md',
|
|
route: 'reference/cordis-primer.md',
|
|
label: { root: 'Cordis 入门', en: 'Cordis primer' },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '概念', en: 'Concepts' },
|
|
order: 1,
|
|
},
|
|
])
|
|
|
|
const coreDataReference = pairedPages(([
|
|
['core.md', '核心数据结构', 'Core data structures', 0],
|
|
['scope.md', '作用域', 'Scopes', 1],
|
|
['session.md', '会话', 'Sessions', 2],
|
|
['system-prompt.md', '系统提示词', 'System prompts', 4],
|
|
['tools.md', '工具', 'Tools', 5],
|
|
['llm-streaming.md', 'LLM 流式响应', 'LLM streaming', 6],
|
|
['bash.md', 'Bash 执行', 'Bash execution', 7],
|
|
['filesystem.md', '文件系统', 'Filesystem', 9],
|
|
['code-runtime.md', '代码运行时', 'Code runtime', 10],
|
|
['compaction.md', '上下文压缩', 'Compaction', 11],
|
|
['subagent.md', '子代理', 'Subagents', 12],
|
|
['workflow.md', '工作流', 'Workflows', 13],
|
|
['skills.md', '技能', 'Skills', 14],
|
|
['approval.md', '审批', 'Approvals', 15],
|
|
['user-interaction.md', '用户交互', 'User interaction', 16],
|
|
['sandbox.md', '沙箱', 'Sandboxing', 18],
|
|
['web.md', 'Web 访问', 'Web access', 19],
|
|
['persistence.md', '会话持久化', 'Session persistence', 20],
|
|
['settings.md', '用户设置', 'User settings', 21],
|
|
['credentials.md', '用户凭据', 'User credentials', 22],
|
|
] as const).map(([file, rootLabel, enLabel, order]): PairedPage => ({
|
|
source: `docs/core-data-structures/${file}`,
|
|
route: `reference/core-data-structures/${file}`,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '数据结构', en: 'Data structures' },
|
|
order,
|
|
...(file === 'core.md' ? { sourceAliases: ['docs/core-data-structures'] } : {}),
|
|
})))
|
|
|
|
const reference = mirroredPages([
|
|
...([
|
|
['docs/architecture.md', 'reference/index.md', '架构', 'Architecture', 0],
|
|
['docs/capability-seams.md', 'reference/capability-seams.md', '能力服务', 'Capability services', 2],
|
|
['docs/agent-lifecycle.md', 'reference/agent-lifecycle.md', 'Agent 生命周期', 'Agent lifecycle', 3],
|
|
['docs/tool-execution-pipeline.md', 'reference/tool-execution-pipeline.md', 'Tool 执行', 'Tool execution', 4],
|
|
] as const).map(([source, route, rootLabel, enLabel, order]): MirroredPage => ({
|
|
source,
|
|
route,
|
|
contentLocale: 'en-US',
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '概念', en: 'Concepts' },
|
|
order,
|
|
})),
|
|
...([
|
|
['docs/config-catalog.md', 'reference/config-catalog.md', '插件配置', 'Plugin configuration'],
|
|
['docs/tool-catalog.md', 'reference/tool-catalog.md', 'Tool Schema', 'Tool schemas'],
|
|
['docs/cordis-catalog/services.md', 'reference/cordis-catalog/services.md', '服务', 'Services'],
|
|
['docs/cordis-catalog/events.md', 'reference/cordis-catalog/events.md', '事件', 'Events'],
|
|
['docs/persistence-catalog.md', 'reference/persistence-catalog.md', '持久化事件', 'Persistence events', 'deep'],
|
|
] as const).map(([source, route, rootLabel, enLabel, outline], order): MirroredPage => ({
|
|
source,
|
|
route,
|
|
contentLocale: 'en-US',
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '生成参考', en: 'Generated reference' },
|
|
order,
|
|
...(outline === undefined ? {} : { outline }),
|
|
})),
|
|
...([
|
|
['context.md', 'Context', 'Context'],
|
|
['events.md', 'Events', 'Events'],
|
|
['fiber.md', 'Fiber', 'Fiber'],
|
|
['registry.md', 'Plugin Registry', 'Plugin Registry'],
|
|
['service.md', 'Service', 'Service'],
|
|
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
|
|
source: `docs/cordis-catalog/core/${file}`,
|
|
route: `reference/cordis-api/${file}`,
|
|
contentLocale: 'en-US',
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: 'Cordis API', en: 'Cordis Core API' },
|
|
order,
|
|
})),
|
|
...([
|
|
['goal.md', '目标', 'Goals', 3],
|
|
['pty.md', 'PTY 会话', 'PTY sessions', 8],
|
|
['commands.md', '命令', 'Human commands', 17],
|
|
] as const).map(([file, rootLabel, enLabel, order]): MirroredPage => ({
|
|
source: `docs/core-data-structures/${file}`,
|
|
route: `reference/core-data-structures/${file}`,
|
|
contentLocale: 'en-US',
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '数据结构', en: 'Data structures' },
|
|
order,
|
|
})),
|
|
...([
|
|
['adding-a-package.md', '新增 Package', 'Adding a package'],
|
|
['adding-a-tool.md', '新增 Tool', 'Adding a tool'],
|
|
['adding-an-llm-adapter.md', '新增 LLM Adapter', 'Adding an LLM adapter'],
|
|
['extension-cookbook.md', '扩展模式', 'Extension patterns'],
|
|
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
|
|
source: `docs/cookbook/${file}`,
|
|
route: `reference/cookbook/${file}`,
|
|
contentLocale: 'en-US',
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '开发手册', en: 'Cookbook' },
|
|
order,
|
|
})),
|
|
])
|
|
|
|
/** Every canonical page published by the documentation website. */
|
|
export const docsPages: DocsPage[] = [
|
|
...homeAndGuide,
|
|
...develop,
|
|
...cordisTutorial,
|
|
...cordisPrimerReference,
|
|
...coreDataReference,
|
|
...reference,
|
|
]
|