chore(web): 降级留痕 + 收尾 —— 静默失效改为可观测;picker 升 0.1.5 消漂移(档案 88 / T07)

- 【留痕】model-catalog 读不到目录:console.warn(每进程一次)+ 新增 catalogDiagnostics(),并把结果透出到 GET /api/me/model-providers 的 catalog 字段;plugin-compat 的 platformPkgCount()=0 同样告警一次 ⇒「功能没做」与「做了但读不到目录」从此**可区分**(这正是那个 P1 长期潜伏的原因)
- 【前端】插件 0.3.13:厂家目录不可读 / 为空时**如实提示**(含目录路径),不再静默给空列表
- 【消漂移】poc/workspace-scoped-picker 升 0.1.5 并重打包铺发 —— 两实例已装 0.1.5 且含按序探测的新代码(此前只改了源码、线上还是旧候选)
- 【文档】README 补 DSHS_PACKAGE_DIR / DSHS_COMPAT_ROOT / DSHS_PI_AI_DATA_DIR(逃生口此前**未在任何地方暴露**)
- 【回归】verify-dsh-install 扩到 6 组(含「读不到 ≠ 目录为空」的区分);verify-platform-admin-section 加 2 条断言(词典 284/284)
- 真环境回归:picker 在 /usr/local 仍 import OK(没把本来能用的改坏)|providers 39(端点回 38)|platformPkgCount 223
This commit is contained in:
admin committed 2026-09-14 04:52:35 +08:00
1 parent cbaf3ad653
commit 1d72e8f9c8
9 files changed
+127 -6

No files matched your search

+37
View File
@@ -175,9 +175,37 @@ function parseProvider(id: string, raw: string): CatalogProvider | null {
let cache: { at: number; list: CatalogProvider[] } | null = null
const TTL_MS = 10 * 60 * 1000
/** 读取结果的可读性诊断。 */
export interface CatalogDiagnostics {
/** 实际使用的目录(`dsh-install.ts` 探测出来的)。 */
dir: string
/** 能否读到(`false` = 探测/读取失败 ⇒ 界面只剩内置 DeepSeek + 自定义网关)。 */
readable: boolean
/** 读到了几个厂家。 */
count: number
}
/**
* 目录可读性诊断(2026-09-14 加,见 `04-调整方案/88`)。
*
* 为什么必须加:定位/读取失败原本**完全静默**(catch 后返回 `[]`),于是
* 「功能没做」与「功能做了但读不到目录」在界面上**一模一样** —— 一个 P1 缺陷就这样在
* 另一种安装布局(`npm root -g` = `/usr/lib/node_modules`)上潜伏了很久,最后靠人工
* 比对才看出来。⇒ ① 读不到时 `console.warn`(每进程一次,不刷日志);
* ② 把结果透出到 `/api/me/model-providers`,让「到底读没读到」可观测。
*/
let diag: CatalogDiagnostics | null = null
let warnedUnreadable = false
/** 最近一次读取的可读性诊断(尚未读过时返回 `null`)。 */
export function catalogDiagnostics(): CatalogDiagnostics | null {
return diag
}
/**
* 读出目录里的全部厂家(按中文名/官方名排序)。
* 目录不存在(例如本地开发机)⇒ 返回空数组,**不抛错**:调用方据此降级为"只有内置 + 自定义"。
* ⚠️ 降级**不是无声的** —— 见 `catalogDiagnostics()` 与上方的 `console.warn`。
*/
export async function listCatalogProviders(force = false): Promise<CatalogProvider[]> {
if (!force && cache !== null && Date.now() - cache.at < TTL_MS) return cache.list
@@ -186,6 +214,14 @@ export async function listCatalogProviders(force = false): Promise<CatalogProvid
try {
files = await readdir(dir)
} catch {
diag = { dir, readable: false, count: 0 }
if (!warnedUnreadable) {
warnedUnreadable = true
console.warn(
`[model-catalog] 厂家目录不可读:${dir} —— 「设置 → 模型设置 → 新增厂家」只会显示内置 DeepSeek 与自定义网关。` +
' 检查 DSHS_DSH_BIN / DSH_PACKAGE_DIR 是否正确(见 src/web/dsh-install.ts)。',
)
}
cache = { at: Date.now(), list: [] }
return []
}
@@ -200,6 +236,7 @@ export async function listCatalogProviders(force = false): Promise<CatalogProvid
}
}
out.sort((a, b) => a.label.localeCompare(b.label, 'zh-Hans-CN'))
diag = { dir, readable: true, count: out.length }
cache = { at: Date.now(), list: out }
return out
}