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:
1 parent
cbaf3ad653
commit
1d72e8f9c8
9 files changed
+127
-6
No files matched your search
@@ -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
|
||||
}
|
||||
|
||||
@@ -72,11 +72,24 @@ function platformPkgDir(pkg: string): string {
|
||||
return join(dshScopeDir(), shortName(pkg))
|
||||
}
|
||||
|
||||
/** 是否已经就「平台包目录不可读」告过一次警(避免刷日志)。 */
|
||||
let warnedScopeUnreadable = false
|
||||
|
||||
/** 平台内置 @deepseek-ai 包的数量(目录级,低成本)。 */
|
||||
export function platformPkgCount(): number {
|
||||
try {
|
||||
return readdirSync(dshScopeDir()).filter((n) => !n.startsWith('.')).length
|
||||
} catch {
|
||||
// ⚠️ 这里**不能只返回 0**:调用方会把它当成"没有平台包"⇒ 预检直接退化成
|
||||
// 「平台包目录不可读」——**安全网静默失效**(上传不兼容插件不再被拦)。
|
||||
// 2026-09-14:加一次告警,让"读不到"这件事可观测(见 `04-调整方案/88`)。
|
||||
if (!warnedScopeUnreadable) {
|
||||
warnedScopeUnreadable = true
|
||||
console.warn(
|
||||
`[plugin-compat] 平台包目录不可读:${dshScopeDir()} —— 插件兼容性预检将退化为「不检查」。` +
|
||||
' 检查 DSHS_DSH_BIN / DSH_COMPAT_ROOT 是否正确(见 src/web/dsh-install.ts)。',
|
||||
)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { requireAuth } from '../middleware/authn.js'
|
||||
import { homeRoot, userRoot } from '../../fs/workspace.js'
|
||||
import { deriveKey, encrypt } from '../../crypto.js'
|
||||
import { toPublicUser } from '../../db/types.js'
|
||||
import { isCatalogProvider, isCnProvider, listCatalogProviders } from '../model-catalog.js'
|
||||
import { catalogDiagnostics, isCatalogProvider, isCnProvider, listCatalogProviders } from '../model-catalog.js'
|
||||
import { PROTOCOLS } from '../model-landing.js'
|
||||
import {
|
||||
clearSessionCookie,
|
||||
@@ -243,6 +243,9 @@ export const authRoutes: FastifyPluginAsync = async (app) => {
|
||||
// 只回前 60 个模型名给界面展示(足量的"看到它自带什么"),不整份下发。
|
||||
models: p.models.slice(0, 60).map((m) => m.name ?? m.id),
|
||||
})),
|
||||
// 目录**可读性**:读不到时前端要如实说明"为什么只剩两项",而不是静默给个空列表
|
||||
// (2026-09-14:这个静默曾让一个 P1 布局缺陷长期不可见,见 `04-调整方案/88`)。
|
||||
catalog: catalogDiagnostics(),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user