const SOURCE_KIND_LABELS = { api_missing: '等待接口', config_toml: '配置文件', local_sample: '示例数据', plan_file: '计划文件', runtime_missing: '运行数据缺失', sqlite_missing: 'SQLite 缺失', sqlite_missing_table: 'SQLite 表缺失', sqlite_partial: 'SQLite 部分可用', sqlite_readonly: 'SQLite 只读', sqlite_schema_drift: 'SQLite 结构变化', sqlite_table: 'SQLite 表', test: '测试数据', } const CONFIDENCE_LABELS = { high: '高', medium: '中', low: '低', 高: '高', 中: '中', 低: '低', } const STATUS_LABELS = { blocked: '受阻', clean: '无草稿', complete: '已完成', done: '已完成', failed: '失败', idle: '空闲', invalid: '无效', pending: '待处理', recent: '最近活跃', running: '运行中', unknown: '未知', valid: '有效', } const PARSE_STATUS_LABELS = { invalid: '解析失败', valid: '解析通过', } const TRUST_LABELS = { trusted: '受信任', untrusted: '未信任', unknown: '未知', } export function formatSourceKind(kind) { if (!kind) { return '来源未知' } return SOURCE_KIND_LABELS[kind] ?? '来源未知' } export function formatConfidence(confidence) { if (!confidence) { return '低' } return CONFIDENCE_LABELS[confidence] ?? '低' } export function formatStatus(status) { if (!status) { return '未知' } return STATUS_LABELS[status] ?? '未知' } export function formatParseStatus(status) { if (!status) { return '未知' } return PARSE_STATUS_LABELS[status] ?? formatStatus(status) } export function normalizeSource(source, fallback = {}) { const kind = source?.kind ?? fallback.kind ?? 'api_missing' const confidence = source?.confidence ?? fallback.confidence ?? 'low' return { kind, label: formatSourceKind(kind), confidence, confidenceLabel: formatConfidence(confidence), path: source?.path ?? fallback.path ?? '', message: source?.message ?? fallback.message ?? '', } } export function normalizeProject(project) { const source = normalizeSource(project?.source, { kind: 'config_toml', confidence: 'high' }) const path = project?.path ?? '' return { id: path || project?.displayName || 'project', name: project?.displayName || basename(path) || '未命名项目', path, status: project?.directoryExists ? 'complete' : 'unknown', statusZh: project?.directoryExists ? '目录存在' : '目录不可用', trust: TRUST_LABELS[project?.trustLevel] ?? '未知', directoryExists: Boolean(project?.directoryExists), source: source.label, confidence: source.confidenceLabel, sourceDetail: source, } } export function normalizeProjects(payload = {}) { const projects = Array.isArray(payload.items) ? payload.items.map(normalizeProject) : [] return { projects, isEmpty: projects.length === 0, emptyTitle: '没有项目配置', emptyText: '后端没有返回项目条目。请确认 .codex/config.toml 中是否存在项目配置;这里不会用示例数据伪装真实结果。', } } export function normalizeAgent(agent = {}) { const fileName = agent.fileName || basename(agent.filePath) || agent.id || '未命名智能体' const parseStatus = agent.parseStatus || 'unknown' const isInvalid = parseStatus === 'invalid' const name = agent.name || fileName const description = isInvalid ? `TOML 解析失败:${agent.parseError || '后端未返回具体错误'}` : agent.description || '没有描述' const source = normalizeSource(null, { kind: isInvalid ? 'api_missing' : 'config_toml', confidence: isInvalid ? 'low' : 'high', }) return { id: agent.id || fileName, file: agent.filePath || fileName, fileName, name, description, role: agent.developerInstructions || '没有角色设定字段', status: isInvalid ? 'unknown' : 'complete', statusLabel: isInvalid ? 'TOML 无效' : 'TOML 有效', parseStatus, parseStatusLabel: formatParseStatus(parseStatus), parseError: agent.parseError || '', draftStatus: agent.draftStatus || 'unknown', draftStatusLabel: formatStatus(agent.draftStatus), extraFields: agent.extraFields || {}, modifiedAt: formatDateTime(agent.modifiedAt), source: source.label, confidence: source.confidenceLabel, toml: synthesizeAgentPreview(agent, { isInvalid, description }), } } export function normalizeAgents(payload = {}) { const agents = Array.isArray(payload.items) ? payload.items.map(normalizeAgent) : [] return { agents, isEmpty: agents.length === 0, emptyTitle: '没有智能体定义', emptyText: '后端没有返回 .codex/agents/*.toml 条目。当前显示为空状态,不会把示例智能体当作真实数据。', } } export function normalizeRuntime(payload = {}) { const threads = Array.isArray(payload.items) ? payload.items : [] const goals = Array.isArray(payload.goals) ? payload.goals : [] const goalsByThread = groupBy(goals, (goal) => goal.threadId) const agents = threads.map((thread) => { const source = normalizeSource(thread.source, payload.source) const threadGoals = goalsByThread.get(thread.id) ?? [] return { id: thread.id, name: thread.role || thread.id || '未命名线程', role: thread.id, status: thread.status || 'unknown', statusZh: formatStatus(thread.status), goal: threadGoals.map((goal) => goal.goal).filter(Boolean).join(';') || '没有目标记录', process: 'SQLite 只读快照', source: source.label, confidence: source.confidenceLabel, lastActivity: thread.updatedAt || thread.createdAt || '没有时间记录', } }) const source = normalizeSource(payload.source) return { agents, threads, goals, edges: Array.isArray(payload.edges) ? payload.edges : [], source, isEmpty: threads.length === 0, emptyTitle: '没有运行线程', emptyText: source.message || '后端返回了空的运行线程列表;这里保持空状态并展示来源证据。', } } export function normalizeWorkflow(payload = {}) { const source = normalizeSource(payload.source) const phases = Array.isArray(payload.phases) ? payload.phases.filter(isDisplayPhase).map((phase) => { const phaseSource = normalizeSource(phase.source, payload.source) const phaseName = normalizePhaseName(phase.name) return { name: phaseName, status: phase.status || 'unknown', label: formatStatus(phase.status), gate: phaseName, evidence: phaseSource.label, confidence: phaseSource.confidenceLabel, source: phaseSource, } }) : [] const handoffs = Array.isArray(payload.handoffEdges) ? payload.handoffEdges.map((edge) => { const edgeSource = normalizeSource(edge.source, payload.source) return { from: edge.fromThreadId || '未知线程', to: edge.toThreadId || '未知线程', summary: edge.label || '没有交接说明', time: '后端事件', source: edgeSource.label, confidence: edgeSource.confidenceLabel, } }) : [] const edges = handoffs.map((handoff) => ({ parent: handoff.from, child: handoff.to, status: handoff.summary, source: handoff.source, confidence: handoff.confidence, })) const events = Array.isArray(payload.items) ? payload.items : [] return { events, phases, handoffs, edges, source, isEmpty: events.length === 0 && phases.length === 0 && handoffs.length === 0, emptyTitle: '没有工作流事件', emptyText: source.message || '后端返回了空的工作流事件流;这里不会回退到伪装真实的示例关系。', emptyHandoffsText: '后端没有返回交接边;当前保持空状态。', } } function synthesizeAgentPreview(agent, { isInvalid, description }) { if (isInvalid) { return `# TOML 无效,仅只读展示解析错误\n# ${description}` } const lines = [ '# 接口未返回原始 TOML;以下为只读展示的已解析字段', `name = ${quote(agent.name || '')}`, `description = ${quote(agent.description || '')}`, ] if (agent.developerInstructions) { lines.push(`角色设定 = ${quote(agent.developerInstructions)}`) } for (const [key, value] of Object.entries(agent.extraFields || {})) { lines.push(`${key} = ${quote(value)}`) } return lines.join('\n') } function basename(path) { if (!path) { return '' } return String(path).split('/').filter(Boolean).at(-1) ?? String(path) } function formatDateTime(value) { if (!value) { return '没有时间记录' } const date = new Date(value) if (Number.isNaN(date.getTime())) { return value } return new Intl.DateTimeFormat('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }).format(date) } function groupBy(items, keyOf) { const groups = new Map() for (const item of items) { const key = keyOf(item) if (!groups.has(key)) { groups.set(key, []) } groups.get(key).push(item) } return groups } function quote(value) { return JSON.stringify(String(value ?? '')) } function isDisplayPhase(phase) { const name = String(phase?.name ?? '').trim().toLowerCase() const status = String(phase?.status ?? '').trim().toLowerCase() if (!name) { return false } if (name === 'phase' || status === 'status') { return false } if (name === 'time' && status === 'phase') { return false } return true } function normalizePhaseName(name) { if (!name) { return '未命名阶段' } return /^\d+$/.test(String(name)) ? `阶段 ${name}` : String(name) }