从产品名称和 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 与无障碍文本。logoUrl 和 logoPath 只能选择一个;没有配置时保留默认图标。
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,
})
})
这里传给浏览器的是产品名称、logoHref 和 logoAlt,不会包含本地文件系统路径。
本地 Logo:固定路由而非暴露磁盘路径
当用户设置 logoPath 时,Host 端要求它是绝对路径、非符号链接的普通图片文件,并只接受 gif、jpeg、jpg、png、svg、webp 扩展名。
验证通过后,浏览器只访问固定路由:
text
/plugins/dsh-client-ui-brand/brand-logo
路由只允许 GET 和 HEAD,不会接受 URL 中传入的任意文件路径。读取失败返回 404;响应带有 Cache-Control: no-store 和 X-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/... 地址;logoUrl 与 logoPath 不能同时配置。logoAlt 未设置时会自动使用 productName。
小结
产品名称和 Logo 看似是很小的改动,却是把通用 Agent Harness 变成自己产品的第一步。这个插件利用 DSH 的 bundle、Host Web Server 和客户端插槽完成品牌定制:不修改核心源码,同时保留可升级性与本地文件的安全边界。
仓库地址: