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

+26
View File
@@ -113,6 +113,32 @@ const real = dshPackageRoot()
ok('返回非空绝对路径', typeof real === 'string' && real.length > 0 && real.startsWith('/'), real)
ok('dshScopeDir 形状正确', dshScopeDir().endsWith(join('node_modules', '@deepseek-ai')), dshScopeDir())
// ── 6. 目录可读性诊断:**「读不到」必须与「目录为空」可区分**(2026-09-14 留痕)─────
console.log('\n[6] 目录可读性诊断(静默降级 → 可观测)')
const cat = await import(pathToFileURL(join(ROOT, 'lib/web/model-catalog.js')).href)
const warnings = []
const realWarn = console.warn
console.warn = (...a) => warnings.push(a.join(' '))
clearEnv()
process.env.PI_AI_DATA_DIR = join(T, 'no-such-data-dir') // 不存在 ⇒ 读不到
process.env.DSH_PACKAGE_DIR = '/nonexistent'
await cat.listCatalogProviders(true)
const d1 = cat.catalogDiagnostics()
ok('读不到时 readable=false', d1 !== null && d1.readable === false, JSON.stringify(d1))
ok('读不到时 count=0', d1 !== null && d1.count === 0)
ok('读不到时**告警过一次**(不再静默)', warnings.some((w) => w.includes('厂家目录不可读')), warnings[0]?.slice(0, 60) ?? '(无)')
clearEnv()
const emptyDir = join(T, 'empty-data')
mkdirSync(emptyDir, { recursive: true })
process.env.PI_AI_DATA_DIR = emptyDir // 存在但没有任何 <id>.json ⇒ "目录为空"
await cat.listCatalogProviders(true)
const d2 = cat.catalogDiagnostics()
ok('目录可读但为空时 readable=true(与"读不到"可区分)', d2 !== null && d2.readable === true && d2.count === 0, JSON.stringify(d2))
ok('已告警过就不再刷日志', warnings.filter((w) => w.includes('厂家目录不可读')).length === 1, String(warnings.length))
console.warn = realWarn
rmSync(T, { recursive: true, force: true })
console.log('')
if (failed > 0) {
+17
View File
@@ -71,6 +71,7 @@ const DATA = {
{ id: "moonshotai-cn", label: "月之暗面 Moonshot(中国)", api: "openai-completions", baseURL: "https://api.moonshot.cn/v1", cn: true, modelCount: 12, models: ["kimi-k2", "moonshot-v1-8k"] },
{ id: "openai", label: "OpenAI", api: "openai-responses", baseURL: null, cn: false, modelCount: 20, models: ["gpt-4o"] },
],
catalog: { dir: "/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@earendil-works/pi-ai/dist/providers/data", readable: true, count: 2 },
},
};
const sandbox = {
@@ -242,6 +243,22 @@ ok("非 admin:不出现任何功能项", !PORTAL_ITEMS.some(k => tUser.include
tMs.includes("中国大陆") && tMs.includes("国际") && tMs.includes("月之暗面 Moonshot(中国)") && tMs.includes("OpenAI"));
ok("模型设置:默认选中「内置 DeepSeek」时给出回落说明",
tMs.includes("密钥留空则回落到平台共享密钥"));
// 2026-09-14 留痕:目录读不到时要**如实说明**,不能静默给个空列表
{
const saved = DATA["/api/me/model-providers"];
DATA["/api/me/model-providers"] = {
providers: [],
catalog: { dir: "/opt/nope/pi-ai/dist/providers/data", readable: false, count: 0 },
};
const tBad = text(await render(ms.Comp)).join(" | ");
ok("模型设置:目录不可读时如实提示(含目录路径,不静默空列表)",
tBad.includes("未能读取官方厂家目录") && tBad.includes("/opt/nope/pi-ai"), tBad.slice(0, 0) + "命中");
DATA["/api/me/model-providers"] = { providers: [], catalog: { dir: "/x", readable: true, count: 0 } };
const tEmpty = text(await render(ms.Comp)).join(" | ");
ok("模型设置:目录为空的文案与「读不到」不同",
tEmpty.includes("官方厂家目录为空") && !tEmpty.includes("未能读取官方厂家目录"));
DATA["/api/me/model-providers"] = saved;
}
ok("模型设置:词典备齐「只填密钥」相关文案(目录提示 / 展开模型)",
!!ZH["ms.catalogHint"] && !!ZH["ms.showModels"] && !!ZH["ms.hideModels"] && !!ZH["ms.pickProvider"]);
// 落地在 spawn 时 ⇒ 文案必须说清"重启才生效",否则用户会以为保存即生效