概览
dsh-plugin-local-model
README / ZH
插件文档
目录摘要
dsh.pub 核对固定版本的组合包契约、运行时事实与分发语义;完整 README 请查看源仓库。
在 GitHub 阅读完整 READMELIMITATIONS
已知限制
dsh 目前处于开发者预览期,接口仍在变动,因此**所有宿主集成点都做了显式降级,宁可少做也不报错**: - 本插件 `inject` 为空:`llm` / `tools` / `commands` / `timer` 全部通过 `ctx.get()` 读取, 缺失或接口对不上时只跳过对应增强,**模型加载与空闲卸载这条主线始终可用**。 - `llmBridge` 会依次探测 `registerProvider / upsertProvider / registerRoute / addRoute / setRoute`, 全都不匹配时打印可直接粘贴的 `settings.yaml` 片段(第 5 节),而不是失败。 - `/local-model` 命令同时挂了 `execute` 和 `run` 两种入口名,以适配不同版本的 commands 服务。 - `src/commands.ts` 与 `/local-model status` 的输出格式可能随宿主调整而调整 —— 它们只是便利功能, 真实状态以 `GET http://127.0.0.1:18080/local-model/status` 为准。 - `types/dsh-ambient.d.ts` 是**离线编译用**的环境声明(只覆盖本插件用到的接口)。 在 dsh 仓库内开发时以主仓库的真实类型为准;若出现重复声明告警,把它从 `tsconfig.json` 的 `include` 里去掉即可。 - 未做:多模型同时常驻(严格一次一个,符合"释放资源"的目标)、图像输入透传的端到端验证、 Windows 上 llama-server 的优雅退出(llama.cpp 无状态可刷,直接结束进程树)。 ### 浏览器半侧是复刻的构建,不是官方预设 dsh 官方明确说明产出 `dsh.client` bundle 的 `clientBundle` 预设**不在已发布的包里**, 仓库之外的插件必须自己复刻。本项目的复刻落在 `scripts/build-client.mjs`:esbuild 打成 CJS (`react` / `react/jsx-runtime` 全部 external,由宿主提供),再手工套上 `window.__ModuleLoader__.load({ id, factory })` 外壳;形状是拿本机三个**已经在生效**的 `dsh.client` 包的产物逐字节对照出来的,并由 `npm run test:client` 用假 loader 真跑一遍守住。 升级 dsh 后如果设置页不出现,先跑这两个脚本,再检查 `dsh.client.inject` 里的客户端包名是否仍然存在。 面板的数据面是**自建的同源 HTTP 桥**而不是 dsh 的 settings 命名空间(原因见第 4 节), 因此设置写在插件自己的 `<DSH_HOME>/local-model/state/config.json` 里。这是刻意的取舍: 它不依赖宿主内部接口的版本,代价是这些设置不出现在 `settings.yaml` 中,也就不能跟着 dsh 的凭据/同步机制走。桥只服务回环来源 —— 通过局域网访问 dsh 时面板不可用。 界面文案目前是中文硬编码(只额外注册了中英两份分区标题词条)。 ### 本包的 manifest 是按实机对照确认的 `dsh.bundle` 与 `dsh.client` 的形状不是照文档猜的,是拿本机几个**已经在生效**的第三方插件 逐个字段对出来的(`dshmarket`、`@linxin666/dsh-web-all`、`dsh-better-sidebar`): ```json "dsh": { "bundle": { "patch": "./cordis.patch.yml" }, "client": { "inject": ["@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-settings"], "platform": "web" } } ``` `engines.dsh` 与 `@deepseek-ai/dsh-tools` 的 peer 范围都**不再声明**,只留 `engines.node`。 历史上这里写过显式的预发布分支(`^0.1.0-rc.1 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-0`), 原因是 node-semver 的一条规则:**范围里不含预发布标签的比较符不会匹配预发布版本** —— 所以裸 `"*"` 反而匹配不到 harness 的 `-rc.x` 构建,必须逐个 minor 补 `|| ^X.Y.Z-0` 分支。 但这条路是死胡同:harness 每出一个新 minor 预发布(例如 `0.2.0-rc.1`)就要补一个分支, 漏补就会被市场判成「与当前 DSH 不兼容」并弹出崩溃警告,而插件本身其实照常工作 —— 这是纯声明侧的维护成本,不是真实的功能依赖。 现在改为**不设版本闸门**:删掉 `engines.dsh`,`dsh-tools` 的 peer 范围回归 `"*"`。 插件对 harness 的耦合实际很弱,全部走运行时能力探测 —— `lib/tools.js` 对 `dsh-tools` 是 `await import()` 且失败只告警跳过工具注册,客户端依赖走 `dsh.client.inject` 由宿主解析, 没有任何一处硬编码 harness 版本号。因此版本上限交给宿主/市场自己去管,插件不再替它断言。 官方 `@deepseek-ai/*` 仍一律写进 `peerDependencies`(而不是 `dependencies`),并全部标 `optional`。 `cordis.patch.yml` 里的 loader entry id 带作者前缀(`dzqjoker-local-model`),不用裸的 `local-model`:cordis 拒绝加载含重复 entry id 的插件树,而插件市场一旦发现新装插件的 id 与已加载的相撞,会直接把该插件卸载(否则下次启动整个 profile 都起不来)。 `exports` 只声明了 `.` / `./client` / `./cordis.patch.yml` / `./package.json` 四个入口, 与上述包的风格保持一致。 `repository` 字段必须指回本仓库 —— 插件市场用它把 npm 包与列表条目关联起来。 本包不再有 `private` 字段:它是要被公开安装的插件。
