mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-12 12:17:23 +00:00
feat(lua)!: 插件只通过 modelrouter SDK 与内核交互,且不再随内核安装
- 新增唯一公开接口表 `modelrouter`(sdk_version=1.0):http / secrets / kernel / adapters / plugin_dir。成员与内部桥是**同一对象**而非副本 —— 复制会让凭据保险库分叉(插件写 A、内核读 B)。 - `llmsproxy` 与 `__llmsproxy_*` 降为同对象兼容别名,只为已装插件继续可用。 仓库内 4 个插件(channels / billing / quota / usage)全部改用 `modelrouter.*`:此前它们直接依赖内核私有全局,内核无法在不炸插件的前提下 改实现(重命名、加守卫、换实现都算破坏性变更)。 - 插件不再由网关安装:`SeedBundled()` 变彻底 no-op,`core.New` 不再调用; `ReadBundledPlugin` 改为按源文件定位(脱离 cwd),只服务测试与开发工具。 - 桌面端删除 `BUILTIN_PLUGINS` 与 `syncBundledPluginsOnUpgrade`:升级不再 删除、改名或覆盖用户插件文件。插件是运维资产,不是内核资源。 - `Builtin` 不再靠"源码与仓库示例逐字节相同"推断,插件一律视为外部文件。 - 判据:SDK 存在/版本/形状、别名与内部桥同一张表、适配器 VM 拿不到 SDK、 经 SDK 真实写读凭据并驱动内核动作、GUI 升级不动插件文件、release 包不含插件。 说明:channels.lua / quota.lua 同时带有此前的渠道与额度改动(含 OpenCode 免费池 监控),与 SDK 改名同处一个文件,无法文件级拆分,故一并纳入。
This commit is contained in:
@ -9,22 +9,22 @@ import (
|
||||
// **测试绝不允许写生产存储** —— 这条是真实事故的回归判据。
|
||||
//
|
||||
// 事故:channels 插件用 storeDir() 决定自己的存储位置,而 storeDir 有多级兜底:
|
||||
// 环境变量 → settings → **读 /etc/llmsproxy/config.yaml 的 plugin_dir** →
|
||||
// 硬编码 /etc/llmsproxy/plugins。测试台没设环境变量时,后两级直接命中**生产**:
|
||||
// 环境变量 → settings → **读 /etc/modelrouter/config.yaml 的 plugin_dir** →
|
||||
// 硬编码 /etc/modelrouter/plugins。测试台没设环境变量时,后两级直接命中**生产**:
|
||||
//
|
||||
// 测试插件目录 = /tmp/TestXXX/002/plugins
|
||||
// storeDir 结果 = /etc/llmsproxy/plugins ← 实测
|
||||
// storeDir 结果 = /etc/modelrouter/plugins ← 实测
|
||||
//
|
||||
// 于是测试加载真 channels.lua 时**读的是生产存储、写的也是生产存储**。一次全量
|
||||
// `go test ./internal/lua` 就把生产账号池覆盖成测试的空 pool:
|
||||
// /etc/llmsproxy/plugins/.channels-store.json 从 1730 变 1986 字节,
|
||||
// /etc/modelrouter/plugins/.channels-store.json 从 1730 变 1986 字节,
|
||||
// 13 个账号变成 0 个(凭据还留在保险库,所以症状是"账号凭空消失")。
|
||||
//
|
||||
// 同类前科(本文件存在的另一半理由):曾经有 26 个测试账号被写进生产
|
||||
// .channels-store.json。两次都是"测试没隔离存储目录",所以这条判据必须存在。
|
||||
func TestStoreDirIsolatedInTests(t *testing.T) {
|
||||
_, ps, pdir := runtimePlugins(t)
|
||||
src, err := bundledAdapters.ReadFile("plugins/channels.lua")
|
||||
src, err := ReadBundledPlugin("channels")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -45,17 +45,17 @@ func TestStoreDirIsolatedInTests(t *testing.T) {
|
||||
t.Logf("storeDir 结果 = %s", got)
|
||||
|
||||
// ① 绝不能是生产目录
|
||||
if got == "/etc/llmsproxy/plugins" || strings.HasPrefix(got, "/etc/llmsproxy") {
|
||||
if got == "/etc/modelrouter/plugins" || strings.HasPrefix(got, "/etc/llmsproxy") {
|
||||
t.Fatalf("storeDir 指向了**生产**目录 %q —— 测试会覆盖生产账号池\n"+
|
||||
"(真实事故:13 个账号被覆盖成 0 个)。修法:runtimePlugins 设\n"+
|
||||
"LLMSPROXY_PLUGIN_DIR 到 t.TempDir()", got)
|
||||
"MODELROUTER_PLUGIN_DIR 到 t.TempDir()", got)
|
||||
}
|
||||
// ② 必须落在测试的临时目录里
|
||||
if !strings.Contains(got, pdir) && !strings.HasPrefix(got, os.TempDir()) {
|
||||
t.Errorf("storeDir 应落在临时目录(%s 或 %s),实得 %q", pdir, os.TempDir(), got)
|
||||
}
|
||||
// ③ 环境变量必须真的被设上(机制而非巧合)
|
||||
if os.Getenv("LLMSPROXY_PLUGIN_DIR") == "" {
|
||||
t.Error("runtimePlugins 必须设 LLMSPROXY_PLUGIN_DIR(否则 storeDir 会读生产 config.yaml)")
|
||||
if os.Getenv("MODELROUTER_PLUGIN_DIR") == "" {
|
||||
t.Error("runtimePlugins 必须设 MODELROUTER_PLUGIN_DIR(否则 storeDir 会读生产 config.yaml)")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user