fabric:给 DeepSeek Harness 装上 Minecraft 式 Mixin 钩子

前言

DeepSeek Harness(dsh)的核心理念是「一切皆插件」:模型、工具、会话、沙箱、调度,乃至界面,都可以在 Cordis 内核上组合或替换,而不必去改 Harness 源码。官方仓库 deepseek-ai/deepseek-harness 把这件事写得很清楚。

插件能「加能力」,并不等于插件能「改别人的能力」。一个社区插件如果想在不 fork、不改对方源码的前提下,改写另一个插件里某个函数的参数或返回值,官方插件机制通常到不了这一层。Minecraft 生态里,Fabric 用 Mixin 在加载期改写游戏代码,原版 JAR 可以保持不动。社区插件 fabric 做的就是同一类事情,只不过目标从游戏模组换成了 DSH / Cordis 插件。

本文介绍的是社区目录里的 fabric,不是 DeepSeek 官方应用。目录站点 deepseek-harness-plugin.com 是独立收录站,与 DeepSeek / 幻方没有官方从属关系。

fabric 是什么

fabric 是一款 工具与能力 类 DSH 插件,由 GitHub 组织 omdsh-dev 维护,仓库地址是 omdsh-dev/fabric。目录页一句话定位是:「一种类似 MC Fabric 的 hook 处理器。」

仓库自述更完整:它是一套面向 DSH 的 Fabric / Mixin 扩展层,结构对齐上游 fabric 的三包拆分,再加一个可安装的 profile bundle 载体。根包名是 cordis-fabric-bundle,当前版本 0.0.3,主要语言是 TypeScript。GitHub 主题标了 dshdsh-plugin。目录页 2026-08-09 收录,当时显示 9 星;截至 2026-08-17,GitHub 仓库星标为 14。

它要解决的问题可以压成一句:让受信任的插件在加载期对目标模块做代码变换,从而在不修改对方源码的情况下挂钩子。它不是给模型用的工具,也不是给聊天窗口加按钮;底层变换本身不产生任何模型可见内容。

核心功能

仓库里只有三个完整包,三包之外的改动(例如官方 @deepseek-ai/dsh-tool-cordis 的行为修正)一律放在 patches/ 里做 pnpm 依赖补丁,不会再拆第四个包。

1、cordis-fabric:纯 Cordis 的加载期变换服务。 提供 FabricServicebootstrapFabric、Orchestrion 变换、Node loader hooks、bridge、浏览器 transform 和 testkit。这个包不导入任何 DSH 模块。受信任插件通过 ctx.fabric.register() 注册 patch,可以对目标函数做四种操作:

  • before:在原函数体执行前改写参数
  • after:观察或替换成功结果(含异步 settlement 之后)
  • around:决定原函数体是否执行,可通过 invoke() 委托
  • replace:完全接管调用;只有 handler 调用 invoke() 时才跑原函数体

机制是加载期代码变换:transform hook 把目标函数体重写为向进程内 bridge 发调用记录,runtime 再分发给当前注册的 handler。没有活跃 handler 时,变换后的代码原样委托给原函数体。

2、cordis-fabric-api:协作式兼容门面。 只 peer 依赖 Cordis 和 cordis-fabric,提供 FabricCompatServicebuildCompatInstrumentations。文档把它比作 Minecraft Fabric API 那一层:在 loader 与 Mixin 之上,给 Mod 一个相对稳定的注册入口。bundle 不会自动挂这一行,需要 Mod 自己 import FabricCompatService from 'cordis-fabric-api'

3、cordis-fabric-dsh:DSH 集成包。 对外暴露 ctx.fabricAgentctx.fabricToolsctx.fabricPromptctx.fabricCommands,浏览器侧是 ctx.fabricClient,另外还有包 invariant 和 profile bootstrap(installFabricBootstrap)。这些 facade 把 prompt、tools、commands、agent 事件和浏览器 command/slot 注册委托给权威 DSH 服务,不另存一份领域状态,也不能绕过权限、审批、超时、日志或取消语义。

bundle 往 profile 里插入的两行默认都是禁用的 opt-in:

- id: cordis-fabric
  name: 'cordis-fabric'
  disabled: true

- id: cordis-fabric-dsh
  name: 'cordis-fabric-dsh'
  disabled: true

