mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
feat(i18n): localize planner and authority diagnostics
This commit is contained in:
168
i18n/en-US.js
168
i18n/en-US.js
@@ -279,6 +279,94 @@ export default {
|
||||
"llm.providerHelp.normalizedUrl": "Normalized URL: {apiUrl}",
|
||||
"llm.providerHelp.transport": "Transport: {transport}",
|
||||
|
||||
"authority.audit.blobBehindSql": "Blob checkpoint is behind Authority SQL: {blobRevision} < {sqlRevision}",
|
||||
"authority.audit.blobCheckpointMissing": "Authority Blob has no usable checkpoint yet",
|
||||
"authority.audit.blobReadFailed": "Authority Blob read failed: {error}",
|
||||
"authority.audit.blobRuntimeDrift": "Blob checkpoint revision differs from runtime: {blobRevision} ≠ {runtimeRevision}",
|
||||
"authority.audit.chatIdMismatch": "Checkpoint chatId mismatch: {blobChatId} ≠ {chatId}",
|
||||
"authority.audit.collectionMismatch": "Trivium collection/namespace differs from runtime: {triviumNamespace} ≠ {runtimeCollectionId}",
|
||||
"authority.audit.sqlProbeFailed": "Authority SQL probe failed: {error}",
|
||||
"authority.audit.sqlRuntimeDrift": "SQL revision differs from runtime: {sqlRevision} ≠ {runtimeRevision}",
|
||||
"authority.audit.triviumBehindSql": "Trivium vector replica is behind Authority SQL: {triviumRevision} < {sqlRevision}",
|
||||
"authority.audit.triviumProbeFailed": "Authority Trivium probe failed: {error}",
|
||||
"authority.audit.triviumSqlDrift": "Trivium revision differs from SQL: {triviumRevision} ≠ {sqlRevision}",
|
||||
"authority.audit.vectorDirty": "The current vector index is still dirty",
|
||||
"authority.action.disasterRecovery": "Disaster recovery: overwrite SQL from Checkpoint",
|
||||
"authority.action.reaudit": "Re-audit",
|
||||
"authority.action.syncCheckpoint": "Sync backup Checkpoint",
|
||||
"authority.action.syncTrivium": "Sync vector/Trivium replica",
|
||||
"authority.button.captureBaseline": "Capture Perf Baseline",
|
||||
"authority.button.disasterRecovery": "Disaster recovery: Checkpoint overwrites SQL",
|
||||
"authority.button.exportDiagnostics": "Export diagnostics bundle",
|
||||
"authority.button.refreshArtifacts": "Refresh artifact list",
|
||||
"authority.button.runAudit": "Run Authority Audit",
|
||||
"authority.button.runRepair": "Run Replica Sync",
|
||||
"authority.button.syncCheckpoint": "Sync Checkpoint",
|
||||
"authority.button.syncTrivium": "Sync Authority Trivium",
|
||||
"authority.confirm.checkpointRestore": "Disaster recovery will overwrite Authority SQL using the Blob Checkpoint.\n\nSQL rev: {sqlRevision}\nCheckpoint rev: {checkpointRevision}\n\nOnly continue if SQL is missing, corrupted, or an explicit rollback is needed. Proceed?",
|
||||
"authority.confirm.deleteArtifact": "Delete this diagnostics artifact?\n{path}",
|
||||
"authority.confirm.repairPlan": "The replica sync plan will run in this order:\n{steps}\n\nIt includes restoring SQL from a Blob Checkpoint. This is only appropriate when SQL is missing, corrupted, or needs rollback. Continue?",
|
||||
"authority.diagnostics.artifactsRefreshFailed": "Artifact list refresh failed: {error}",
|
||||
"authority.diagnostics.noArtifacts": "Recent artifact list refreshed, but no diagnostics bundle records are available",
|
||||
"authority.diagnostics.notYetRefreshed": "Diagnostics artifact list has not been refreshed yet",
|
||||
"authority.diagnostics.recentArtifacts": "Recent diagnostics artifacts",
|
||||
"authority.repair.disasterRecovery": "Disaster recovery: restore SQL from Blob Checkpoint",
|
||||
"authority.repair.disasterRecovery.detail": "Only use Blob checkpoint restore when SQL is missing, corrupted, or the user explicitly needs to roll back.",
|
||||
"authority.repair.noStepsNeeded": "The current audit found no automatically orchestrated repair steps",
|
||||
"authority.repair.none": "No repair orchestration needed",
|
||||
"authority.repair.summary.detail": "Suggested sync: {steps}",
|
||||
"authority.repair.summaryLabel": "Suggested replica sync: {count} steps",
|
||||
"authority.repair.syncCheckpoint": "Sync backup Checkpoint",
|
||||
"authority.repair.syncCheckpoint.detail": "Authority Blob checkpoint is behind or missing; sync a new backup checkpoint from the current authoritative graph source.",
|
||||
"authority.repair.syncTrivium": "Sync vector/Trivium replica",
|
||||
"authority.repair.syncTrivium.detail": "The Trivium vector replica is behind, the collection mismatches, or the current vector index is dirty. Rebuild/sync it from the authoritative graph source.",
|
||||
"authority.repair.resultHandoff": "handed off to async Job",
|
||||
"authority.repair.resultSteps": "{count} steps",
|
||||
"authority.repair.status.error": "Sync failed",
|
||||
"authority.repair.status.handoff": "Waiting for Job handoff",
|
||||
"authority.repair.status.idle": "Not run",
|
||||
"authority.repair.status.running": "Syncing",
|
||||
"authority.repair.status.success": "Sync complete",
|
||||
"authority.repair.status.warning": "Partial sync failed",
|
||||
"authority.restore.error": "Restore failed",
|
||||
"authority.restore.idle": "Not run",
|
||||
"authority.restore.running": "Restoring",
|
||||
"authority.restore.success": "Restored",
|
||||
"authority.summary.aligned": "Authority artifacts are aligned",
|
||||
"authority.summary.alignedDetail": "Authority SQL / Trivium / Blob are aligned within the current observable state",
|
||||
"authority.summary.auditRunning": "Authority audit running",
|
||||
"authority.summary.blockingInconsistency": "Blocking inconsistency detected",
|
||||
"authority.summary.driftPending": "Pending drift detected",
|
||||
"authority.summary.notYetAudited": "Audit has not run yet",
|
||||
"authority.summary.replicasPendingSync": "Replicas pending sync",
|
||||
"authority.summary.waitingForAudit": "Waiting for audit",
|
||||
"authority.toast.artifactsRefreshed": "Diagnostics artifact list refreshed ({count} entries)",
|
||||
"authority.toast.artifactsRefreshFailed": "Diagnostics artifact list refresh failed: {error}",
|
||||
"authority.toast.auditCompleted": "Authority audit completed",
|
||||
"authority.toast.auditFailed": "Authority audit failed: {error}",
|
||||
"authority.toast.auditRunning": "Authority consistency audit running…",
|
||||
"authority.toast.baselineCaptured": "Authority Perf Baseline captured",
|
||||
"authority.toast.baselineCaptureFailed": "Authority Perf Baseline capture failed: {error}",
|
||||
"authority.toast.checkpointRestored": "Authority Checkpoint restored: rev {revision}",
|
||||
"authority.toast.checkpointRestoreFailed": "Authority Checkpoint restore failed: {error}",
|
||||
"authority.toast.checkpointRestoring": "Authority Checkpoint restoring…",
|
||||
"authority.toast.checkpointWritten": "Authority Checkpoint written: rev {revision}",
|
||||
"authority.toast.checkpointWriteFailed": "Authority Checkpoint write failed: {error}",
|
||||
"authority.toast.checkpointWriting": "Authority Checkpoint writing…",
|
||||
"authority.toast.copyPathFailed": "Copy path failed: {error}",
|
||||
"authority.toast.diagnosticDeleted": "Diagnostics bundle deleted",
|
||||
"authority.toast.diagnosticDeleteFailed": "Diagnostics bundle delete failed: {error}",
|
||||
"authority.toast.diagnosticDownloaded": "Diagnostics bundle downloaded",
|
||||
"authority.toast.diagnosticDownloadFailed": "Diagnostics bundle download failed: {error}",
|
||||
"authority.toast.diagnosticPathCopied": "Diagnostics bundle path copied",
|
||||
"authority.toast.diagnosticReadFailed": "Diagnostics bundle read failed: {error}",
|
||||
"authority.toast.repairCompleted": "Authority replica sync completed ({count} steps)",
|
||||
"authority.toast.repairFailed": "Authority replica sync failed: {error}",
|
||||
"authority.toast.repairHandedOff": "Authority replica sync handed off to async Job ({count} steps)",
|
||||
"authority.toast.repairPartialFailure": "Authority replica sync partially failed; memory graph is unaffected ({count} steps)",
|
||||
"authority.toast.repairRunning": "Authority replica sync running…",
|
||||
"authority.toast.triviumRebuildFailed": "Authority Trivium rebuild failed: {error}",
|
||||
|
||||
"memory.type.character": "Character",
|
||||
"memory.type.event": "Event",
|
||||
"memory.type.location": "Location",
|
||||
@@ -304,4 +392,84 @@ export default {
|
||||
"storyTime.meta": "Story time: {time}",
|
||||
"storyTime.mixed": "mixed",
|
||||
"storyTime.mixedTime": "Mixed time",
|
||||
|
||||
"planner.llmPreset.global": "Follow Global API",
|
||||
"planner.llmPreset.legacy": "Legacy ENA Dedicated Connection (Compat)",
|
||||
"planner.llmPreset.legacyWarning": "Still using the legacy ENA dedicated connection. Switching to global or a preset will discard this hidden config.",
|
||||
"planner.llmPreset.missingPresetFallback": "Fell back to Global: preset {name} not found",
|
||||
"planner.llmPreset.switchedToGlobal": "Switched to follow Global BME API",
|
||||
"planner.llmPreset.keepingLegacy": "Keeping legacy ENA dedicated connection",
|
||||
"planner.llmPreset.presetNotFound": "Selected API preset does not exist; fell back to Global",
|
||||
"planner.llmPreset.switchedToPreset": "Switched to API preset: {name}",
|
||||
|
||||
"planner.promptBlock.namePlaceholder": "Block name",
|
||||
"planner.promptBlock.contentPlaceholder": "Prompt content…",
|
||||
"planner.promptBlock.moveUp": "Move up",
|
||||
"planner.promptBlock.moveDown": "Move down",
|
||||
"planner.promptBlock.deleteBlock": "Delete block",
|
||||
"planner.promptBlock.newBlock": "New block",
|
||||
"planner.promptBlock.confirmReset": "Reset prompt blocks to default? Current blocks will be overwritten.",
|
||||
|
||||
"planner.template.selectPlaceholder": "-- Select template --",
|
||||
"planner.template.newTemplateName": "New template name",
|
||||
"planner.template.selectOrCreateFirst": "Please select or create a template first",
|
||||
|
||||
"planner.log.noLogs": "No logs yet",
|
||||
"planner.log.success": "OK",
|
||||
"planner.log.failure": "Failed",
|
||||
"planner.log.noMessages": "No messages",
|
||||
"planner.log.requestMessages": "Request messages ({count})",
|
||||
"planner.log.rawReply": "Raw reply",
|
||||
"planner.log.filteredReply": "Filtered reply",
|
||||
"planner.log.confirmClear": "Clear all logs?",
|
||||
|
||||
"planner.status.enabled": "Enabled",
|
||||
"planner.status.disabled": "Disabled",
|
||||
"planner.status.ready": "Ready",
|
||||
"planner.status.saving": "Saving…",
|
||||
"planner.status.saved": "Saved",
|
||||
"planner.status.saveFailed": "Save failed",
|
||||
"planner.status.apiNotReady": "API not ready",
|
||||
"planner.status.testing": "Testing…",
|
||||
"planner.status.testComplete": "Planner test completed",
|
||||
"planner.status.testFailed": "Planner test failed",
|
||||
"planner.status.resetting": "Resetting…",
|
||||
"planner.status.resetToDefault": "Restored defaults",
|
||||
"planner.status.resetFailed": "Reset failed",
|
||||
"planner.status.moduleNotLoaded": "Module not loaded",
|
||||
"planner.status.unavailable": "Unavailable",
|
||||
"planner.status.fetchModelsFailed": "Failed to fetch models",
|
||||
"planner.status.noModelsFetched": "No models fetched",
|
||||
"planner.status.modelsFetched": "Fetched {count} model(s)",
|
||||
"planner.status.fetchingModels": "Fetching models…",
|
||||
|
||||
"planner.apiKey.hide": "Hide",
|
||||
"planner.apiKey.show": "Show",
|
||||
|
||||
"planner.model.selectFromList": "-- Select from list --",
|
||||
|
||||
"planner.taskPreset.workspaceNotFound": "Task preset workspace not found. Please switch to Tasks → Planner manually.",
|
||||
"planner.taskPreset.workspaceSwitched": "Switched to Tasks → Planner preset editor",
|
||||
|
||||
"planner.debug.diagnosing": "Diagnosing…",
|
||||
"planner.debug.failed": "Diagnostics failed",
|
||||
|
||||
"authority.mode.standalone": "Frontend-only mode",
|
||||
"authority.mode.standalone.meta": "No server-side enhancement detected; BME will continue running locally",
|
||||
"authority.mode.standalone.disabled.meta": "Server-side enhancement is disabled; BME will continue running locally",
|
||||
"authority.mode.standalone.noAuthority.meta": "No DOA/Authority detected; automatically using the local stable path",
|
||||
"authority.mode.probing": "Probing",
|
||||
"authority.mode.probing.meta": "Detecting server-side enhancement capabilities",
|
||||
"authority.mode.shadow": "Server shadow sync",
|
||||
"authority.mode.shadow.meta": "DOA/Authority available, but currently prioritizing the local path",
|
||||
"authority.mode.candidate": "Server enhancement preparing",
|
||||
"authority.mode.candidate.meta.storageReady": "Graph server storage available; vector enhancement still awaiting capability confirmation",
|
||||
"authority.mode.candidate.meta.vectorReady": "Vector server capability available; graph server storage still awaiting capability confirmation",
|
||||
"authority.mode.enhanced": "Server enhancement enabled",
|
||||
"authority.mode.enhanced.meta.manifestReady": "Graph and vector storage enhanced; server vector manifest available",
|
||||
"authority.mode.enhanced.meta.noManifest": "Graph and vector storage enhanced; awaiting BME vector manifest capability",
|
||||
"authority.mode.enhanced.meta.noJobs": "Graph and vector storage enhanced; server job capability unavailable",
|
||||
"authority.mode.degraded": "Automatically rolled back",
|
||||
"authority.mode.degraded.unhealthy.meta": "Server enhancement temporarily unavailable: {reason}",
|
||||
"authority.mode.degraded.capabilityNotReady.meta": "DOA/Authority connected, but critical capabilities not ready: {reason}",
|
||||
};
|
||||
|
||||
@@ -170,6 +170,23 @@ export function formatUiStatusMeta(status) {
|
||||
return status?.meta ?? "";
|
||||
}
|
||||
|
||||
export function formatI18nValue(source = null, {
|
||||
keyField = "key",
|
||||
paramsField = "params",
|
||||
fallbackField = "fallback",
|
||||
fallback = "",
|
||||
} = {}) {
|
||||
if (typeof source === "string") return source;
|
||||
if (!source || typeof source !== "object") return fallback;
|
||||
const key = String(source?.[keyField] || "").trim();
|
||||
if (!key) {
|
||||
return String(source?.[fallbackField] ?? fallback ?? "");
|
||||
}
|
||||
return t(key, source?.[paramsField] || {}, {
|
||||
fallback: source?.[fallbackField] ?? fallback ?? key,
|
||||
});
|
||||
}
|
||||
|
||||
// Keep module import side-effect stable for non-UI tests/modules. Runtime UI
|
||||
// entry points explicitly call setLocale(settings.uiLocale), where `auto` may
|
||||
// resolve from the host/navigator environment.
|
||||
|
||||
168
i18n/zh-CN.js
168
i18n/zh-CN.js
@@ -279,6 +279,94 @@ export default {
|
||||
"llm.providerHelp.normalizedUrl": "规范化地址:{apiUrl}",
|
||||
"llm.providerHelp.transport": "请求通道:{transport}",
|
||||
|
||||
"authority.audit.blobBehindSql": "Blob checkpoint 落后于 Authority SQL:{blobRevision} < {sqlRevision}",
|
||||
"authority.audit.blobCheckpointMissing": "Authority Blob 尚无可用 checkpoint",
|
||||
"authority.audit.blobReadFailed": "Authority Blob 读取失败:{error}",
|
||||
"authority.audit.blobRuntimeDrift": "Blob checkpoint revision 与 runtime 不一致:{blobRevision} ≠ {runtimeRevision}",
|
||||
"authority.audit.chatIdMismatch": "Checkpoint chatId 不匹配:{blobChatId} ≠ {chatId}",
|
||||
"authority.audit.collectionMismatch": "Trivium collection/namespace 与 runtime 不一致:{triviumNamespace} ≠ {runtimeCollectionId}",
|
||||
"authority.audit.sqlProbeFailed": "Authority SQL 探针失败:{error}",
|
||||
"authority.audit.sqlRuntimeDrift": "SQL revision 与 runtime 不一致:{sqlRevision} ≠ {runtimeRevision}",
|
||||
"authority.audit.triviumBehindSql": "Trivium 向量副本落后于 Authority SQL:{triviumRevision} < {sqlRevision}",
|
||||
"authority.audit.triviumProbeFailed": "Authority Trivium 探针失败:{error}",
|
||||
"authority.audit.triviumSqlDrift": "Trivium revision 与 SQL 不一致:{triviumRevision} ≠ {sqlRevision}",
|
||||
"authority.audit.vectorDirty": "当前向量索引仍处于 dirty 状态",
|
||||
"authority.action.disasterRecovery": "灾难恢复:从 Checkpoint 覆盖 SQL",
|
||||
"authority.action.reaudit": "重新审计",
|
||||
"authority.action.syncCheckpoint": "同步备份 Checkpoint",
|
||||
"authority.action.syncTrivium": "同步向量/Trivium 副本",
|
||||
"authority.button.captureBaseline": "捕获 Perf Baseline",
|
||||
"authority.button.disasterRecovery": "灾难恢复:Checkpoint 覆盖 SQL",
|
||||
"authority.button.exportDiagnostics": "导出诊断包",
|
||||
"authority.button.refreshArtifacts": "刷新工件列表",
|
||||
"authority.button.runAudit": "执行 Authority 审计",
|
||||
"authority.button.runRepair": "执行副本同步",
|
||||
"authority.button.syncCheckpoint": "同步 Checkpoint",
|
||||
"authority.button.syncTrivium": "同步 Authority Trivium",
|
||||
"authority.confirm.checkpointRestore": "灾难恢复会用 Blob Checkpoint 覆盖 Authority SQL。\n\nSQL rev: {sqlRevision}\nCheckpoint rev: {checkpointRevision}\n\n只有 SQL 缺失、损坏或明确需要回滚时才继续。确定执行?",
|
||||
"authority.confirm.deleteArtifact": "确定删除该 diagnostics artifact?\n{path}",
|
||||
"authority.confirm.repairPlan": "副本同步计划将按以下顺序执行:\n{steps}\n\n其中包含从 Blob Checkpoint 恢复 SQL。此操作只适合 SQL 缺失、损坏或需要回滚时使用,确定继续?",
|
||||
"authority.diagnostics.artifactsRefreshFailed": "工件列表刷新失败:{error}",
|
||||
"authority.diagnostics.noArtifacts": "最近工件列表已刷新,但暂无可用诊断包记录",
|
||||
"authority.diagnostics.notYetRefreshed": "尚未刷新 diagnostics artifact 列表",
|
||||
"authority.diagnostics.recentArtifacts": "最近 diagnostics artifacts",
|
||||
"authority.repair.disasterRecovery": "灾难恢复:从 Blob Checkpoint 恢复 SQL",
|
||||
"authority.repair.disasterRecovery.detail": "仅在 SQL 缺失、损坏或用户明确需要回滚时,才可用 Blob checkpoint 回灌 Authority SQL。",
|
||||
"authority.repair.noStepsNeeded": "当前审计未发现需要自动编排的修复步骤",
|
||||
"authority.repair.none": "当前无需编排修复",
|
||||
"authority.repair.summary.detail": "建议同步:{steps}",
|
||||
"authority.repair.summaryLabel": "建议同步副本 {count} 步",
|
||||
"authority.repair.syncCheckpoint": "同步备份 Checkpoint",
|
||||
"authority.repair.syncCheckpoint.detail": "Authority Blob checkpoint 落后或缺失,应从当前权威图谱源同步一个新的备份 checkpoint。",
|
||||
"authority.repair.syncTrivium": "同步向量/Trivium 副本",
|
||||
"authority.repair.syncTrivium.detail": "Trivium 向量副本落后、collection 不匹配,或当前向量索引为 dirty,需要从权威图谱源重建/同步。",
|
||||
"authority.repair.resultHandoff": "已交接异步 Job",
|
||||
"authority.repair.resultSteps": "{count} 步",
|
||||
"authority.repair.status.error": "同步失败",
|
||||
"authority.repair.status.handoff": "等待 Job 交接",
|
||||
"authority.repair.status.idle": "未执行",
|
||||
"authority.repair.status.running": "同步中",
|
||||
"authority.repair.status.success": "同步完成",
|
||||
"authority.repair.status.warning": "部分同步失败",
|
||||
"authority.restore.error": "恢复失败",
|
||||
"authority.restore.idle": "未执行",
|
||||
"authority.restore.running": "恢复中",
|
||||
"authority.restore.success": "已恢复",
|
||||
"authority.summary.aligned": "Authority 工件已对齐",
|
||||
"authority.summary.alignedDetail": "Authority SQL / Trivium / Blob 已达到当前可观测的一致状态",
|
||||
"authority.summary.auditRunning": "Authority 审计中",
|
||||
"authority.summary.blockingInconsistency": "存在阻塞性不一致",
|
||||
"authority.summary.driftPending": "存在待处理漂移",
|
||||
"authority.summary.notYetAudited": "尚未运行审计",
|
||||
"authority.summary.replicasPendingSync": "副本待同步",
|
||||
"authority.summary.waitingForAudit": "等待审计",
|
||||
"authority.toast.artifactsRefreshed": "已刷新 diagnostics artifact 列表({count} 条)",
|
||||
"authority.toast.artifactsRefreshFailed": "diagnostics artifact 列表刷新失败:{error}",
|
||||
"authority.toast.auditCompleted": "Authority 审计完成",
|
||||
"authority.toast.auditFailed": "Authority 审计失败:{error}",
|
||||
"authority.toast.auditRunning": "Authority 一致性审计中…",
|
||||
"authority.toast.baselineCaptured": "Authority Perf Baseline 已捕获",
|
||||
"authority.toast.baselineCaptureFailed": "Authority Perf Baseline 捕获失败:{error}",
|
||||
"authority.toast.checkpointRestored": "Authority Checkpoint 已恢复:rev {revision}",
|
||||
"authority.toast.checkpointRestoreFailed": "Authority Checkpoint 恢复失败:{error}",
|
||||
"authority.toast.checkpointRestoring": "Authority Checkpoint 恢复中…",
|
||||
"authority.toast.checkpointWritten": "Authority Checkpoint 已写入:rev {revision}",
|
||||
"authority.toast.checkpointWriteFailed": "Authority Checkpoint 写入失败:{error}",
|
||||
"authority.toast.checkpointWriting": "Authority Checkpoint 写入中…",
|
||||
"authority.toast.copyPathFailed": "复制路径失败:{error}",
|
||||
"authority.toast.diagnosticDeleted": "诊断包已删除",
|
||||
"authority.toast.diagnosticDeleteFailed": "诊断包删除失败:{error}",
|
||||
"authority.toast.diagnosticDownloaded": "诊断包已下载",
|
||||
"authority.toast.diagnosticDownloadFailed": "下载诊断包失败:{error}",
|
||||
"authority.toast.diagnosticPathCopied": "诊断包路径已复制",
|
||||
"authority.toast.diagnosticReadFailed": "诊断包读取失败:{error}",
|
||||
"authority.toast.repairCompleted": "Authority 副本同步已完成({count} 步)",
|
||||
"authority.toast.repairFailed": "Authority 副本同步失败:{error}",
|
||||
"authority.toast.repairHandedOff": "Authority 副本同步已交接异步 Job({count} 步)",
|
||||
"authority.toast.repairPartialFailure": "Authority 副本部分同步失败;记忆图谱不受影响({count} 步)",
|
||||
"authority.toast.repairRunning": "Authority 副本同步执行中…",
|
||||
"authority.toast.triviumRebuildFailed": "Authority Trivium 重建失败:{error}",
|
||||
|
||||
"memory.type.character": "角色",
|
||||
"memory.type.event": "事件",
|
||||
"memory.type.location": "地点",
|
||||
@@ -304,4 +392,84 @@ export default {
|
||||
"storyTime.meta": "剧情时间: {time}",
|
||||
"storyTime.mixed": "混合",
|
||||
"storyTime.mixedTime": "混合时间",
|
||||
|
||||
"planner.llmPreset.global": "跟随全局 API",
|
||||
"planner.llmPreset.legacy": "旧 ENA 独立连接(兼容)",
|
||||
"planner.llmPreset.legacyWarning": "当前仍在使用旧版 ENA 独立连接;切换为全局或预设后将不再保留这套隐藏配置。",
|
||||
"planner.llmPreset.missingPresetFallback": "已回退为跟随全局:缺少预设 {name}",
|
||||
"planner.llmPreset.switchedToGlobal": "已改为跟随全局 BME API",
|
||||
"planner.llmPreset.keepingLegacy": "继续保留旧版 ENA 独立连接",
|
||||
"planner.llmPreset.presetNotFound": "选中的 API 预设不存在,已回退为跟随全局",
|
||||
"planner.llmPreset.switchedToPreset": "已切换为 API 预设:{name}",
|
||||
|
||||
"planner.promptBlock.namePlaceholder": "块名称",
|
||||
"planner.promptBlock.contentPlaceholder": "提示词内容…",
|
||||
"planner.promptBlock.moveUp": "上移",
|
||||
"planner.promptBlock.moveDown": "下移",
|
||||
"planner.promptBlock.deleteBlock": "删除块",
|
||||
"planner.promptBlock.newBlock": "新块",
|
||||
"planner.promptBlock.confirmReset": "确定恢复默认提示词块?当前提示词块将被覆盖。",
|
||||
|
||||
"planner.template.selectPlaceholder": "-- 选择模板 --",
|
||||
"planner.template.newTemplateName": "新模板名称",
|
||||
"planner.template.selectOrCreateFirst": "请先选择或新建模板",
|
||||
|
||||
"planner.log.noLogs": "暂无日志",
|
||||
"planner.log.success": "成功",
|
||||
"planner.log.failure": "失败",
|
||||
"planner.log.noMessages": "无消息",
|
||||
"planner.log.requestMessages": "请求消息 ({count} 条)",
|
||||
"planner.log.rawReply": "原始回复",
|
||||
"planner.log.filteredReply": "过滤后回复",
|
||||
"planner.log.confirmClear": "确定清空所有日志?",
|
||||
|
||||
"planner.status.enabled": "已启用",
|
||||
"planner.status.disabled": "未启用",
|
||||
"planner.status.ready": "就绪",
|
||||
"planner.status.saving": "保存中…",
|
||||
"planner.status.saved": "已保存",
|
||||
"planner.status.saveFailed": "保存失败",
|
||||
"planner.status.apiNotReady": "API 未就绪",
|
||||
"planner.status.testing": "测试中…",
|
||||
"planner.status.testComplete": "规划测试完成",
|
||||
"planner.status.testFailed": "规划测试失败",
|
||||
"planner.status.resetting": "重置中…",
|
||||
"planner.status.resetToDefault": "已恢复默认",
|
||||
"planner.status.resetFailed": "重置失败",
|
||||
"planner.status.moduleNotLoaded": "模块未加载",
|
||||
"planner.status.unavailable": "不可用",
|
||||
"planner.status.fetchModelsFailed": "拉取失败",
|
||||
"planner.status.noModelsFetched": "未获取到模型",
|
||||
"planner.status.modelsFetched": "获取到 {count} 个模型",
|
||||
"planner.status.fetchingModels": "拉取中…",
|
||||
|
||||
"planner.apiKey.hide": "隐藏",
|
||||
"planner.apiKey.show": "显示",
|
||||
|
||||
"planner.model.selectFromList": "-- 从列表选择 --",
|
||||
|
||||
"planner.taskPreset.workspaceNotFound": "未找到任务预设工作区,请手动切到「任务 -> 规划」",
|
||||
"planner.taskPreset.workspaceSwitched": "已切换到「任务 -> 规划」预设编辑器",
|
||||
|
||||
"planner.debug.diagnosing": "诊断中…",
|
||||
"planner.debug.failed": "诊断失败",
|
||||
|
||||
"authority.mode.standalone": "纯前端模式",
|
||||
"authority.mode.standalone.meta": "未检测到可用服务端增强,BME 将继续本地运行",
|
||||
"authority.mode.standalone.disabled.meta": "服务端增强已关闭,BME 将继续本地运行",
|
||||
"authority.mode.standalone.noAuthority.meta": "未检测到 DOA/Authority,已自动使用本地稳定路径",
|
||||
"authority.mode.probing": "探测中",
|
||||
"authority.mode.probing.meta": "正在检测服务端增强能力",
|
||||
"authority.mode.shadow": "服务端影子同步",
|
||||
"authority.mode.shadow.meta": "DOA/Authority 可用,但当前仍以本地路径为主",
|
||||
"authority.mode.candidate": "服务端增强准备中",
|
||||
"authority.mode.candidate.meta.storageReady": "图谱服务端存储可用,向量增强仍在等待能力确认",
|
||||
"authority.mode.candidate.meta.vectorReady": "向量服务端能力可用,图谱服务端存储仍在等待能力确认",
|
||||
"authority.mode.enhanced": "服务端增强已启用",
|
||||
"authority.mode.enhanced.meta.manifestReady": "图谱与向量存储已增强,服务端向量清单可用",
|
||||
"authority.mode.enhanced.meta.noManifest": "图谱与向量存储已增强,等待 BME 向量清单能力",
|
||||
"authority.mode.enhanced.meta.noJobs": "图谱与向量存储已增强,服务端后台任务能力暂不可用",
|
||||
"authority.mode.degraded": "已自动回退",
|
||||
"authority.mode.degraded.unhealthy.meta": "服务端增强暂不可用:{reason}",
|
||||
"authority.mode.degraded.capabilityNotReady.meta": "DOA/Authority 已连接,但关键能力未就绪:{reason}",
|
||||
};
|
||||
|
||||
@@ -27,11 +27,16 @@ function normalizeOptionalInteger(value) {
|
||||
return Math.max(0, Math.floor(parsed));
|
||||
}
|
||||
|
||||
function normalizeIssue(severity, code, message) {
|
||||
function normalizeIssue(severity, code, message, i18n = {}) {
|
||||
return {
|
||||
severity,
|
||||
code: String(code || "unknown"),
|
||||
message: String(message || ""),
|
||||
messageKey: String(i18n.messageKey || ""),
|
||||
messageParams:
|
||||
i18n.messageParams && typeof i18n.messageParams === "object" && !Array.isArray(i18n.messageParams)
|
||||
? clonePlain(i18n.messageParams, {})
|
||||
: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,7 +111,7 @@ export function buildAuthorityConsistencyRepairPlan(audit = null) {
|
||||
const sqlRevision = normalizeOptionalInteger(source?.sql?.revision);
|
||||
const blobRevision = normalizeOptionalInteger(source?.blob?.revision);
|
||||
const sqlNewerThanBlob = Number.isFinite(sqlRevision) && Number.isFinite(blobRevision) && sqlRevision > blobRevision;
|
||||
const addStep = (action, label, detail, codes = []) => {
|
||||
const addStep = (action, label, detail, codes = [], i18n = {}) => {
|
||||
const normalizedAction = normalizeRepairAction(action);
|
||||
if (!normalizedAction || !actions.includes(normalizedAction)) {
|
||||
return;
|
||||
@@ -124,6 +129,16 @@ export function buildAuthorityConsistencyRepairPlan(audit = null) {
|
||||
action: normalizedAction,
|
||||
label: String(label || normalizedAction),
|
||||
detail: String(detail || ""),
|
||||
labelKey: String(i18n.labelKey || ""),
|
||||
detailKey: String(i18n.detailKey || ""),
|
||||
labelParams:
|
||||
i18n.labelParams && typeof i18n.labelParams === "object" && !Array.isArray(i18n.labelParams)
|
||||
? clonePlain(i18n.labelParams, {})
|
||||
: {},
|
||||
detailParams:
|
||||
i18n.detailParams && typeof i18n.detailParams === "object" && !Array.isArray(i18n.detailParams)
|
||||
? clonePlain(i18n.detailParams, {})
|
||||
: {},
|
||||
issueCodes: Array.isArray(codes) ? codes.map((code) => String(code || "").trim()).filter(Boolean) : [],
|
||||
});
|
||||
};
|
||||
@@ -133,6 +148,10 @@ export function buildAuthorityConsistencyRepairPlan(audit = null) {
|
||||
"同步备份 Checkpoint",
|
||||
"Authority Blob checkpoint 落后或缺失,应从当前权威图谱源同步一个新的备份 checkpoint。",
|
||||
["blob-checkpoint-missing", "blob-checkpoint-behind", "blob-runtime-revision-drift"],
|
||||
{
|
||||
labelKey: "authority.repair.syncCheckpoint",
|
||||
detailKey: "authority.repair.syncCheckpoint.detail",
|
||||
},
|
||||
);
|
||||
if (!sqlNewerThanBlob) {
|
||||
addStep(
|
||||
@@ -140,6 +159,10 @@ export function buildAuthorityConsistencyRepairPlan(audit = null) {
|
||||
"灾难恢复:从 Blob Checkpoint 恢复 SQL",
|
||||
"仅在 SQL 缺失、损坏或用户明确需要回滚时,才可用 Blob checkpoint 回灌 Authority SQL。",
|
||||
["sql-runtime-revision-drift", "blob-newer-than-sql", "blob-chat-mismatch"],
|
||||
{
|
||||
labelKey: "authority.repair.disasterRecovery",
|
||||
detailKey: "authority.repair.disasterRecovery.detail",
|
||||
},
|
||||
);
|
||||
}
|
||||
addStep(
|
||||
@@ -147,6 +170,10 @@ export function buildAuthorityConsistencyRepairPlan(audit = null) {
|
||||
"同步向量/Trivium 副本",
|
||||
"Trivium 向量副本落后、collection 不匹配,或当前向量索引为 dirty,需要从权威图谱源重建/同步。",
|
||||
["trivium-sql-revision-drift", "trivium-replica-behind", "trivium-collection-mismatch", "vector-dirty"],
|
||||
{
|
||||
labelKey: "authority.repair.syncTrivium",
|
||||
detailKey: "authority.repair.syncTrivium.detail",
|
||||
},
|
||||
);
|
||||
|
||||
const blockedIssueCodes = (Array.isArray(source.issues) ? source.issues : [])
|
||||
@@ -159,6 +186,12 @@ export function buildAuthorityConsistencyRepairPlan(audit = null) {
|
||||
const detail = steps.length
|
||||
? `建议同步:${steps.map((step) => step.label).join(" → ")}`
|
||||
: String(source?.summary?.detail || "当前审计未发现需要自动编排的修复步骤");
|
||||
const detailKey = steps.length
|
||||
? "authority.repair.summary.detail"
|
||||
: String(source?.summary?.detailKey || "authority.repair.noStepsNeeded");
|
||||
const detailParams = steps.length
|
||||
? { steps: steps.map((step) => step.label).join(" → ") }
|
||||
: clonePlain(source?.summary?.detailParams, {});
|
||||
|
||||
return {
|
||||
ok: steps.length > 0,
|
||||
@@ -170,7 +203,11 @@ export function buildAuthorityConsistencyRepairPlan(audit = null) {
|
||||
summary: {
|
||||
level: steps.length > 0 ? "warning" : String(source?.summary?.level || "idle"),
|
||||
label: steps.length > 0 ? `建议同步副本 ${steps.length} 步` : "当前无需编排修复",
|
||||
labelKey: steps.length > 0 ? "authority.repair.summaryLabel" : "authority.repair.none",
|
||||
labelParams: { count: steps.length },
|
||||
detail,
|
||||
detailKey,
|
||||
detailParams,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -469,16 +506,28 @@ export function buildAuthorityConsistencyAudit(input = {}) {
|
||||
|
||||
const issues = [];
|
||||
if (sql.error) {
|
||||
issues.push(normalizeIssue("error", "sql-probe-error", `Authority SQL 探针失败:${sql.error}`));
|
||||
issues.push(normalizeIssue("error", "sql-probe-error", `Authority SQL 探针失败:${sql.error}`, {
|
||||
messageKey: "authority.audit.sqlProbeFailed",
|
||||
messageParams: { error: sql.error },
|
||||
}));
|
||||
}
|
||||
if (blob.error) {
|
||||
issues.push(normalizeIssue("warning", "blob-probe-error", `Authority Blob 读取失败:${blob.error}`));
|
||||
issues.push(normalizeIssue("warning", "blob-probe-error", `Authority Blob 读取失败:${blob.error}`, {
|
||||
messageKey: "authority.audit.blobReadFailed",
|
||||
messageParams: { error: blob.error },
|
||||
}));
|
||||
}
|
||||
if (trivium.error) {
|
||||
issues.push(normalizeIssue("warning", "trivium-probe-error", `Authority Trivium 探针失败:${trivium.error}`));
|
||||
issues.push(normalizeIssue("warning", "trivium-probe-error", `Authority Trivium 探针失败:${trivium.error}`, {
|
||||
messageKey: "authority.audit.triviumProbeFailed",
|
||||
messageParams: { error: trivium.error },
|
||||
}));
|
||||
}
|
||||
if (blob.exists && blob.chatId && chatId && blob.chatId !== chatId) {
|
||||
issues.push(normalizeIssue("error", "blob-chat-mismatch", `Checkpoint chatId 不匹配:${blob.chatId} ≠ ${chatId}`));
|
||||
issues.push(normalizeIssue("error", "blob-chat-mismatch", `Checkpoint chatId 不匹配:${blob.chatId} ≠ ${chatId}`, {
|
||||
messageKey: "authority.audit.chatIdMismatch",
|
||||
messageParams: { blobChatId: blob.chatId, chatId },
|
||||
}));
|
||||
}
|
||||
if (
|
||||
Number.isFinite(sql.revision) &&
|
||||
@@ -490,6 +539,10 @@ export function buildAuthorityConsistencyAudit(input = {}) {
|
||||
"warning",
|
||||
"sql-runtime-revision-drift",
|
||||
`SQL revision 与 runtime 不一致:${sql.revision} ≠ ${runtime.revision}`,
|
||||
{
|
||||
messageKey: "authority.audit.sqlRuntimeDrift",
|
||||
messageParams: { sqlRevision: sql.revision, runtimeRevision: runtime.revision },
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -508,6 +561,15 @@ export function buildAuthorityConsistencyAudit(input = {}) {
|
||||
code === "blob-checkpoint-behind"
|
||||
? `Blob checkpoint 落后于 Authority SQL:${blob.revision} < ${sql.revision}`
|
||||
: `Blob checkpoint revision 与 runtime 不一致:${blob.revision} ≠ ${runtime.revision}`,
|
||||
code === "blob-checkpoint-behind"
|
||||
? {
|
||||
messageKey: "authority.audit.blobBehindSql",
|
||||
messageParams: { blobRevision: blob.revision, sqlRevision: sql.revision },
|
||||
}
|
||||
: {
|
||||
messageKey: "authority.audit.blobRuntimeDrift",
|
||||
messageParams: { blobRevision: blob.revision, runtimeRevision: runtime.revision },
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -526,6 +588,15 @@ export function buildAuthorityConsistencyAudit(input = {}) {
|
||||
code === "trivium-replica-behind"
|
||||
? `Trivium 向量副本落后于 Authority SQL:${trivium.revision} < ${sql.revision}`
|
||||
: `Trivium revision 与 SQL 不一致:${trivium.revision} ≠ ${sql.revision}`,
|
||||
code === "trivium-replica-behind"
|
||||
? {
|
||||
messageKey: "authority.audit.triviumBehindSql",
|
||||
messageParams: { triviumRevision: trivium.revision, sqlRevision: sql.revision },
|
||||
}
|
||||
: {
|
||||
messageKey: "authority.audit.triviumSqlDrift",
|
||||
messageParams: { triviumRevision: trivium.revision, sqlRevision: sql.revision },
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -535,14 +606,22 @@ export function buildAuthorityConsistencyAudit(input = {}) {
|
||||
"warning",
|
||||
"trivium-collection-mismatch",
|
||||
`Trivium collection/namespace 与 runtime 不一致:${trivium.namespace} ≠ ${runtime.collectionId}`,
|
||||
{
|
||||
messageKey: "authority.audit.collectionMismatch",
|
||||
messageParams: { triviumNamespace: trivium.namespace, runtimeCollectionId: runtime.collectionId },
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
if (runtime.vectorDirty) {
|
||||
issues.push(normalizeIssue("warning", "vector-dirty", "当前向量索引仍处于 dirty 状态"));
|
||||
issues.push(normalizeIssue("warning", "vector-dirty", "当前向量索引仍处于 dirty 状态", {
|
||||
messageKey: "authority.audit.vectorDirty",
|
||||
}));
|
||||
}
|
||||
if (!blob.exists && source.capability?.blobReady) {
|
||||
issues.push(normalizeIssue("warning", "blob-checkpoint-missing", "Authority Blob 尚无可用 checkpoint"));
|
||||
issues.push(normalizeIssue("warning", "blob-checkpoint-missing", "Authority Blob 尚无可用 checkpoint", {
|
||||
messageKey: "authority.audit.blobCheckpointMissing",
|
||||
}));
|
||||
}
|
||||
|
||||
const actions = [];
|
||||
@@ -581,9 +660,23 @@ export function buildAuthorityConsistencyAudit(input = {}) {
|
||||
: level === "success"
|
||||
? "Authority 工件已对齐"
|
||||
: "等待审计";
|
||||
const labelKey =
|
||||
level === "error"
|
||||
? "authority.summary.blockingInconsistency"
|
||||
: level === "warning"
|
||||
? sql.ok
|
||||
? "authority.summary.replicasPendingSync"
|
||||
: "authority.summary.driftPending"
|
||||
: level === "success"
|
||||
? "authority.summary.aligned"
|
||||
: "authority.summary.waitingForAudit";
|
||||
const detail = issues[0]?.message || (level === "success"
|
||||
? "Authority SQL / Trivium / Blob 已达到当前可观测的一致状态"
|
||||
: "尚未运行审计");
|
||||
const detailKey = issues[0]?.messageKey || (level === "success"
|
||||
? "authority.summary.alignedDetail"
|
||||
: "authority.summary.notYetAudited");
|
||||
const detailParams = issues[0]?.messageParams || {};
|
||||
const backupLag = issues.some((issue) => [
|
||||
"blob-checkpoint-missing",
|
||||
"blob-checkpoint-behind",
|
||||
@@ -618,7 +711,11 @@ export function buildAuthorityConsistencyAudit(input = {}) {
|
||||
summary: {
|
||||
level,
|
||||
label,
|
||||
labelKey,
|
||||
labelParams: {},
|
||||
detail,
|
||||
detailKey,
|
||||
detailParams,
|
||||
issueCount: issues.length,
|
||||
dataSafety,
|
||||
backupRedundancy: backupLag ? "degraded" : (blob.exists ? "ok" : "unknown"),
|
||||
|
||||
@@ -18,6 +18,10 @@ export function createAuthorityUpgradeState(overrides = {}) {
|
||||
mode,
|
||||
text: normalizeString(overrides.text, "纯前端模式"),
|
||||
meta: normalizeString(overrides.meta, "未检测到可用服务端增强,BME 将继续本地运行"),
|
||||
textKey: normalizeString(overrides.textKey, "authority.mode.standalone"),
|
||||
metaKey: normalizeString(overrides.metaKey, "authority.mode.standalone.meta"),
|
||||
textParams: overrides.textParams ?? {},
|
||||
metaParams: overrides.metaParams ?? {},
|
||||
level: normalizeString(overrides.level, "idle"),
|
||||
ready: Boolean(overrides.ready),
|
||||
reason: normalizeString(overrides.reason, "standalone"),
|
||||
@@ -57,6 +61,8 @@ export function deriveAuthorityUpgradeState({
|
||||
mode: AUTHORITY_UPGRADE_MODES.STANDALONE,
|
||||
text: "纯前端模式",
|
||||
meta: "服务端增强已关闭,BME 将继续本地运行",
|
||||
textKey: "authority.mode.standalone",
|
||||
metaKey: "authority.mode.standalone.disabled.meta",
|
||||
level: "idle",
|
||||
reason: "authority-disabled",
|
||||
browserCacheMode,
|
||||
@@ -69,6 +75,8 @@ export function deriveAuthorityUpgradeState({
|
||||
mode: AUTHORITY_UPGRADE_MODES.STANDALONE,
|
||||
text: "纯前端模式",
|
||||
meta: "未检测到 DOA/Authority,已自动使用本地稳定路径",
|
||||
textKey: "authority.mode.standalone",
|
||||
metaKey: "authority.mode.standalone.noAuthority.meta",
|
||||
level: "idle",
|
||||
reason,
|
||||
browserCacheMode,
|
||||
@@ -81,6 +89,9 @@ export function deriveAuthorityUpgradeState({
|
||||
mode: AUTHORITY_UPGRADE_MODES.DEGRADED,
|
||||
text: "已自动回退",
|
||||
meta: `服务端增强暂不可用:${reason}`,
|
||||
textKey: "authority.mode.degraded",
|
||||
metaKey: "authority.mode.degraded.unhealthy.meta",
|
||||
metaParams: { reason },
|
||||
level: "warning",
|
||||
reason,
|
||||
browserCacheMode,
|
||||
@@ -93,6 +104,8 @@ export function deriveAuthorityUpgradeState({
|
||||
mode: AUTHORITY_UPGRADE_MODES.SHADOW,
|
||||
text: "服务端影子同步",
|
||||
meta: "DOA/Authority 可用,但当前仍以本地路径为主",
|
||||
textKey: "authority.mode.shadow",
|
||||
metaKey: "authority.mode.shadow.meta",
|
||||
level: "info",
|
||||
reason: "primary-disabled",
|
||||
serverPrimaryReady,
|
||||
@@ -108,6 +121,11 @@ export function deriveAuthorityUpgradeState({
|
||||
}
|
||||
|
||||
if (storageReady && vectorReady) {
|
||||
const enhancedMetaKey = jobsReady
|
||||
? bmeVectorManifestReady
|
||||
? "authority.mode.enhanced.meta.manifestReady"
|
||||
: "authority.mode.enhanced.meta.noManifest"
|
||||
: "authority.mode.enhanced.meta.noJobs";
|
||||
return createAuthorityUpgradeState({
|
||||
mode: AUTHORITY_UPGRADE_MODES.ENHANCED,
|
||||
text: "服务端增强已启用",
|
||||
@@ -116,6 +134,8 @@ export function deriveAuthorityUpgradeState({
|
||||
? "图谱与向量存储已增强,服务端向量清单可用"
|
||||
: "图谱与向量存储已增强,等待 BME 向量清单能力"
|
||||
: "图谱与向量存储已增强,服务端后台任务能力暂不可用",
|
||||
textKey: "authority.mode.enhanced",
|
||||
metaKey: enhancedMetaKey,
|
||||
level: "success",
|
||||
ready: true,
|
||||
reason: "authority-ready",
|
||||
@@ -132,12 +152,17 @@ export function deriveAuthorityUpgradeState({
|
||||
}
|
||||
|
||||
if (storageReady || vectorReady) {
|
||||
const candidateMetaKey = storageReady
|
||||
? "authority.mode.candidate.meta.storageReady"
|
||||
: "authority.mode.candidate.meta.vectorReady";
|
||||
return createAuthorityUpgradeState({
|
||||
mode: AUTHORITY_UPGRADE_MODES.CANDIDATE,
|
||||
text: "服务端增强准备中",
|
||||
meta: storageReady
|
||||
? "图谱服务端存储可用,向量增强仍在等待能力确认"
|
||||
: "向量服务端能力可用,图谱服务端存储仍在等待能力确认",
|
||||
textKey: "authority.mode.candidate",
|
||||
metaKey: candidateMetaKey,
|
||||
level: "info",
|
||||
reason: "partial-authority-ready",
|
||||
serverPrimaryReady,
|
||||
@@ -156,6 +181,9 @@ export function deriveAuthorityUpgradeState({
|
||||
mode: AUTHORITY_UPGRADE_MODES.DEGRADED,
|
||||
text: "已自动回退",
|
||||
meta: `DOA/Authority 已连接,但关键能力未就绪:${reason}`,
|
||||
textKey: "authority.mode.degraded",
|
||||
metaKey: "authority.mode.degraded.capabilityNotReady.meta",
|
||||
metaParams: { reason },
|
||||
level: "warning",
|
||||
reason,
|
||||
serverPrimaryReady,
|
||||
|
||||
@@ -204,11 +204,14 @@ const auditSqlAheadReplicasBehind = buildAuthorityConsistencyAudit({
|
||||
});
|
||||
assert.equal(auditSqlAheadReplicasBehind.summary.level, "warning");
|
||||
assert.equal(auditSqlAheadReplicasBehind.summary.label, "副本待同步");
|
||||
assert.equal(auditSqlAheadReplicasBehind.summary.labelKey, "authority.summary.replicasPendingSync");
|
||||
assert.equal(auditSqlAheadReplicasBehind.summary.dataSafety, "saved-replicas-behind");
|
||||
assert.equal(auditSqlAheadReplicasBehind.summary.backupRedundancy, "degraded");
|
||||
assert.equal(auditSqlAheadReplicasBehind.summary.searchQuality, "degraded");
|
||||
assert.ok(auditSqlAheadReplicasBehind.issues.some((issue) => issue.code === "blob-checkpoint-behind"));
|
||||
assert.ok(auditSqlAheadReplicasBehind.issues.some((issue) => issue.code === "trivium-replica-behind"));
|
||||
assert.ok(auditSqlAheadReplicasBehind.issues.some((issue) => issue.messageKey === "authority.audit.blobBehindSql"));
|
||||
assert.ok(auditSqlAheadReplicasBehind.issues.some((issue) => issue.messageKey === "authority.audit.triviumBehindSql"));
|
||||
assert.ok(auditSqlAheadReplicasBehind.actions.includes("write-authority-checkpoint"));
|
||||
assert.ok(auditSqlAheadReplicasBehind.actions.includes("rebuild-authority-trivium"));
|
||||
assert.equal(auditSqlAheadReplicasBehind.actions.includes("restore-from-authority-blob-checkpoint"), false);
|
||||
@@ -216,6 +219,7 @@ assert.equal(auditSqlAheadReplicasBehind.drift.checkpointRestorable, false);
|
||||
const sqlAheadRepairPlan = buildAuthorityConsistencyRepairPlan(auditSqlAheadReplicasBehind);
|
||||
assert.equal(sqlAheadRepairPlan.ok, true);
|
||||
assert.equal(sqlAheadRepairPlan.requiresConfirmation, false);
|
||||
assert.equal(sqlAheadRepairPlan.summary.labelKey, "authority.repair.summaryLabel");
|
||||
assert.deepEqual(
|
||||
sqlAheadRepairPlan.steps.map((step) => step.action),
|
||||
[
|
||||
@@ -223,6 +227,13 @@ assert.deepEqual(
|
||||
"rebuild-authority-trivium",
|
||||
],
|
||||
);
|
||||
assert.deepEqual(
|
||||
sqlAheadRepairPlan.steps.map((step) => step.labelKey),
|
||||
[
|
||||
"authority.repair.syncCheckpoint",
|
||||
"authority.repair.syncTrivium",
|
||||
],
|
||||
);
|
||||
|
||||
const restoreRepairPlan = buildAuthorityConsistencyRepairPlan({
|
||||
issues: [
|
||||
|
||||
@@ -6,6 +6,9 @@ import {
|
||||
formatAuthorityUpgradeMeta,
|
||||
} from "../runtime/authority-upgrade-state.js";
|
||||
import { createGraphPersistenceState } from "../ui/ui-status.js";
|
||||
import { t, formatUiStatusText, formatUiStatusMeta } from "../i18n/index.js";
|
||||
|
||||
// ── Original tests (unchanged expectations) ──
|
||||
|
||||
const initial = createAuthorityUpgradeState();
|
||||
assert.equal(initial.mode, "standalone");
|
||||
@@ -69,4 +72,144 @@ assert.equal(
|
||||
"准备就绪 · 服务端增强已启用",
|
||||
);
|
||||
|
||||
console.log("authority-upgrade-state tests passed");
|
||||
// ── Phase 7: textKey / metaKey presence and defaults ──
|
||||
|
||||
// createAuthorityUpgradeState defaults
|
||||
const defaults = createAuthorityUpgradeState();
|
||||
assert.equal(defaults.textKey, "authority.mode.standalone", "default textKey");
|
||||
assert.equal(defaults.metaKey, "authority.mode.standalone.meta", "default metaKey");
|
||||
assert.deepEqual(defaults.textParams, {}, "default textParams is empty object");
|
||||
assert.deepEqual(defaults.metaParams, {}, "default metaParams is empty object");
|
||||
|
||||
// deriveAuthorityUpgradeState: each branch has correct keys
|
||||
const disabledState = deriveAuthorityUpgradeState({
|
||||
settings: { authorityEnabled: "off" },
|
||||
capability: {},
|
||||
browserState: { mode: "minimal" },
|
||||
});
|
||||
assert.equal(disabledState.textKey, "authority.mode.standalone");
|
||||
assert.equal(disabledState.metaKey, "authority.mode.standalone.disabled.meta");
|
||||
|
||||
assert.equal(absent.textKey, "authority.mode.standalone");
|
||||
assert.equal(absent.metaKey, "authority.mode.standalone.noAuthority.meta");
|
||||
|
||||
assert.equal(degraded.textKey, "authority.mode.degraded");
|
||||
assert.equal(degraded.metaKey, "authority.mode.degraded.unhealthy.meta");
|
||||
assert.equal(degraded.metaParams.reason, "probe-failed");
|
||||
|
||||
assert.equal(enhanced.textKey, "authority.mode.enhanced");
|
||||
assert.equal(enhanced.metaKey, "authority.mode.enhanced.meta.manifestReady");
|
||||
|
||||
const shadow = deriveAuthorityUpgradeState({
|
||||
settings: { authorityEnabled: "auto", authorityPrimaryWhenAvailable: false },
|
||||
capability: {
|
||||
installed: true, healthy: true, sessionReady: true, permissionReady: true,
|
||||
},
|
||||
browserState: { mode: "minimal" },
|
||||
});
|
||||
assert.equal(shadow.textKey, "authority.mode.shadow");
|
||||
assert.equal(shadow.metaKey, "authority.mode.shadow.meta");
|
||||
|
||||
const candidateStorage = deriveAuthorityUpgradeState({
|
||||
settings: { authorityEnabled: "auto", authorityPrimaryWhenAvailable: true },
|
||||
capability: {
|
||||
installed: true, healthy: true, sessionReady: true, permissionReady: true,
|
||||
storagePrimaryReady: true, triviumPrimaryReady: false,
|
||||
},
|
||||
browserState: { mode: "minimal" },
|
||||
});
|
||||
assert.equal(candidateStorage.textKey, "authority.mode.candidate");
|
||||
assert.equal(candidateStorage.metaKey, "authority.mode.candidate.meta.storageReady");
|
||||
|
||||
const candidateVector = deriveAuthorityUpgradeState({
|
||||
settings: { authorityEnabled: "auto", authorityPrimaryWhenAvailable: true },
|
||||
capability: {
|
||||
installed: true, healthy: true, sessionReady: true, permissionReady: true,
|
||||
storagePrimaryReady: false, triviumPrimaryReady: true,
|
||||
},
|
||||
browserState: { mode: "minimal" },
|
||||
});
|
||||
assert.equal(candidateVector.textKey, "authority.mode.candidate");
|
||||
assert.equal(candidateVector.metaKey, "authority.mode.candidate.meta.vectorReady");
|
||||
|
||||
const degradedCapability = deriveAuthorityUpgradeState({
|
||||
settings: { authorityEnabled: "auto", authorityPrimaryWhenAvailable: true },
|
||||
capability: {
|
||||
installed: true, healthy: true, sessionReady: true, permissionReady: true,
|
||||
reason: "time-out",
|
||||
},
|
||||
browserState: { mode: "minimal" },
|
||||
});
|
||||
assert.equal(degradedCapability.textKey, "authority.mode.degraded");
|
||||
assert.equal(degradedCapability.metaKey, "authority.mode.degraded.capabilityNotReady.meta");
|
||||
assert.equal(degradedCapability.metaParams.reason, "time-out");
|
||||
|
||||
// Enhanced with no jobs
|
||||
const enhancedNoJobs = deriveAuthorityUpgradeState({
|
||||
settings: { authorityEnabled: "auto", authorityPrimaryWhenAvailable: true },
|
||||
capability: {
|
||||
installed: true, healthy: true, sessionReady: true, permissionReady: true,
|
||||
storagePrimaryReady: true, triviumPrimaryReady: true, jobsReady: false,
|
||||
},
|
||||
browserState: { mode: "minimal" },
|
||||
});
|
||||
assert.equal(enhancedNoJobs.textKey, "authority.mode.enhanced");
|
||||
assert.equal(enhancedNoJobs.metaKey, "authority.mode.enhanced.meta.noJobs");
|
||||
|
||||
// Enhanced with jobs but no manifest
|
||||
const enhancedNoManifest = deriveAuthorityUpgradeState({
|
||||
settings: { authorityEnabled: "auto", authorityPrimaryWhenAvailable: true },
|
||||
capability: {
|
||||
installed: true, healthy: true, sessionReady: true, permissionReady: true,
|
||||
storagePrimaryReady: true, triviumPrimaryReady: true,
|
||||
jobsReady: true, bmeVectorManifestReady: false,
|
||||
},
|
||||
browserState: { mode: "minimal" },
|
||||
});
|
||||
assert.equal(enhancedNoManifest.textKey, "authority.mode.enhanced");
|
||||
assert.equal(enhancedNoManifest.metaKey, "authority.mode.enhanced.meta.noManifest");
|
||||
|
||||
// ── Phase 7: formatUiStatusText / formatUiStatusMeta with i18n ──
|
||||
|
||||
// When textKey resolves to a catalog entry, t() should produce the localized string
|
||||
const standaloneText = formatUiStatusText(disabledState);
|
||||
assert.ok(typeof standaloneText === "string", "formatUiStatusText returns string");
|
||||
assert.ok(standaloneText.length > 0, "formatUiStatusText is non-empty");
|
||||
|
||||
const standaloneMeta = formatUiStatusMeta(disabledState);
|
||||
assert.ok(typeof standaloneMeta === "string", "formatUiStatusMeta returns string");
|
||||
assert.ok(standaloneMeta.length > 0, "formatUiStatusMeta is non-empty");
|
||||
|
||||
// Verify that zh-CN (default locale) catalog keys match the Chinese fallbacks
|
||||
assert.equal(t("authority.mode.standalone"), "纯前端模式");
|
||||
assert.equal(t("authority.mode.shadow"), "服务端影子同步");
|
||||
assert.equal(t("authority.mode.enhanced"), "服务端增强已启用");
|
||||
assert.equal(t("authority.mode.candidate"), "服务端增强准备中");
|
||||
assert.equal(t("authority.mode.degraded"), "已自动回退");
|
||||
|
||||
// Verify meta keys resolve
|
||||
assert.equal(
|
||||
t("authority.mode.enhanced.meta.manifestReady"),
|
||||
"图谱与向量存储已增强,服务端向量清单可用",
|
||||
);
|
||||
assert.equal(
|
||||
t("authority.mode.candidate.meta.storageReady"),
|
||||
"图谱服务端存储可用,向量增强仍在等待能力确认",
|
||||
);
|
||||
|
||||
// Verify degraded meta with params
|
||||
assert.equal(
|
||||
t("authority.mode.degraded.unhealthy.meta", { reason: "probe-failed" }),
|
||||
"服务端增强暂不可用:probe-failed",
|
||||
);
|
||||
|
||||
// Verify formatUiStatusText falls back to .text when no textKey
|
||||
const noKeyState = { text: "fallback", meta: "metaFallback" };
|
||||
assert.equal(formatUiStatusText(noKeyState), "fallback");
|
||||
assert.equal(formatUiStatusMeta(noKeyState), "metaFallback");
|
||||
|
||||
// Verify string passthrough
|
||||
assert.equal(formatUiStatusText("just a string"), "just a string");
|
||||
assert.equal(formatUiStatusMeta("just a meta string"), "just a meta string");
|
||||
|
||||
console.log("authority-upgrade-state tests passed");
|
||||
@@ -9,6 +9,7 @@
|
||||
* BME theming automatically.
|
||||
*/
|
||||
|
||||
import { t } from '../i18n/index.js';
|
||||
import {
|
||||
isSameLlmConfigSnapshot,
|
||||
resolveDedicatedLlmProviderConfig,
|
||||
@@ -248,7 +249,7 @@ function populatePlannerLlmPresetSelect(selectedPreset = resolvePlannerLlmSelect
|
||||
if (!select) return;
|
||||
|
||||
if (select.options.length > 0) {
|
||||
select.options[0].textContent = '-- 跟随全局(当前 BME API) --';
|
||||
select.options[0].textContent = t('planner.llmPreset.global');
|
||||
}
|
||||
|
||||
while (select.options.length > 1) {
|
||||
@@ -268,7 +269,7 @@ function populatePlannerLlmPresetSelect(selectedPreset = resolvePlannerLlmSelect
|
||||
if (selectedPreset === LEGACY_PLANNER_LLM_OPTION) {
|
||||
const legacyOption = document.createElement('option');
|
||||
legacyOption.value = LEGACY_PLANNER_LLM_OPTION;
|
||||
legacyOption.textContent = '旧 ENA 独立连接(兼容)';
|
||||
legacyOption.textContent = t('planner.llmPreset.legacy');
|
||||
select.appendChild(legacyOption);
|
||||
}
|
||||
|
||||
@@ -294,7 +295,7 @@ function createPromptBlockElement(block, idx, total) {
|
||||
const nameInput = document.createElement('input');
|
||||
nameInput.type = 'text';
|
||||
nameInput.className = 'bme-config-input';
|
||||
nameInput.placeholder = '块名称';
|
||||
nameInput.placeholder = t('planner.promptBlock.namePlaceholder');
|
||||
nameInput.value = block.name || '';
|
||||
nameInput.addEventListener('change', () => {
|
||||
block.name = nameInput.value;
|
||||
@@ -324,7 +325,7 @@ function createPromptBlockElement(block, idx, total) {
|
||||
upBtn.type = 'button';
|
||||
upBtn.className = 'bme-config-secondary-btn bme-planner-icon-btn';
|
||||
upBtn.innerHTML = '<i class="fa-solid fa-chevron-up"></i>';
|
||||
upBtn.title = '上移';
|
||||
upBtn.title = t('planner.promptBlock.moveUp');
|
||||
upBtn.disabled = idx === 0;
|
||||
upBtn.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
@@ -340,7 +341,7 @@ function createPromptBlockElement(block, idx, total) {
|
||||
downBtn.type = 'button';
|
||||
downBtn.className = 'bme-config-secondary-btn bme-planner-icon-btn';
|
||||
downBtn.innerHTML = '<i class="fa-solid fa-chevron-down"></i>';
|
||||
downBtn.title = '下移';
|
||||
downBtn.title = t('planner.promptBlock.moveDown');
|
||||
downBtn.disabled = idx === total - 1;
|
||||
downBtn.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
@@ -356,7 +357,7 @@ function createPromptBlockElement(block, idx, total) {
|
||||
delBtn.type = 'button';
|
||||
delBtn.className = 'bme-config-secondary-btn bme-config-danger-btn bme-planner-icon-btn';
|
||||
delBtn.innerHTML = '<i class="fa-solid fa-trash-can"></i>';
|
||||
delBtn.title = '删除块';
|
||||
delBtn.title = t('planner.promptBlock.deleteBlock');
|
||||
delBtn.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
@@ -370,7 +371,7 @@ function createPromptBlockElement(block, idx, total) {
|
||||
|
||||
const content = document.createElement('textarea');
|
||||
content.className = 'bme-config-input bme-planner-textarea';
|
||||
content.placeholder = '提示词内容...';
|
||||
content.placeholder = t('planner.promptBlock.contentPlaceholder');
|
||||
content.rows = 4;
|
||||
content.value = block.content || '';
|
||||
content.addEventListener('change', () => {
|
||||
@@ -402,7 +403,7 @@ function renderPromptList() {
|
||||
function renderTemplateSelect(selected = '') {
|
||||
const sel = $('bme-planner-tpl-select');
|
||||
if (!sel) return;
|
||||
sel.innerHTML = '<option value="">-- 选择模板 --</option>';
|
||||
sel.innerHTML = `<option value="">${t('planner.template.selectPlaceholder')}</option>`;
|
||||
const names = Object.keys(cfgCache?.promptTemplates || {});
|
||||
const selectedName = names.includes(selected) ? selected : '';
|
||||
for (const name of names) {
|
||||
@@ -445,14 +446,14 @@ function renderLogs() {
|
||||
if (!body) return;
|
||||
const list = Array.isArray(logsCache) ? logsCache : [];
|
||||
if (!list.length) {
|
||||
body.innerHTML = '<div class="bme-planner-log-empty">暂无日志</div>';
|
||||
body.innerHTML = `<div class="bme-planner-log-empty">${t('planner.log.noLogs')}</div>`;
|
||||
return;
|
||||
}
|
||||
body.innerHTML = list
|
||||
.map((item) => {
|
||||
const time = item.time ? new Date(item.time).toLocaleString() : '-';
|
||||
const cls = item.ok ? 'success' : 'error';
|
||||
const label = item.ok ? '成功' : '失败';
|
||||
const label = item.ok ? t('planner.log.success') : t('planner.log.failure');
|
||||
let msgHtml = '';
|
||||
if (Array.isArray(item.requestMessages) && item.requestMessages.length) {
|
||||
msgHtml = item.requestMessages
|
||||
@@ -472,7 +473,7 @@ function renderLogs() {
|
||||
})
|
||||
.join('');
|
||||
} else {
|
||||
msgHtml = '<div class="bme-planner-log-empty">无消息</div>';
|
||||
msgHtml = `<div class="bme-planner-log-empty">${t('planner.log.noMessages')}</div>`;
|
||||
}
|
||||
return `
|
||||
<div class="bme-planner-log-item">
|
||||
@@ -481,13 +482,13 @@ function renderLogs() {
|
||||
<span>${escapeHtml(item.model || '-')}</span>
|
||||
</div>
|
||||
${item.error ? `<div class="bme-planner-log-error">${escapeHtml(item.error)}</div>` : ''}
|
||||
<details><summary>请求消息 (${(item.requestMessages || []).length} 条)</summary>
|
||||
<details><summary>${t('planner.log.requestMessages', { count: (item.requestMessages || []).length })}</summary>
|
||||
<div class="bme-planner-msg-list">${msgHtml}</div>
|
||||
</details>
|
||||
<details><summary>原始回复</summary>
|
||||
<details><summary>${t('planner.log.rawReply')}</summary>
|
||||
<pre class="bme-planner-log-pre">${escapeHtml(item.rawReply || '')}</pre>
|
||||
</details>
|
||||
<details open><summary>过滤后回复</summary>
|
||||
<details open><summary>${t('planner.log.filteredReply')}</summary>
|
||||
<pre class="bme-planner-log-pre">${escapeHtml(item.filteredReply || '')}</pre>
|
||||
</details>
|
||||
</div>`;
|
||||
@@ -540,16 +541,16 @@ function applyConfigToFields(cfg) {
|
||||
|
||||
setStatusChip(
|
||||
'bme-planner-state-chip',
|
||||
toBool(cfgCache.enabled, false) ? '已启用' : '未启用',
|
||||
toBool(cfgCache.enabled, false) ? t('planner.status.enabled') : t('planner.status.disabled'),
|
||||
toBool(cfgCache.enabled, false) ? 'active' : 'idle',
|
||||
);
|
||||
updatePrefixModeUI();
|
||||
syncPlannerLlmPresetSelect();
|
||||
const llmSelectState = resolvePlannerLlmSelectState(cfgCache);
|
||||
if (llmSelectState.mode === 'legacy') {
|
||||
setLocalStatus('bme-planner-api-status', '当前仍在使用旧版 ENA 独立连接;切换为全局或预设后将不再保留这套隐藏配置。', '');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.llmPreset.legacyWarning'), '');
|
||||
} else if (llmSelectState.missingPresetName) {
|
||||
setLocalStatus('bme-planner-api-status', `已回退为跟随全局:缺少预设 ${llmSelectState.missingPresetName}`, 'error');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.llmPreset.missingPresetFallback', { name: llmSelectState.missingPresetName }), 'error');
|
||||
} else {
|
||||
setLocalStatus('bme-planner-api-status', '', '');
|
||||
}
|
||||
@@ -605,7 +606,7 @@ function updatePrefixModeUI() {
|
||||
|
||||
function resetPlannerSaveStatusIfReady() {
|
||||
if (autosaveInProgress) return;
|
||||
setStatusChip('bme-planner-save-chip', '就绪', 'idle');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.ready'), 'idle');
|
||||
}
|
||||
|
||||
/* ── Save flow ──────────────────────────────────────────────────────────── */
|
||||
@@ -629,24 +630,24 @@ async function doSave() {
|
||||
if (autosaveInProgress) return;
|
||||
const api = getPlannerApi();
|
||||
if (!api?.patchConfig) {
|
||||
setStatusChip('bme-planner-save-chip', 'API 未就绪', 'error');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.apiNotReady'), 'error');
|
||||
return;
|
||||
}
|
||||
autosaveInProgress = true;
|
||||
setStatusChip('bme-planner-save-chip', '保存中…', 'loading');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.saving'), 'loading');
|
||||
try {
|
||||
const patch = pendingSavePatch || collectPatch();
|
||||
const res = await api.patchConfig(patch);
|
||||
if (res?.ok) {
|
||||
pendingSavePatch = null;
|
||||
setStatusChip('bme-planner-save-chip', '已保存', 'success');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.saved'), 'success');
|
||||
setTimeout(() => {
|
||||
if ($('bme-planner-save-chip')?.dataset?.tone === 'success') {
|
||||
setStatusChip('bme-planner-save-chip', '就绪', 'idle');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.ready'), 'idle');
|
||||
}
|
||||
}, 2000);
|
||||
} else {
|
||||
setStatusChip('bme-planner-save-chip', res?.error || '保存失败', 'error');
|
||||
setStatusChip('bme-planner-save-chip', res?.error || t('planner.status.saveFailed'), 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
setStatusChip('bme-planner-save-chip', String(err?.message ?? err), 'error');
|
||||
@@ -674,7 +675,7 @@ function bindOnce(section) {
|
||||
$('bme-planner-enabled')?.addEventListener('change', () => {
|
||||
setStatusChip(
|
||||
'bme-planner-state-chip',
|
||||
toBool($('bme-planner-enabled').value, false) ? '已启用' : '未启用',
|
||||
toBool($('bme-planner-enabled').value, false) ? t('planner.status.enabled') : t('planner.status.disabled'),
|
||||
toBool($('bme-planner-enabled').value, false) ? 'active' : 'idle',
|
||||
);
|
||||
flushSave();
|
||||
@@ -687,10 +688,10 @@ function bindOnce(section) {
|
||||
$('bme-planner-run-test')?.addEventListener('click', async () => {
|
||||
const textEl = $('bme-planner-test-input');
|
||||
const text = (textEl?.value || '').trim();
|
||||
setLocalStatus('bme-planner-test-status', '测试中…', 'loading');
|
||||
setLocalStatus('bme-planner-test-status', t('planner.status.testing'), 'loading');
|
||||
const res = await api?.runTest?.(text);
|
||||
if (res?.ok) setLocalStatus('bme-planner-test-status', '规划测试完成', 'success');
|
||||
else setLocalStatus('bme-planner-test-status', res?.error || '规划测试失败', 'error');
|
||||
if (res?.ok) setLocalStatus('bme-planner-test-status', t('planner.status.testComplete'), 'success');
|
||||
else setLocalStatus('bme-planner-test-status', res?.error || t('planner.status.testFailed'), 'error');
|
||||
});
|
||||
|
||||
/* API connection */
|
||||
@@ -700,10 +701,10 @@ function bindOnce(section) {
|
||||
if (!input || !btn) return;
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
btn.querySelector('span').textContent = '隐藏';
|
||||
btn.querySelector('span').textContent = t('planner.apiKey.hide');
|
||||
} else {
|
||||
input.type = 'password';
|
||||
btn.querySelector('span').textContent = '显示';
|
||||
btn.querySelector('span').textContent = t('planner.apiKey.show');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -713,16 +714,16 @@ function bindOnce(section) {
|
||||
setLocalStatus('bme-planner-api-status', statusText, 'loading');
|
||||
const res = await api?.fetchModels?.();
|
||||
if (!res) {
|
||||
setLocalStatus('bme-planner-api-status', 'API 未就绪', 'error');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.status.apiNotReady'), 'error');
|
||||
return;
|
||||
}
|
||||
if (!res.ok) {
|
||||
setLocalStatus('bme-planner-api-status', res.error || '拉取失败', 'error');
|
||||
setLocalStatus('bme-planner-api-status', res.error || t('planner.status.fetchModelsFailed'), 'error');
|
||||
return;
|
||||
}
|
||||
const models = Array.isArray(res.models) ? res.models : [];
|
||||
if (!models.length) {
|
||||
setLocalStatus('bme-planner-api-status', '未获取到模型', 'error');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.status.noModelsFetched'), 'error');
|
||||
const sel = $('bme-planner-model-select');
|
||||
if (sel) sel.style.display = 'none';
|
||||
return;
|
||||
@@ -730,7 +731,7 @@ function bindOnce(section) {
|
||||
fetchedModels = models;
|
||||
const sel = $('bme-planner-model-select');
|
||||
if (sel) {
|
||||
sel.innerHTML = '<option value="">-- 从列表选择 --</option>';
|
||||
sel.innerHTML = `<option value="">${t('planner.model.selectFromList')}</option>`;
|
||||
const cur = ($('bme-planner-model')?.value || '').trim();
|
||||
for (const m of models) {
|
||||
const opt = document.createElement('option');
|
||||
@@ -741,11 +742,11 @@ function bindOnce(section) {
|
||||
}
|
||||
sel.style.display = '';
|
||||
}
|
||||
setLocalStatus('bme-planner-api-status', `获取到 ${models.length} 个模型`, 'success');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.status.modelsFetched', { count: models.length }), 'success');
|
||||
};
|
||||
|
||||
$('bme-planner-fetch-models')?.addEventListener('click', () => handleFetchModels('拉取中…'));
|
||||
$('bme-planner-test-conn')?.addEventListener('click', () => handleFetchModels('测试中…'));
|
||||
$('bme-planner-fetch-models')?.addEventListener('click', () => handleFetchModels(t('planner.status.fetchingModels')));
|
||||
$('bme-planner-test-conn')?.addEventListener('click', () => handleFetchModels(t('planner.status.testing')));
|
||||
|
||||
$('bme-planner-model-select')?.addEventListener('change', () => {
|
||||
const sel = $('bme-planner-model-select');
|
||||
@@ -771,13 +772,13 @@ function bindOnce(section) {
|
||||
cfgCache.api.apiKey = '';
|
||||
cfgCache.api.model = '';
|
||||
syncPlannerLlmPresetSelect();
|
||||
setLocalStatus('bme-planner-api-status', '已改为跟随全局 BME API', 'success');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.llmPreset.switchedToGlobal'), 'success');
|
||||
scheduleSave();
|
||||
return;
|
||||
}
|
||||
if (selectedName === LEGACY_PLANNER_LLM_OPTION) {
|
||||
syncPlannerLlmPresetSelect();
|
||||
setLocalStatus('bme-planner-api-status', '继续保留旧版 ENA 独立连接', '');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.llmPreset.keepingLegacy'), '');
|
||||
scheduleSave();
|
||||
return;
|
||||
}
|
||||
@@ -791,7 +792,7 @@ function bindOnce(section) {
|
||||
cfgCache.api.apiKey = '';
|
||||
cfgCache.api.model = '';
|
||||
syncPlannerLlmPresetSelect();
|
||||
setLocalStatus('bme-planner-api-status', '选中的 API 预设不存在,已回退为跟随全局', 'error');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.llmPreset.presetNotFound'), 'error');
|
||||
scheduleSave();
|
||||
return;
|
||||
}
|
||||
@@ -803,17 +804,17 @@ function bindOnce(section) {
|
||||
cfgCache.api.apiKey = '';
|
||||
cfgCache.api.model = '';
|
||||
syncPlannerLlmPresetSelect();
|
||||
setLocalStatus('bme-planner-api-status', `已切换为 API 预设:${selectedName}`, 'success');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.llmPreset.switchedToPreset', { name: selectedName }), 'success');
|
||||
scheduleSave();
|
||||
});
|
||||
|
||||
$('bme-planner-open-task-presets')?.addEventListener('click', () => {
|
||||
const opened = openPlannerTaskPresetWorkspace();
|
||||
if (!opened) {
|
||||
setLocalStatus('bme-planner-api-status', '未找到任务预设工作区,请手动切到“任务 -> 规划”', 'error');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.taskPreset.workspaceNotFound'), 'error');
|
||||
return;
|
||||
}
|
||||
setLocalStatus('bme-planner-api-status', '已切换到“任务 -> 规划”预设编辑器', 'success');
|
||||
setLocalStatus('bme-planner-api-status', t('planner.taskPreset.workspaceSwitched'), 'success');
|
||||
});
|
||||
|
||||
/* Prompts + templates */
|
||||
@@ -822,20 +823,20 @@ function bindOnce(section) {
|
||||
$('bme-planner-add-prompt')?.addEventListener('click', () => {
|
||||
cfgCache = cfgCache || {};
|
||||
cfgCache.promptBlocks = cfgCache.promptBlocks || [];
|
||||
cfgCache.promptBlocks.push({ id: genId(), role: 'system', name: '新块', content: '' });
|
||||
cfgCache.promptBlocks.push({ id: genId(), role: 'system', name: t('planner.promptBlock.newBlock'), content: '' });
|
||||
renderPromptList();
|
||||
scheduleSave();
|
||||
});
|
||||
|
||||
$('bme-planner-reset-prompt')?.addEventListener('click', async () => {
|
||||
if (!confirm('确定恢复默认提示词块?当前提示词块将被覆盖。')) return;
|
||||
setStatusChip('bme-planner-save-chip', '重置中…', 'loading');
|
||||
if (!confirm(t('planner.promptBlock.confirmReset'))) return;
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.resetting'), 'loading');
|
||||
const res = await api?.resetPromptToDefault?.();
|
||||
if (res?.ok && res.config) {
|
||||
applyConfigToFields(res.config);
|
||||
setStatusChip('bme-planner-save-chip', '已恢复默认', 'success');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.resetToDefault'), 'success');
|
||||
} else {
|
||||
setStatusChip('bme-planner-save-chip', res?.error || '重置失败', 'error');
|
||||
setStatusChip('bme-planner-save-chip', res?.error || t('planner.status.resetFailed'), 'error');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -854,7 +855,7 @@ function bindOnce(section) {
|
||||
$('bme-planner-tpl-save')?.addEventListener('click', () => {
|
||||
const name = $('bme-planner-tpl-select').value;
|
||||
if (!name) {
|
||||
setStatusChip('bme-planner-save-chip', '请先选择或新建模板', 'error');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.template.selectOrCreateFirst'), 'error');
|
||||
return;
|
||||
}
|
||||
cfgCache.promptTemplates = cfgCache.promptTemplates || {};
|
||||
@@ -865,7 +866,7 @@ function bindOnce(section) {
|
||||
});
|
||||
|
||||
$('bme-planner-tpl-saveas')?.addEventListener('click', () => {
|
||||
const name = prompt('新模板名称');
|
||||
const name = prompt(t('planner.template.newTemplateName'));
|
||||
if (!name) return;
|
||||
cfgCache.promptTemplates = cfgCache.promptTemplates || {};
|
||||
cfgCache.promptTemplates[name] = structuredClone(cfgCache.promptBlocks || []);
|
||||
@@ -901,20 +902,20 @@ function bindOnce(section) {
|
||||
const out = $('bme-planner-debug-output');
|
||||
if (out) {
|
||||
setHidden(out, false);
|
||||
out.textContent = '诊断中…';
|
||||
out.textContent = t('planner.debug.diagnosing');
|
||||
}
|
||||
const res = await api?.debugWorldbook?.();
|
||||
if (out) out.textContent = res?.output ?? '诊断失败';
|
||||
if (out) out.textContent = res?.output ?? t('planner.debug.failed');
|
||||
});
|
||||
|
||||
$('bme-planner-debug-char')?.addEventListener('click', async () => {
|
||||
const out = $('bme-planner-debug-output');
|
||||
if (out) {
|
||||
setHidden(out, false);
|
||||
out.textContent = '诊断中…';
|
||||
out.textContent = t('planner.debug.diagnosing');
|
||||
}
|
||||
const res = await api?.debugChar?.();
|
||||
if (out) out.textContent = res?.output ?? '诊断失败';
|
||||
if (out) out.textContent = res?.output ?? t('planner.debug.failed');
|
||||
});
|
||||
|
||||
/* Logs */
|
||||
@@ -925,7 +926,7 @@ function bindOnce(section) {
|
||||
});
|
||||
|
||||
$('bme-planner-logs-clear')?.addEventListener('click', async () => {
|
||||
if (!confirm('确定清空所有日志?')) return;
|
||||
if (!confirm(t('planner.log.confirmClear'))) return;
|
||||
const res = await api?.clearLogs?.();
|
||||
if (res?.ok !== false) {
|
||||
logsCache = [];
|
||||
@@ -975,8 +976,8 @@ export function initPlannerSections(rootEl, options = {}) {
|
||||
|
||||
const api = getPlannerApi();
|
||||
if (!api) {
|
||||
setStatusChip('bme-planner-state-chip', '模块未加载', 'error');
|
||||
setStatusChip('bme-planner-save-chip', '不可用', 'error');
|
||||
setStatusChip('bme-planner-state-chip', t('planner.status.moduleNotLoaded'), 'error');
|
||||
setStatusChip('bme-planner-save-chip', t('planner.status.unavailable'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1007,7 +1008,7 @@ export function refreshPlannerSections(options = {}) {
|
||||
}
|
||||
const api = getPlannerApi();
|
||||
if (!api) {
|
||||
setStatusChip('bme-planner-state-chip', '模块未加载', 'error');
|
||||
setStatusChip('bme-planner-state-chip', t('planner.status.moduleNotLoaded'), 'error');
|
||||
return;
|
||||
}
|
||||
if (typeof api.getConfig === 'function') applyConfigToFields(api.getConfig());
|
||||
|
||||
197
ui/panel.js
197
ui/panel.js
@@ -64,6 +64,7 @@ import {
|
||||
normalizeMaintenanceExecutionMode,
|
||||
} from "../runtime/concurrency.js";
|
||||
import {
|
||||
formatI18nValue,
|
||||
formatUiStatusMeta,
|
||||
formatUiStatusText,
|
||||
hydrateI18n,
|
||||
@@ -3329,7 +3330,30 @@ function _refreshTaskPersistence() {
|
||||
? "Authority 审计中"
|
||||
: "等待审计",
|
||||
detail: String(ps.authorityConsistencyError || "尚未运行一致性审计"),
|
||||
labelKey:
|
||||
ps.authorityConsistencyState === "success"
|
||||
? "authority.summary.aligned"
|
||||
: ps.authorityConsistencyState === "warning"
|
||||
? "authority.summary.driftPending"
|
||||
: ps.authorityConsistencyState === "error"
|
||||
? "error.auditFailed"
|
||||
: ps.authorityConsistencyState === "running"
|
||||
? "authority.summary.auditRunning"
|
||||
: "authority.summary.waitingForAudit",
|
||||
labelParams: {},
|
||||
detailKey: ps.authorityConsistencyError ? "" : "authority.summary.notYetAudited",
|
||||
detailParams: {},
|
||||
};
|
||||
const authorityAuditSummaryLabel = formatI18nValue(authorityAuditSummary, {
|
||||
keyField: "labelKey",
|
||||
paramsField: "labelParams",
|
||||
fallbackField: "label",
|
||||
});
|
||||
const authorityAuditSummaryDetail = formatI18nValue(authorityAuditSummary, {
|
||||
keyField: "detailKey",
|
||||
paramsField: "detailParams",
|
||||
fallbackField: "detail",
|
||||
});
|
||||
const authorityAuditSqlRevision = Number.isFinite(Number(authorityAudit?.sql?.revision))
|
||||
? String(Number(authorityAudit.sql.revision))
|
||||
: "—";
|
||||
@@ -3345,14 +3369,21 @@ function _refreshTaskPersistence() {
|
||||
authorityAudit?.blob?.path || ps.authorityBlobCheckpointPath || "",
|
||||
).trim() || "—";
|
||||
const authorityAuditIssuesLabel = Array.isArray(authorityAudit?.issues) && authorityAudit.issues.length
|
||||
? authorityAudit.issues.map((issue) => issue.message).filter(Boolean).join(" / ")
|
||||
: authorityAuditSummary.detail || "—";
|
||||
? authorityAudit.issues
|
||||
.map((issue) => formatI18nValue(issue, {
|
||||
keyField: "messageKey",
|
||||
paramsField: "messageParams",
|
||||
fallbackField: "message",
|
||||
}))
|
||||
.filter(Boolean)
|
||||
.join(" / ")
|
||||
: authorityAuditSummaryDetail || "—";
|
||||
const authorityAuditActionsLabel = Array.isArray(authorityAudit?.actions) && authorityAudit.actions.length
|
||||
? authorityAudit.actions.map((action) => ({
|
||||
"write-authority-checkpoint": "同步备份 Checkpoint",
|
||||
"rebuild-authority-trivium": "同步向量/Trivium 副本",
|
||||
"run-authority-consistency-audit": "重新审计",
|
||||
"restore-from-authority-blob-checkpoint": "灾难恢复:从 Checkpoint 覆盖 SQL",
|
||||
"write-authority-checkpoint": t("authority.action.syncCheckpoint"),
|
||||
"rebuild-authority-trivium": t("authority.action.syncTrivium"),
|
||||
"run-authority-consistency-audit": t("authority.action.reaudit"),
|
||||
"restore-from-authority-blob-checkpoint": t("authority.action.disasterRecovery"),
|
||||
}[action] || action)).join(" · ")
|
||||
: "—";
|
||||
const authorityAuditUpdatedLabel = ps.authorityConsistencyUpdatedAt
|
||||
@@ -3367,12 +3398,12 @@ function _refreshTaskPersistence() {
|
||||
const authorityRestoreState = String(ps.authorityCheckpointRestoreState || "idle").trim();
|
||||
const authorityRestoreLabel =
|
||||
authorityRestoreState === "success"
|
||||
? "已恢复"
|
||||
? t("authority.restore.success")
|
||||
: authorityRestoreState === "error"
|
||||
? "恢复失败"
|
||||
? t("authority.restore.error")
|
||||
: authorityRestoreState === "running"
|
||||
? "恢复中"
|
||||
: "未执行";
|
||||
? t("authority.restore.running")
|
||||
: t("authority.restore.idle");
|
||||
const authorityRestoreUpdatedLabel = ps.authorityCheckpointRestoreUpdatedAt
|
||||
? _formatTaskProfileTime(ps.authorityCheckpointRestoreUpdatedAt)
|
||||
: "—";
|
||||
@@ -3389,28 +3420,37 @@ function _refreshTaskPersistence() {
|
||||
).trim();
|
||||
const authorityRepairLabel =
|
||||
authorityRepairState === "success"
|
||||
? "同步完成"
|
||||
? t("authority.repair.status.success")
|
||||
: authorityRepairState === "error"
|
||||
? "同步失败"
|
||||
? t("authority.repair.status.error")
|
||||
: authorityRepairState === "warning"
|
||||
? "部分同步失败"
|
||||
? t("authority.repair.status.warning")
|
||||
: authorityRepairState === "running"
|
||||
? authorityRepairResult?.handoffRequired
|
||||
? "等待 Job 交接"
|
||||
: "同步中"
|
||||
: "未执行";
|
||||
? t("authority.repair.status.handoff")
|
||||
: t("authority.repair.status.running")
|
||||
: t("authority.repair.status.idle");
|
||||
const authorityRepairUpdatedLabel = ps.authorityRepairUpdatedAt
|
||||
? _formatTaskProfileTime(ps.authorityRepairUpdatedAt)
|
||||
: "—";
|
||||
const authorityRepairPlanLabel = authorityRepairPlan.ok
|
||||
? authorityRepairPlan.steps.map((step) => step.label).join(" → ")
|
||||
: authorityRepairPlan.summary.label || "当前无需编排同步";
|
||||
? authorityRepairPlan.steps.map((step) => formatI18nValue(step, {
|
||||
keyField: "labelKey",
|
||||
paramsField: "labelParams",
|
||||
fallbackField: "label",
|
||||
})).join(" → ")
|
||||
: formatI18nValue(authorityRepairPlan.summary, {
|
||||
keyField: "labelKey",
|
||||
paramsField: "labelParams",
|
||||
fallbackField: "label",
|
||||
fallback: t("authority.repair.none"),
|
||||
});
|
||||
const authorityRepairResultLabel = authorityRepairResult?.steps?.length
|
||||
? `${Number(authorityRepairResult.steps.length || 0)} 步${
|
||||
? `${t("authority.repair.resultSteps", { count: Number(authorityRepairResult.steps.length || 0) })}${
|
||||
authorityRepairResult?.handoffRequired
|
||||
? authorityRepairHandoffJobId
|
||||
? ` · job ${authorityRepairHandoffJobId}`
|
||||
: " · 已交接异步 Job"
|
||||
: ` · ${t("authority.repair.resultHandoff")}`
|
||||
: ""
|
||||
}`
|
||||
: "—";
|
||||
@@ -3479,6 +3519,11 @@ function _refreshTaskPersistence() {
|
||||
const authorityArtifactPruneLabel = ps.authorityDiagnosticsLastPrunedAt
|
||||
? `${Number(ps.authorityDiagnosticsLastPrunedCount || 0)} 条 · ${_formatTaskProfileTime(ps.authorityDiagnosticsLastPrunedAt)}`
|
||||
: "未触发";
|
||||
const authorityRepairPlanSummaryDetail = formatI18nValue(authorityRepairPlan.summary, {
|
||||
keyField: "detailKey",
|
||||
paramsField: "detailParams",
|
||||
fallbackField: "detail",
|
||||
});
|
||||
const activeRegionLabel = String(
|
||||
historyState?.activeRegion ||
|
||||
historyState?.lastExtractedRegion ||
|
||||
@@ -3576,7 +3621,7 @@ function _refreshTaskPersistence() {
|
||||
..._buildPersistDeltaDiagnosticRows(persistDeltaDiagnostics),
|
||||
);
|
||||
const authorityRows = [
|
||||
["审计状态", authorityAuditSummary.label],
|
||||
["审计状态", authorityAuditSummaryLabel],
|
||||
["SQL rev", authorityAuditSqlRevision],
|
||||
["Trivium rev", authorityAuditTriviumRevision],
|
||||
["Blob rev", authorityAuditBlobRevision],
|
||||
@@ -3634,28 +3679,28 @@ function _refreshTaskPersistence() {
|
||||
);
|
||||
const authorityActionButtons = [
|
||||
typeof _actionHandlers.runAuthorityConsistencyAudit === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="audit">执行 Authority 审计</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="audit">${_escHtml(t("authority.button.runAudit"))}</button>`
|
||||
: "",
|
||||
showAuthorityRepairAction && typeof _actionHandlers.runAuthorityConsistencyRepairPlan === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="repair-plan">执行副本同步</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="repair-plan">${_escHtml(t("authority.button.runRepair"))}</button>`
|
||||
: "",
|
||||
showAuthorityCheckpointWriteAction && typeof _actionHandlers.writeAuthorityCheckpoint === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="checkpoint">同步 Checkpoint</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="checkpoint">${_escHtml(t("authority.button.syncCheckpoint"))}</button>`
|
||||
: "",
|
||||
showAuthorityRestoreAction && typeof _actionHandlers.restoreAuthorityCheckpoint === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="restore">灾难恢复:Checkpoint 覆盖 SQL</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="restore">${_escHtml(t("authority.button.disasterRecovery"))}</button>`
|
||||
: "",
|
||||
showAuthorityTriviumRebuildAction && typeof _actionHandlers.rebuildVectorIndex === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="rebuild-trivium">同步 Authority Trivium</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="rebuild-trivium">${_escHtml(t("authority.button.syncTrivium"))}</button>`
|
||||
: "",
|
||||
typeof _actionHandlers.captureAuthorityPerformanceBaseline === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="baseline">捕获 Perf Baseline</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="baseline">${_escHtml(t("authority.button.captureBaseline"))}</button>`
|
||||
: "",
|
||||
typeof _actionHandlers.exportDiagnosticsBundle === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="bundle">导出诊断包</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="bundle">${_escHtml(t("authority.button.exportDiagnostics"))}</button>`
|
||||
: "",
|
||||
typeof _actionHandlers.refreshAuthorityDiagnosticsArtifacts === "function"
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="artifacts-refresh">刷新工件列表</button>`
|
||||
? `<button class="bme-config-secondary-btn" type="button" data-authority-persistence-action="artifacts-refresh">${_escHtml(t("authority.button.refreshArtifacts"))}</button>`
|
||||
: "",
|
||||
].filter(Boolean).join("");
|
||||
const authorityArtifactsHtml = authorityArtifactEntries.length
|
||||
@@ -3686,10 +3731,10 @@ function _refreshTaskPersistence() {
|
||||
</div>`
|
||||
: `<div class="bme-config-help" style="margin-top:12px">${_escHtml(
|
||||
ps.authorityDiagnosticsArtifactsError
|
||||
? `工件列表刷新失败:${ps.authorityDiagnosticsArtifactsError}`
|
||||
? t("authority.diagnostics.artifactsRefreshFailed", { error: ps.authorityDiagnosticsArtifactsError })
|
||||
: ps.authorityDiagnosticsArtifactsUpdatedAt
|
||||
? "最近工件列表已刷新,但暂无可用诊断包记录"
|
||||
: "尚未刷新 diagnostics artifact 列表"
|
||||
? t("authority.diagnostics.noArtifacts")
|
||||
: t("authority.diagnostics.notYetRefreshed")
|
||||
)}</div>`;
|
||||
|
||||
el.innerHTML = `
|
||||
@@ -3727,10 +3772,10 @@ function _refreshTaskPersistence() {
|
||||
</div>
|
||||
${authorityActionButtons ? `<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px">${authorityActionButtons}</div>` : ""}
|
||||
${renderRowsTwoColumn(authorityRows)}
|
||||
<div class="bme-config-help" style="margin-top:10px">${_escHtml(authorityAuditSummary.detail || "—")}</div>
|
||||
<div class="bme-config-help" style="margin-top:10px">${_escHtml(authorityAuditSummaryDetail || "—")}</div>
|
||||
<div class="bme-config-help" style="margin-top:6px">${_escHtml(authorityAuditIssuesLabel)}</div>
|
||||
<div class="bme-config-help" style="margin-top:6px">${_escHtml(authorityRepairPlan.summary.detail || "—")}</div>
|
||||
<div class="bme-config-help" style="margin-top:10px">最近 diagnostics artifacts</div>
|
||||
<div class="bme-config-help" style="margin-top:6px">${_escHtml(authorityRepairPlanSummaryDetail || "—")}</div>
|
||||
<div class="bme-config-help" style="margin-top:10px">${_escHtml(t("authority.diagnostics.recentArtifacts"))}</div>
|
||||
${authorityArtifactsHtml}
|
||||
${ps.authorityRepairError ? `<div class="bme-config-help" style="margin-top:6px;color:#e74c3c">${_escHtml(ps.authorityRepairError)}</div>` : ""}
|
||||
${ps.authorityCheckpointRestoreError ? `<div class="bme-config-help" style="margin-top:6px;color:#e74c3c">${_escHtml(ps.authorityCheckpointRestoreError)}</div>` : ""}
|
||||
@@ -3747,56 +3792,66 @@ function _refreshTaskPersistence() {
|
||||
try {
|
||||
if (action === "audit") {
|
||||
if (typeof _actionHandlers.runAuthorityConsistencyAudit !== "function") return;
|
||||
toastr.info("Authority 一致性审计中…", "ST-BME", { timeOut: 2000 });
|
||||
toastr.info(t("authority.toast.auditRunning"), "ST-BME", { timeOut: 2000 });
|
||||
const result = await _actionHandlers.runAuthorityConsistencyAudit();
|
||||
if (result?.success) {
|
||||
toastr.success(result?.audit?.summary?.label || "Authority 审计完成", "ST-BME");
|
||||
toastr.success(
|
||||
formatI18nValue(result?.audit?.summary, {
|
||||
keyField: "labelKey",
|
||||
paramsField: "labelParams",
|
||||
fallbackField: "label",
|
||||
fallback: t("authority.toast.auditCompleted"),
|
||||
}) || t("authority.toast.auditCompleted"),
|
||||
"ST-BME",
|
||||
);
|
||||
} else {
|
||||
toastr.warning(`Authority 审计失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.auditFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
}
|
||||
} else if (action === "repair-plan") {
|
||||
if (typeof _actionHandlers.runAuthorityConsistencyRepairPlan !== "function") return;
|
||||
if (authorityRepairPlan.requiresConfirmation) {
|
||||
const confirmed = globalThis.confirm?.(
|
||||
`副本同步计划将按以下顺序执行:\n${authorityRepairPlan.steps.map((step, index) => `${index + 1}. ${step.label}`).join("\n")}\n\n其中包含从 Blob Checkpoint 恢复 SQL。此操作只适合 SQL 缺失、损坏或需要回滚时使用,确定继续?`,
|
||||
t("authority.confirm.repairPlan", {
|
||||
steps: authorityRepairPlan.steps.map((step, index) => `${index + 1}. ${formatI18nValue(step, { keyField: "labelKey", paramsField: "labelParams", fallbackField: "label" })}`).join("\n"),
|
||||
}),
|
||||
);
|
||||
if (!confirmed) return;
|
||||
}
|
||||
toastr.info("Authority 副本同步执行中…", "ST-BME", { timeOut: 2000 });
|
||||
toastr.info(t("authority.toast.repairRunning"), "ST-BME", { timeOut: 2000 });
|
||||
const result = await _actionHandlers.runAuthorityConsistencyRepairPlan();
|
||||
if (result?.success) {
|
||||
const stepCount = Number(result?.repairResult?.steps?.length || result?.results?.length || 0);
|
||||
if (result?.partialFailure || result?.repairResult?.partialFailure || result?.outcome === "warning" || result?.repairResult?.outcome === "warning") {
|
||||
toastr.warning(`Authority 副本部分同步失败;记忆图谱不受影响${stepCount > 0 ? `(${stepCount} 步)` : ""}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.repairPartialFailure", { count: stepCount }), "ST-BME");
|
||||
} else if (result?.handoffRequired || result?.repairResult?.handoffRequired) {
|
||||
toastr.success(`Authority 副本同步已交接异步 Job${stepCount > 0 ? `(${stepCount} 步)` : ""}`, "ST-BME");
|
||||
toastr.success(t("authority.toast.repairHandedOff", { count: stepCount }), "ST-BME");
|
||||
} else {
|
||||
toastr.success(`Authority 副本同步已完成${stepCount > 0 ? `(${stepCount} 步)` : ""}`, "ST-BME");
|
||||
toastr.success(t("authority.toast.repairCompleted", { count: stepCount }), "ST-BME");
|
||||
}
|
||||
} else {
|
||||
toastr.warning(`Authority 副本同步失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.repairFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
}
|
||||
} else if (action === "checkpoint") {
|
||||
if (typeof _actionHandlers.writeAuthorityCheckpoint !== "function") return;
|
||||
toastr.info("Authority Checkpoint 写入中…", "ST-BME", { timeOut: 2000 });
|
||||
toastr.info(t("authority.toast.checkpointWriting"), "ST-BME", { timeOut: 2000 });
|
||||
const result = await _actionHandlers.writeAuthorityCheckpoint();
|
||||
if (result?.success) {
|
||||
toastr.success(`Authority Checkpoint 已写入:rev ${Number(result?.result?.checkpointRevision || result?.result?.revision || 0) || "?"}`, "ST-BME");
|
||||
toastr.success(t("authority.toast.checkpointWritten", { revision: Number(result?.result?.checkpointRevision || result?.result?.revision || 0) || "?" }), "ST-BME");
|
||||
} else {
|
||||
toastr.warning(`Authority Checkpoint 写入失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.checkpointWriteFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
}
|
||||
} else if (action === "restore") {
|
||||
if (typeof _actionHandlers.restoreAuthorityCheckpoint !== "function") return;
|
||||
const confirmed = globalThis.confirm?.(
|
||||
`灾难恢复会用 Blob Checkpoint 覆盖 Authority SQL。\n\nSQL rev: ${authorityAuditSqlRevision}\nCheckpoint rev: ${authorityAuditBlobRevision}\n\n只有 SQL 缺失、损坏或明确需要回滚时才继续。确定执行?`,
|
||||
t("authority.confirm.checkpointRestore", { sqlRevision: authorityAuditSqlRevision, checkpointRevision: authorityAuditBlobRevision }),
|
||||
);
|
||||
if (!confirmed) return;
|
||||
toastr.info("Authority Checkpoint 恢复中…", "ST-BME", { timeOut: 2000 });
|
||||
toastr.info(t("authority.toast.checkpointRestoring"), "ST-BME", { timeOut: 2000 });
|
||||
const result = await _actionHandlers.restoreAuthorityCheckpoint();
|
||||
if (result?.success) {
|
||||
toastr.success(`Authority Checkpoint 已恢复:rev ${Number(result?.result?.revision || 0) || "?"}`, "ST-BME");
|
||||
toastr.success(t("authority.toast.checkpointRestored", { revision: Number(result?.result?.revision || 0) || "?" }), "ST-BME");
|
||||
} else {
|
||||
toastr.warning(`Authority Checkpoint 恢复失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.checkpointRestoreFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
}
|
||||
} else if (action === "rebuild-trivium") {
|
||||
if (typeof _actionHandlers.rebuildVectorIndex !== "function") return;
|
||||
@@ -3806,9 +3861,9 @@ function _refreshTaskPersistence() {
|
||||
if (typeof _actionHandlers.captureAuthorityPerformanceBaseline !== "function") return;
|
||||
const result = await _actionHandlers.captureAuthorityPerformanceBaseline();
|
||||
if (result?.ok) {
|
||||
toastr.success("Authority Perf Baseline 已捕获", "ST-BME");
|
||||
toastr.success(t("authority.toast.baselineCaptured"), "ST-BME");
|
||||
} else {
|
||||
toastr.warning(`Authority Perf Baseline 捕获失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.baselineCaptureFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
}
|
||||
} else if (action === "bundle") {
|
||||
if (typeof _actionHandlers.exportDiagnosticsBundle !== "function") return;
|
||||
@@ -3820,26 +3875,26 @@ function _refreshTaskPersistence() {
|
||||
if (typeof _actionHandlers.refreshAuthorityDiagnosticsArtifacts !== "function") return;
|
||||
const result = await _actionHandlers.refreshAuthorityDiagnosticsArtifacts();
|
||||
if (result?.ok) {
|
||||
toastr.success(`已刷新 diagnostics artifact 列表(${Number(result?.entries?.length || 0)} 条)`, "ST-BME");
|
||||
toastr.success(t("authority.toast.artifactsRefreshed", { count: Number(result?.entries?.length || 0) }), "ST-BME");
|
||||
} else {
|
||||
toastr.warning(`diagnostics artifact 列表刷新失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.artifactsRefreshFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
toastr.error(
|
||||
action === "restore"
|
||||
? `Authority Checkpoint 恢复失败: ${error?.message || error}`
|
||||
? t("authority.toast.checkpointRestoreFailed", { error: error?.message || error })
|
||||
: action === "repair-plan"
|
||||
? `Authority 副本同步失败: ${error?.message || error}`
|
||||
? t("authority.toast.repairFailed", { error: error?.message || error })
|
||||
: action === "checkpoint"
|
||||
? `Authority Checkpoint 写入失败: ${error?.message || error}`
|
||||
? t("authority.toast.checkpointWriteFailed", { error: error?.message || error })
|
||||
: action === "rebuild-trivium"
|
||||
? `Authority Trivium 重建失败: ${error?.message || error}`
|
||||
? t("authority.toast.triviumRebuildFailed", { error: error?.message || error })
|
||||
: action === "baseline"
|
||||
? `Authority Perf Baseline 捕获失败: ${error?.message || error}`
|
||||
? t("authority.toast.baselineCaptureFailed", { error: error?.message || error })
|
||||
: action === "artifacts-refresh"
|
||||
? `diagnostics artifact 列表刷新失败: ${error?.message || error}`
|
||||
: `Authority 审计失败: ${error?.message || error}`,
|
||||
? t("authority.toast.artifactsRefreshFailed", { error: error?.message || error })
|
||||
: t("authority.toast.auditFailed", { error: error?.message || error }),
|
||||
"ST-BME",
|
||||
);
|
||||
} finally {
|
||||
@@ -3859,35 +3914,35 @@ function _refreshTaskPersistence() {
|
||||
try {
|
||||
if (action === "copy-path") {
|
||||
await _copyTextToClipboard(artifactPath);
|
||||
toastr.success("诊断包路径已复制", "ST-BME");
|
||||
toastr.success(t("authority.toast.diagnosticPathCopied"), "ST-BME");
|
||||
} else if (action === "download") {
|
||||
if (typeof _actionHandlers.readAuthorityDiagnosticsArtifact !== "function") return;
|
||||
const result = await _actionHandlers.readAuthorityDiagnosticsArtifact(artifactPath);
|
||||
if (!result?.ok || !result?.payload) {
|
||||
toastr.warning(`诊断包读取失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.diagnosticReadFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
return;
|
||||
}
|
||||
const fileName = String(artifactPath.split("/").pop() || `st-bme-diagnostics-${artifactReason}.json`);
|
||||
_downloadJsonFile(result.payload, fileName);
|
||||
toastr.success("诊断包已下载", "ST-BME");
|
||||
toastr.success(t("authority.toast.diagnosticDownloaded"), "ST-BME");
|
||||
} else if (action === "delete") {
|
||||
if (typeof _actionHandlers.deleteAuthorityDiagnosticsArtifact !== "function") return;
|
||||
const confirmed = globalThis.confirm?.(`确定删除该 diagnostics artifact?\n${artifactPath}`);
|
||||
const confirmed = globalThis.confirm?.(t("authority.confirm.deleteArtifact", { path: artifactPath }));
|
||||
if (!confirmed) return;
|
||||
const result = await _actionHandlers.deleteAuthorityDiagnosticsArtifact(artifactPath);
|
||||
if (result?.ok) {
|
||||
toastr.success("诊断包已删除", "ST-BME");
|
||||
toastr.success(t("authority.toast.diagnosticDeleted"), "ST-BME");
|
||||
} else {
|
||||
toastr.warning(`诊断包删除失败:${result?.error || "unknown"}`, "ST-BME");
|
||||
toastr.warning(t("authority.toast.diagnosticDeleteFailed", { error: result?.error || "unknown" }), "ST-BME");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
toastr.error(
|
||||
action === "copy-path"
|
||||
? `复制路径失败: ${error?.message || error}`
|
||||
? t("authority.toast.copyPathFailed", { error: error?.message || error })
|
||||
: action === "download"
|
||||
? `下载诊断包失败: ${error?.message || error}`
|
||||
: `删除诊断包失败: ${error?.message || error}`,
|
||||
? t("authority.toast.diagnosticDownloadFailed", { error: error?.message || error })
|
||||
: t("authority.toast.diagnosticDeleteFailed", { error: error?.message || error }),
|
||||
"ST-BME",
|
||||
);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user