恢复出厂设置=真删除厂商(remove_provider),停用列表命中改真删除压制,pristine 提前到 setup 拍
This commit is contained in:
+80
-5
@@ -78,6 +78,8 @@ const CFG_TEMPLATE = `{
|
||||
}`
|
||||
// modelID 级镜像清单(单模型停用的持久化;整厂级走 cfg 的停用列表)
|
||||
const DISABLED_MIRROR = join(homedir(), ".config", "opencode", "model-manager.disabled.json")
|
||||
// 已彻底删除的厂商列表(remove_provider 标记;面板据此显示「已删除·可找回」)
|
||||
const REMOVED_LIST = join(homedir(), ".config", "opencode", "model-manager.removed.json")
|
||||
// 数据库路径
|
||||
const DB_PATH = join(homedir(), ".local", "share", "opencode", "opencode.db")
|
||||
// SQLite 写执行器(Python,OC 外部子进程跑,绕 OC 自身 DB 写锁)
|
||||
@@ -243,6 +245,24 @@ function mirrorUnmark(provider: string, modelID: string): void {
|
||||
fileLog(`停用镜像已移除 ${provider}/${modelID}`)
|
||||
}
|
||||
|
||||
// 已删除厂商列表(面板显示「已删除·可找回」用)
|
||||
function loadRemoved(): string[] {
|
||||
try {
|
||||
if (!existsSync(REMOVED_LIST)) return []
|
||||
const j = JSON.parse(readFileSync(REMOVED_LIST, "utf-8"))
|
||||
return Array.isArray(j) ? j.filter((x: any) => typeof x === "string") : []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
function saveRemoved(list: string[]): void {
|
||||
try {
|
||||
writeFileSync(REMOVED_LIST, JSON.stringify(list, null, 2), "utf-8")
|
||||
} catch (e: any) {
|
||||
fileLog("已删除厂商列表写入失败: " + (e && e.message ? e.message : String(e)))
|
||||
}
|
||||
}
|
||||
|
||||
// 读 catalog(只读 SQLite,OC 自身持锁但只读不冲突;用 Python 子进程读保证一致)
|
||||
async function readCatalog(): Promise<any | null> {
|
||||
try {
|
||||
@@ -376,8 +396,8 @@ async function processOutbox(ctx: any): Promise<void> {
|
||||
}
|
||||
fileLog(`outbox 命中 op=${op} payload=${JSON.stringify(payload).slice(0, 120)}`)
|
||||
|
||||
// reset 前确保有 pristine 备份
|
||||
if (op === "reset") {
|
||||
// reset 前确保有 pristine 备份(恢复厂商要靠它把出厂模型拉回来)
|
||||
if (op === "reset" || op === "remove_provider") {
|
||||
if (!existsSync(PRISTINE)) {
|
||||
const cat = await readCatalog()
|
||||
if (cat) writeFileSync(PRISTINE, JSON.stringify(cat))
|
||||
@@ -408,8 +428,22 @@ async function processOutbox(ctx: any): Promise<void> {
|
||||
mirrorUnmark(prov, payload.modelID)
|
||||
} else if (op === "enable" && s.startsWith("model not in pristine")) {
|
||||
mirrorUnmark(prov, payload.modelID)
|
||||
} else if (op === "remove_provider" && (s.startsWith("removed_provider ") || s.startsWith("provider not found"))) {
|
||||
// 恢复出厂设置(格式化)= 彻底删除该厂 → 记进 cfg 停用列表,OC 刷新 catalog 回潮时自动再压
|
||||
const list = readStopList()
|
||||
if (!list.some((p) => patMatch(p, prov))) {
|
||||
list.push(prov)
|
||||
writeStopList(list)
|
||||
fileLog(`已删除厂商 ${prov},并记入 cfg 停用列表(cfg 共 ${list.length} 项,持久化)`)
|
||||
}
|
||||
const rm = loadRemoved()
|
||||
if (!rm.includes(prov)) {
|
||||
rm.push(prov)
|
||||
saveRemoved(rm)
|
||||
fileLog(`已删除厂商列表已记录 ${prov}(面板可显示「已删除·可找回」)`)
|
||||
}
|
||||
} else if (op === "reset" && (s.startsWith("reset ") || s.startsWith("provider not in pristine"))) {
|
||||
// 恢复出厂/格式化:整厂从停用列表摘除(含命中它的通配项),单模型镜像清空
|
||||
// 恢复该厂商(撤销删除/停用):从停用列表摘除,单模型镜像清空
|
||||
const list = readStopList()
|
||||
const kept = list.filter((p) => !patMatch(p, prov))
|
||||
const removed = list.length - kept.length
|
||||
@@ -423,6 +457,11 @@ async function processOutbox(ctx: any): Promise<void> {
|
||||
saveMirror(map)
|
||||
fileLog(`恢复出厂 ${prov}:单模型停用镜像已清空`)
|
||||
}
|
||||
const rm = loadRemoved()
|
||||
if (rm.includes(prov)) {
|
||||
saveRemoved(rm.filter((x) => x !== prov))
|
||||
fileLog(`恢复出厂 ${prov}:已从「已删除厂商」列表移除(可再被 /manage 看到)`)
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
fileLog("停用状态维护异常(已吞掉): " + (e && e.message ? e.message : String(e)))
|
||||
@@ -553,6 +592,19 @@ elif op == "reset":
|
||||
else:
|
||||
out("provider not in pristine: " + provider)
|
||||
|
||||
elif op == "remove_provider":
|
||||
# 真删除:把整个 provider 节点从 catalog body 里摘掉(/models 里该厂彻底消失)
|
||||
# 恢复靠 reset(pristine 里有完整节点)
|
||||
cat = load()
|
||||
body = json.loads(cat["body"])
|
||||
if provider not in body:
|
||||
out("provider not found: " + provider); sys.exit(0)
|
||||
n = len(body[provider].get("models", {}))
|
||||
body.pop(provider, None)
|
||||
cat["body"] = json.dumps(body, ensure_ascii=False, separators=(",", ":"))
|
||||
save(cat)
|
||||
out("removed_provider " + provider + " models=" + str(n))
|
||||
|
||||
else:
|
||||
out("unknown op: " + op)
|
||||
`
|
||||
@@ -569,6 +621,24 @@ function ensureDbExec(): void {
|
||||
}
|
||||
}
|
||||
|
||||
// 出厂备份:setup 时就拍一次(幂等)。
|
||||
// 为什么提前拍:若等到第一次 reset/remove_provider 才拍,那时的 catalog 可能已被
|
||||
// 改过(模型被删过),拍到的就不是「出厂态」,恢复出来的会缺模型。
|
||||
async function ensurePristine(): Promise<void> {
|
||||
try {
|
||||
if (existsSync(PRISTINE)) return
|
||||
const cat = await readCatalog()
|
||||
if (!cat || !cat.body) {
|
||||
fileLog("pristine 暂未建立(catalog 读不到,稍后重试)")
|
||||
return
|
||||
}
|
||||
writeFileSync(PRISTINE, JSON.stringify(cat))
|
||||
fileLog("pristine 出厂备份已建立(setup 阶段)")
|
||||
} catch (e: any) {
|
||||
fileLog("pristine 建立失败(已吞掉): " + (e && e.message ? e.message : String(e)))
|
||||
}
|
||||
}
|
||||
|
||||
// 读 catalog 的 time_updated(单值,便宜;用于判断 OC 是否刷新覆盖过 catalog)
|
||||
async function readCatalogTime(): Promise<number> {
|
||||
return new Promise((resolve) => {
|
||||
@@ -626,9 +696,13 @@ async function reconcile(ctx: any): Promise<void> {
|
||||
const p = body[id] || {}
|
||||
const name = p.name || ""
|
||||
const models = p.models || {}
|
||||
if (stopList.length && stopListHits(stopList, id, name) && Object.keys(models).length) {
|
||||
plan.push({ op: "disable_all", payload: { provider: id } })
|
||||
// cfg 停用列表命中 → 真删除厂商节点(不能只清空 models:OC 刷新 catalog 会把
|
||||
// provider 节点连同模型一起加回来,只有摘掉节点才算真删除)
|
||||
if (stopList.length && stopListHits(stopList, id, name)) {
|
||||
plan.push({ op: "remove_provider", payload: { provider: id } })
|
||||
continue
|
||||
}
|
||||
// modelID 级镜像命中 → 只摘掉那一个模型
|
||||
const ids = mirror[id]
|
||||
if (ids && ids.length) {
|
||||
for (const mid of ids) {
|
||||
@@ -671,6 +745,7 @@ export default {
|
||||
}
|
||||
ensureDbExec()
|
||||
ensureCfg() // 防御:OC 更新删掉 cfg 时立即释放新配置(含停用列表镜像恢复)
|
||||
await ensurePristine() // 出厂备份提前拍,避免首次操作时 catalog 已被改
|
||||
|
||||
// 后台 daemon:每 5s 轮询 outbox(普通文件读写,不碰 DB 锁)+ 对账压制回潮
|
||||
let stopped = false
|
||||
|
||||
Reference in New Issue
Block a user