feat: add knowledge API docs
This commit is contained in:
283
scripts/generate-api.mjs
Normal file
283
scripts/generate-api.mjs
Normal file
@@ -0,0 +1,283 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import vm from 'node:vm'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const rootDir = path.resolve(__dirname, '..')
|
||||
const dataDir = path.join(rootDir, 'src', 'data')
|
||||
const apiDir = path.join(rootDir, 'public', 'api')
|
||||
|
||||
function readJson(relativePath) {
|
||||
return JSON.parse(fs.readFileSync(path.join(rootDir, relativePath), 'utf8'))
|
||||
}
|
||||
|
||||
function ensureDir(dir) {
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
}
|
||||
|
||||
function writeJson(relativePath, data) {
|
||||
const target = path.join(apiDir, relativePath)
|
||||
ensureDir(path.dirname(target))
|
||||
fs.writeFileSync(target, `${JSON.stringify(data, null, 2)}\n`, 'utf8')
|
||||
}
|
||||
|
||||
function cleanApiDir() {
|
||||
fs.rmSync(apiDir, { recursive: true, force: true })
|
||||
ensureDir(apiDir)
|
||||
}
|
||||
|
||||
function extractConstArrayFromTs(filePath, constName) {
|
||||
const text = fs.readFileSync(filePath, 'utf8')
|
||||
const marker = `export const ${constName}`
|
||||
const markerIndex = text.indexOf(marker)
|
||||
if (markerIndex === -1) {
|
||||
throw new Error(`未找到 ${constName}`)
|
||||
}
|
||||
|
||||
const assignmentIndex = text.indexOf('=', markerIndex)
|
||||
if (assignmentIndex === -1) {
|
||||
throw new Error(`未找到 ${constName} 赋值符号`)
|
||||
}
|
||||
|
||||
const arrayStart = text.indexOf('[', assignmentIndex)
|
||||
if (arrayStart === -1) {
|
||||
throw new Error(`未找到 ${constName} 数组起点`)
|
||||
}
|
||||
|
||||
let depth = 0
|
||||
let quote = null
|
||||
let escaped = false
|
||||
let lineComment = false
|
||||
let blockComment = false
|
||||
|
||||
for (let index = arrayStart; index < text.length; index += 1) {
|
||||
const char = text[index]
|
||||
const next = text[index + 1]
|
||||
|
||||
if (lineComment) {
|
||||
if (char === '\n') lineComment = false
|
||||
continue
|
||||
}
|
||||
|
||||
if (blockComment) {
|
||||
if (char === '*' && next === '/') {
|
||||
blockComment = false
|
||||
index += 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (quote) {
|
||||
if (escaped) {
|
||||
escaped = false
|
||||
} else if (char === '\\') {
|
||||
escaped = true
|
||||
} else if (char === quote) {
|
||||
quote = null
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (char === '/' && next === '/') {
|
||||
lineComment = true
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (char === '/' && next === '*') {
|
||||
blockComment = true
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (char === '\'' || char === '"' || char === '`') {
|
||||
quote = char
|
||||
continue
|
||||
}
|
||||
|
||||
if (char === '[') depth += 1
|
||||
if (char === ']') {
|
||||
depth -= 1
|
||||
if (depth === 0) {
|
||||
const source = text.slice(arrayStart, index + 1)
|
||||
return vm.runInNewContext(`(${source})`, {}, { timeout: 1000 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`未能完整解析 ${constName}`)
|
||||
}
|
||||
|
||||
const knowledgeAreas = readJson('src/data/knowledge-areas.json').knowledgeAreas
|
||||
const processGroups = readJson('src/data/process-groups.json').processGroups
|
||||
const processes = readJson('src/data/processes.json').processes
|
||||
const artifacts = readJson('src/data/artifacts.json').artifacts
|
||||
const tools = readJson('src/data/tools.json').tools
|
||||
const performanceDomains = extractConstArrayFromTs(
|
||||
path.join(dataDir, 'performance-domains.ts'),
|
||||
'performanceDomains'
|
||||
)
|
||||
|
||||
const knowledgeAreaMap = new Map(knowledgeAreas.map((item) => [item.id, item]))
|
||||
const processGroupMap = new Map(processGroups.map((item) => [item.id, item]))
|
||||
const artifactMap = new Map(artifacts.map((item) => [item.id, item]))
|
||||
const toolMap = new Map(tools.map((item) => [item.id, item]))
|
||||
|
||||
function normalizeRef(ref) {
|
||||
if (typeof ref === 'string') return { id: ref, details: [], note: undefined }
|
||||
return {
|
||||
id: ref.id,
|
||||
details: Array.isArray(ref.detail) ? ref.detail.map((item) => ({ label: item.label })) : [],
|
||||
note: ref.note,
|
||||
}
|
||||
}
|
||||
|
||||
function processSummary(process) {
|
||||
const knowledgeArea = knowledgeAreaMap.get(process.knowledgeAreaId)
|
||||
const processGroup = processGroupMap.get(process.processGroupId)
|
||||
return {
|
||||
id: process.id,
|
||||
name: process.name,
|
||||
knowledgeAreaId: process.knowledgeAreaId,
|
||||
knowledgeAreaName: knowledgeArea?.name ?? '',
|
||||
processGroupId: process.processGroupId,
|
||||
processGroupName: processGroup?.name ?? '',
|
||||
purpose: process.purpose ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
function attachRefMeta(ref, entityMap) {
|
||||
const normalized = normalizeRef(ref)
|
||||
const entity = entityMap.get(normalized.id)
|
||||
return {
|
||||
id: normalized.id,
|
||||
name: entity?.name ?? normalized.id,
|
||||
details: normalized.details,
|
||||
...(normalized.note ? { note: normalized.note } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
function processItto(process) {
|
||||
return {
|
||||
id: process.id,
|
||||
name: process.name,
|
||||
inputs: process.inputs.map((ref) => attachRefMeta(ref, artifactMap)),
|
||||
tools: process.tools.map((ref) => attachRefMeta(ref, toolMap)),
|
||||
outputs: process.outputs.map((ref) => attachRefMeta(ref, artifactMap)),
|
||||
}
|
||||
}
|
||||
|
||||
function includesRef(refs, targetId) {
|
||||
return refs.some((ref) => normalizeRef(ref).id === targetId)
|
||||
}
|
||||
|
||||
function artifactUsage(artifact) {
|
||||
return {
|
||||
id: artifact.id,
|
||||
name: artifact.name,
|
||||
asInput: processes.filter((process) => includesRef(process.inputs, artifact.id)).map(processSummary),
|
||||
asOutput: processes.filter((process) => includesRef(process.outputs, artifact.id)).map(processSummary),
|
||||
}
|
||||
}
|
||||
|
||||
function toolUsage(tool) {
|
||||
return {
|
||||
id: tool.id,
|
||||
name: tool.name,
|
||||
usedIn: processes.filter((process) => includesRef(process.tools, tool.id)).map(processSummary),
|
||||
}
|
||||
}
|
||||
|
||||
cleanApiDir()
|
||||
|
||||
writeJson('process-groups.json', processGroups.map((item) => ({ id: item.id, name: item.name })))
|
||||
|
||||
writeJson(
|
||||
'knowledge-areas.json',
|
||||
knowledgeAreas.map((item) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
tailoringFactors: (item.tailoringFactors ?? []).map((factor) => ({
|
||||
title: factor.title,
|
||||
description: factor.description,
|
||||
})),
|
||||
}))
|
||||
)
|
||||
|
||||
for (const knowledgeArea of knowledgeAreas) {
|
||||
writeJson(
|
||||
`knowledge-areas/${knowledgeArea.id}/tailoring-factors.json`,
|
||||
(knowledgeArea.tailoringFactors ?? []).map((factor) => ({
|
||||
title: factor.title,
|
||||
description: factor.description,
|
||||
}))
|
||||
)
|
||||
|
||||
writeJson(
|
||||
`knowledge-areas/${knowledgeArea.id}/processes.json`,
|
||||
processes
|
||||
.filter((process) => process.knowledgeAreaId === knowledgeArea.id)
|
||||
.map((process) => {
|
||||
const summary = processSummary(process)
|
||||
return {
|
||||
id: summary.id,
|
||||
name: summary.name,
|
||||
processGroupId: summary.processGroupId,
|
||||
processGroupName: summary.processGroupName,
|
||||
purpose: summary.purpose,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
for (const processGroup of processGroups) {
|
||||
writeJson(
|
||||
`process-groups/${processGroup.id}/processes.json`,
|
||||
processes
|
||||
.filter((process) => process.processGroupId === processGroup.id)
|
||||
.map((process) => {
|
||||
const summary = processSummary(process)
|
||||
return {
|
||||
id: summary.id,
|
||||
name: summary.name,
|
||||
knowledgeAreaId: summary.knowledgeAreaId,
|
||||
knowledgeAreaName: summary.knowledgeAreaName,
|
||||
purpose: summary.purpose,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
writeJson('processes.json', processes.map(processSummary))
|
||||
|
||||
for (const process of processes) {
|
||||
writeJson(`processes/${process.id}.json`, processSummary(process))
|
||||
writeJson(`processes/${process.id}/itto.json`, processItto(process))
|
||||
}
|
||||
|
||||
writeJson('performance-domains.json', performanceDomains.map((item) => ({ id: item.id, name: item.name })))
|
||||
|
||||
for (const domain of performanceDomains) {
|
||||
writeJson(`performance-domains/${domain.id}.json`, {
|
||||
id: domain.id,
|
||||
name: domain.name,
|
||||
expectedGoals: domain.detail?.expectedGoals ?? [],
|
||||
keyPoints: domain.detail?.keyPoints ?? [],
|
||||
interactions: domain.detail?.interactions ?? [],
|
||||
checks: domain.detail?.checks ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
writeJson('artifacts.json', artifacts.map((item) => ({ id: item.id, name: item.name })))
|
||||
writeJson('tools.json', tools.map((item) => ({ id: item.id, name: item.name })))
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
writeJson(`artifacts/${artifact.id}/usage.json`, artifactUsage(artifact))
|
||||
}
|
||||
|
||||
for (const tool of tools) {
|
||||
writeJson(`tools/${tool.id}/usage.json`, toolUsage(tool))
|
||||
}
|
||||
|
||||
console.log(`已生成静态 API 文件:${path.relative(rootDir, apiDir)}`)
|
||||
Reference in New Issue
Block a user