从产品名称和 Logo 开始:为 DeepSeek Harness 定制品牌

如果准备基于 DeepSeek Harness 开发自己的产品,最先需要建立的通常不是新的模型能力,而是产品自己的身份:名称和 Logo。

dsh-client-ui-brand 是一个非常简单的 DeepSeek Harness 插件,从这个插件入手 dsh plugin 开发非常合适。这个插件没有修改 DeepSeek Harness 源码,只占用 DSH 已公开的 Web UI 插槽,为侧边栏和空会话页面提供产品名称与产品图标。

能力范围

插件目前提供三处品牌替换:

  • 展开侧边栏中的产品名称;
  • 侧边栏中的产品图标;
  • 空会话页面中的产品图标。

未配置 Logo 时,插件使用内置的默认 Agent 图标。浏览器标题、favicon、PWA 元数据以及空会话 slogan 不属于当前公开的客户端插槽;前者需要定制 Web Shell,后者需要 DSH Core 后续提供对应插槽。

为什么做成插件

直接修改 DSH 的 Sidebar 或 Conversation 源码,会让后续升级与合并变得困难。DSH 的 Web 客户端已经提供了品牌插槽,因此更合适的方式是把品牌功能做成一个独立 bundle:

yaml 复制代码
- insert:
    - id: dsh-client-ui-brand
      name: dsh-client-ui-brand
      inject: [webServer]
      config:
        productName: Brand New Agent

这个 cordis.patch.yml 会在用户把包加入 Web profile 后插入插件条目。用户自己的 profile patch 可以通过同一个 id 覆盖 config,而不需要修改包内默认配置。

核心原理

这个插件有两个运行面:Host 端负责读取配置和提供本地文件;浏览器端负责注册 UI 插槽。

Host:将配置安全地交给浏览器

Host 端先归一化名称、Logo URL 与无障碍文本。logoUrllogoPath 只能选择一个;没有配置时保留默认图标。

ts 复制代码
const productName = config.productName?.trim() || 'Brand New Agent'
const logoAlt = config.logoAlt?.trim() || productName

if (config.logoUrl !== undefined && config.logoPath !== undefined) {
  throw new Error('configure only one of logoUrl or logoPath')
}

随后通过 webserver/index-inject 把浏览器可用的配置写入初始 HTML:

ts 复制代码
ctx.on('webserver/index-inject', (table) => {
  table.push({
    kind: 'global',
    name: '__NINGBO_DSH_BRAND__',
    value: boot,
  })
})

这里传给浏览器的是产品名称、logoHreflogoAlt,不会包含本地文件系统路径。

本地 Logo:固定路由而非暴露磁盘路径

当用户设置 logoPath 时,Host 端要求它是绝对路径、非符号链接的普通图片文件,并只接受 gifjpegjpgpngsvgwebp 扩展名。

验证通过后,浏览器只访问固定路由:

text 复制代码
/plugins/dsh-client-ui-brand/brand-logo

路由只允许 GETHEAD,不会接受 URL 中传入的任意文件路径。读取失败返回 404;响应带有 Cache-Control: no-storeX-Content-Type-Options: nosniff

ts 复制代码
ctx.effect(() => ctx.webServer.register({
  kind: 'exact',
  path: LOCAL_LOGO_ROUTE,
  handler: serveLocalLogo(localLogo),
}), 'dsh-client-ui-brand: local-logo route')

Client:注册三个公开品牌插槽

浏览器端从 window.__NINGBO_DSH_BRAND__ 读取 Host 注入的配置,并注册三个已公开的 DSH 插槽:

ts 复制代码
ctx.slots.inject('sidebar.brand.mark', () =>
  ctx.slots.inject('sidebar.brand.name', () =>
    ctx.slots.inject('conversation.hero.brand.mark', function* () {
      yield ctx.slots.register({ name: 'sidebar.brand.mark' }, BrandMark)
      yield ctx.slots.register({ name: 'sidebar.brand.name' }, BrandName)
      yield ctx.slots.register({ name: 'conversation.hero.brand.mark' }, BrandMark)
    })))

嵌套的 slots.inject() 让注册在对应插槽声明可用后再执行,并使三个 UI 占位在插件卸载时一起清理。

安装与使用

安装插件到 Web profile:

sh 复制代码
dsh plugin --profile web add dsh-client-ui-brand

安装完成后重启 DSH Web。默认显示 Brand New Agent 和内置 Agent 图标。

如需替换默认品牌,在当前 profile 配置文件中加入覆盖项。默认位置为:

text 复制代码
$DSH_HOME/profiles/web/cordis.patch.yml

例如使用远程 Logo:

yaml 复制代码
- id: dsh-client-ui-brand
  config:
    productName: My Agent
    logoUrl: https://example.com/my-agent-logo.svg
    logoAlt: My Agent logo

也可以使用本地图片:

yaml 复制代码
- id: dsh-client-ui-brand
  config:
    productName: My Agent
    logoPath: /absolute/path/to/my-agent-logo.svg
    logoAlt: My Agent logo

logoUrl 支持 HTTPS 地址、同源绝对路径和 data:image/... 地址;logoUrllogoPath 不能同时配置。logoAlt 未设置时会自动使用 productName

小结

产品名称和 Logo 看似是很小的改动,却是把通用 Agent Harness 变成自己产品的第一步。这个插件利用 DSH 的 bundle、Host Web Server 和客户端插槽完成品牌定制:不修改核心源码,同时保留可升级性与本地文件的安全边界。

仓库地址:

github.com/ningbonb/ds...

相关推荐
Flynt1 小时前
给Claude Code装了70行规矩,生成的代码终于不用大改了
ai编程·claude
302wanger2 小时前
大脑不是多核CPU:我和AI的“异步协作”实操
ai编程
ServBay3 小时前
AI 工程师必备的 9 个 Python 库,从数据验证到模型优化
后端·python·ai编程
JavaGuide4 小时前
万字详解Claude Code Hooks :生命周期钩子与自动化工作流
ai编程·claude
滨哥GPT5 小时前
Codex修改数据库后项目启动失败怎么办?Migration、Schema与数据结构排查
数据库·ai编程·开发工具·schema·codex
stolentime5 小时前
OpenClaw 网络数据采集新手入门指南
网络·ai·ai编程
程序员鱼皮5 小时前
3 大 DeepSeek Harness 进阶玩法,招多个大肥鱼帮我干活!
前端·后端·ai编程
Smilejudy6 小时前
Trae+SQLazy 实践 SQL 国产化移植:Oracle => 达梦
ai编程
葡萄城技术团队6 小时前
表格智能体系列 · 2:AI 怎么"看到"你的表格
ai编程