装上 bundle 不等于 Fabric 层已经生效。需要在 profile 组合里启用这两行,并且用仓库提供的 fabric-dsh 启动器注入 loader hooks。直接跑官方 dsh 只走官方代码,不会激活 Fabric hooks。

信任模型也写得很硬:patch handler 是通过 ctx.fabric.register() 绑上去的受信任代码;可执行 handler 不会从 YAML 或模型输入里反序列化。cordis_mount 临时插件和 repository 插件在获得显式授权前不得使用 Fabric 能力。

安装与启用

社区目录页给出的安装命令如下,在 DeepSeek Harness 终端里运行:

dsh plugin add github:omdsh-dev/fabric

目录页同时说明:如需可复现安装,可以固定 commit 哈希:

dsh plugin add github:omdsh-dev/fabric#<commit>

<commit> 换成仓库里实际的提交哈希。不要凭空编一个。

仓库英文 README 当前推荐的是官方 bundle 插件通道 + 预构建 Release 产物(GitHub Release v0.0.3 里确有 pkg.tgz):

dsh plugin add https://github.com/omdsh-dev/fabric/releases/latest/download/pkg.tgz

装完后重启 web 应用,再在 profile 组合中启用 cordis-fabric / cordis-fabric-dsh

这里有一处来源不一致,需要按一手资料处理,不要混用过期命令:

  • 目录页写的是 github:omdsh-dev/fabric
  • 英文 README 写的是上面这条 pkg.tgz 地址
  • 中文 README 仍写 dsh plugin --profile web add github:dsh-external/fabric
  • package.json 里三个运行时依赖也指向 github:dsh-external/fabric#main&path:/packages/...

安装前以目录页命令和仓库当前英文 README 交叉核对;中文 README 与 dsh-external/fabric 这条路径是否仍可用,以仓库当时状态为准,不要默认它们已经合并成同一个安装入口。

Fabric 层要真正挂钩,load-time 变换必须赶在任何目标模块 import 之前。bundle 自带 fabric-dsh 启动器做这件事,宿主源码保持不动(patches/README.md 写明宿主补丁目前是空的)。profile 装好之后,可以直接跑 profile 里的 bin:

# 针对纯官方 deepseek-harness checkout
$DSH_HOME/profiles/web/node_modules/.bin/fabric-dsh \
  --harness <deepseek-harness-checkout> web --port 8000

$DSH_HOME 和 profile 名从安装路径推导。开发形态仍可用:

node /scripts/fabric-dsh.mjs --harness <deepseek-harness-checkout> --profile web

如果是从本仓库做首次准备,README 给出的入口是:

pnpm run install:host -- <deepseek-harness-checkout> --dsh-home "$HOME/.dsh_dev"

这条脚本会装宿主依赖并构建、播种 profile、走官方插件通道装 bundle,并启用 cordis-fabric-dsh 行。patches/README.md 里的示例把 --dsh-home 写成 $HOME/.dsh_dev,这是文档里的路径,不是必须用这个目录。

启动是否走了 Fabric,看 stderr:hooks 装好时会打 fabric-dsh: 标记,随后打印 hook 汇总,列出每条 patch 及其命中的目标文件。

典型用法

下面两段都来自仓库文档,不是虚构场景。

1. 用受信任插件注册一条 before patch

文档示例:在目标包 @example/target-packagelib/index.js 里,拦截同步函数 greet,把第一个参数改成大写。插件需要声明 inject = ['fabric']

import type { Context } from 'cordis'
import type { FabricCall, FabricService } from 'cordis-fabric'

export const inject = ['fabric']

export function apply(ctx: Context & { fabric: FabricService }): void {
  ctx.fabric.register({
    id: 'my-vendor/rewrite-greeting',
    target: {
      module: '@example/target-package',
      versionRange: '^1.0.0',
      filePath: 'lib/index.js',
      functionQuery: { functionName: 'greet', kind: 'Sync' },
    },
    operation: 'before',
    handler(call: FabricCall) {
      call.arguments[0] = String(call.arguments[0]).toUpperCase()
    },
  })
}

