tui.js:/manage 来源B改读 credential 表(active=1 的 integration_id)前缀匹配 catalog,能管到内置 /connect 的 provider(如 Xiaomi 全家族)

This commit is contained in:
youpaishidifu
2026-10-19 11:46:33 +08:00
parent 2f64df6a9b
commit 8e07bd8af8
+17 -14
View File
@@ -426,17 +426,17 @@ function mmReadCatalog() {
return new Promise((resolve) => {
try {
const py = process.platform === "win32" ? "python" : "python3"
// 注意两点坑:
// 1. Python `-c` 单行里 `if row: import sys;` 非法(import 不能跟 if 同行)→ 提前 import sys
// 2. Windows 控制台默认 GBK 编码,catalog 含非 GBK 字符(如 U+2011)会 UnicodeEncodeError
// → 用 sys.stdout.buffer.write 走原始字节(utf-8 编码),Node 侧按 utf-8 解码
// 读 catalog + credential(credential 表存 /connect 传 key 的内置 provider,如 xiaomi)
// 坑:① Python -c 单行里 if row: import sys 非法 ② Windows GBK 编码 → 全走 buffer.write utf-8
const script =
"import sqlite3,os,sys;" +
"import sqlite3,os,sys,json;" +
"db=os.path.join(os.path.expanduser('~'),'.local','share','opencode','opencode.db');" +
"con=sqlite3.connect(db);" +
"row=con.execute(\"SELECT value FROM kv WHERE key='models-dev:catalog'\").fetchone();" +
"creds=[r[0] for r in con.execute(\"SELECT integration_id FROM credential WHERE active=1\").fetchall()];" +
"con.close();" +
"sys.stdout.buffer.write(row[0].encode('utf-8')) if row else None"
"obj={'catalog':(row[0] if row else None),'credentials':creds};" +
"sys.stdout.buffer.write(json.dumps(obj, ensure_ascii=False).encode('utf-8'))"
const r = spawn(py, ["-c", script], {
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" },
@@ -500,24 +500,27 @@ async function openModelManager(context) {
}
// 来源 B:catalog 里「已连接内置 provider」。
// 「已连接」= 该 provider 的 env 字段里的某个 API key 存在于 process.env
// (用户 /connect 时 OC 把 key 注入当前进程环境,运行时态,不落在文件里)。
// 只列「能判定已连接」的 catalog provider,不会全量拉 232 个。
// credential 表存的是"集成名"(如 xiaomi),catalog 存的是"具体 providerID"(xiaomi、
// xiaomi-token-plan-cn、xiaomi-token-plan-sgp、xiaomi-token-plan-ams 等)。
// 匹配规则:catalog providerID 以 credential.integration_id 开头(前缀匹配)就算已连接。
const cat = await mmReadCatalog()
if (cat && cat.body) {
if (cat && cat.catalog && cat.credentials) {
let body = {}
try {
body = JSON.parse(cat.body)
body = JSON.parse(cat.catalog)
} catch (e) {
log("[oc-session-all] /manage catalog body parse 失败: " + e.message)
}
const credentials = Array.isArray(cat.credentials) ? cat.credentials : []
if (credentials.length === 0) {
log("[oc-session-all] /manage credential 表无 active=1 记录(没 /connect 过内置 provider)")
}
for (const cid of Object.keys(body)) {
const cv = body[cid] || {}
const models = Object.keys((cv && cv.models) || {})
if (models.length === 0) continue
// 已连接判定:provider.env 数组里任一 key 在 process.env 里有值
const envs = Array.isArray(cv.env) ? cv.env : []
const isConnected = envs.some((k) => process.env && process.env[k])
// 前缀匹配:providerID 以某个 credential.integration_id 开头
const isConnected = credentials.some((integ) => cid === integ || cid.startsWith(integ + "-") || cid.startsWith(integ + "_"))
if (!isConnected) continue
const name = cv.name || cid
addProvider(name, models, "catalog")