静态描述符可以写在用户 overlay(例如 $DSH_HOME/config.yaml--config 文件)的 config.fabric.patches 下,但那里只放 id / target / operation。handler 不写在 YAML 里,必须由插件在运行时通过 ctx.fabric 绑定。

2. 走协作层,而不是直接碰 Mixin

文档建议 Mod 只声明自己消费的 service。下面这个例子监听 agent 状态,并往 system prompt 里加一节:

import type { Context } from 'cordis'
import type { FabricAgentService, FabricPromptService } from 'cordis-fabric-dsh'

export const name = 'my-mod'
export const inject = ['fabricAgent', 'fabricPrompt']

export function apply(
  ctx: Context & {
    fabricAgent: FabricAgentService
    fabricPrompt: FabricPromptService
  },
): void {
  ctx.fabricAgent.onStatus((agent, status) => {
    ctx.logger.info('agent %s is %s', agent.id, status)
  })
  ctx.fabricPrompt.section({
    name: 'my-mod-identity',
    order: -80,
    text: 'my-mod is active',
  })
}

挂载 Host bundle 本身也很直接:

import * as fabricDsh from 'cordis-fabric-dsh'
import type { Context } from 'cordis'

declare const ctx: Context
await ctx.plugin(fabricDsh)

对应的 profile overlay 是把 cordis-fabric-dsh 行打开:

- id: cordis-fabric-dsh
  disabled: false

协作层公开表面不导出 AST selector、模块文件路径或原始 bridge handle。底层 ctx.fabric patch 仍是 Mixin 子系统的逃生舱,不是这一层契约的一部分。

适用场景与注意事项

适合谁:已经在用源码形态 DeepSeek Harness、需要给其他插件做加载期挂钩的插件作者。如果你只是想给智能体加一个计算器、搜索或通知,普通 DSH 插件就够了,不必上 Fabric。

使用前先看这几条边界,都来自仓库 README 和文档,不是推测。

  1. 启动器限制。 npm 安装的官方 dsh 跑不了 fabric-dsh:CLI 是预构建产物,没有可 preload 的源码入口。源码 checkout 通过 fabric-dsh 启动。README 写到,等官方仓库合入接线后,这类宿主才能用(拆分提交 65bcaf9902 已包含接线,但是否已合入上游,以当时官方仓库为准)。

  2. 运行时要求。package.json 声明 node^22.19.0 || >=24.0.0,包管理器是 pnpm@11.7.0。Node 加载期变换要求预编译 JavaScript;把原始 .ts 交给 Node load hook 会失败。浏览器路径会在应用 handler 前剥离 TypeScript。

  3. Git 依赖与 SSH。 英文 README 仍写:pnpm 通过 SSH 解析 GitHub 依赖,安装机器需要对 dsh-external/fabric 有 GitHub SSH 访问权。若你走的是 pkg.tgz,也要核对该 tarball 解包后的依赖是否还指向这个仓库。

  4. 权限与安全。 目录页和技能要求写明的那条同样适用:插件以当前 dsh 进程的权限运行,安装时可能执行代码。安装前请检查源代码仓库和许可证。变换后的代码在目标模块内拥有进程级权限。目标校验失败会在注册时抛出;格式正确但匹配不到任何文件时,模块保持未变换(静默)。required: true 的 patch 在启动完成后若从未改写过任何东西,会 loud 失败。

  5. 许可证。package.json 声明 BSD-3-Clause。GitHub 仓库元数据里 license 字段为空,根目录文件列表里也没有独立的 LICENSE 文件。以仓库当时声明为准,安装前自行核对。

  6. 不要把它当成模型可写配置。 低层 transformer 不产生模型可见内容;handler 不能从模型输入反序列化。协作层注册的 tool / prompt / command 是否对模型可见,完全取决于它委托的权威 DSH 服务。

小结

fabric 把 Minecraft Fabric 那套「加载期挂钩、不改原版」搬到了 DeepSeek Harness 上:三包拆分、默认禁用、handler 只走受信任代码。它面向的是要改其他插件行为的宿主开发者,不是日常聊天增强。装目录命令只是第一步,真正生效还要启用 profile 行,并用 fabric-dsh 而不是普通 dsh 启动。

目录页:https://deepseek-harness-plugin.com/zh-CN/plugins/fabric/

GitHub:https://github.com/omdsh-dev/fabric

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