<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Dsh on Trevor's Blog</title><link>http://hitrevor.com/tags/dsh/</link><description>Recent content in Dsh on Trevor's Blog</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Wed, 04 Nov 2026 22:40:00 +0800</lastBuildDate><atom:link href="http://hitrevor.com/tags/dsh/index.xml" rel="self" type="application/rss+xml"/><item><title>dsh 0.2.2-rc.1：这次发布改了 0 行代码</title><link>http://hitrevor.com/posts/dsh-022rc/</link><pubDate>Wed, 04 Nov 2026 22:40:00 +0800</pubDate><guid>http://hitrevor.com/posts/dsh-022rc/</guid><description>拆包逐文件比 md5：8 个包 290 个文件，改动只有 8 处，全在 package.json。版本号从 alpha 走到 rc，代码一行没动，但依赖全线升级，插件得跟着抬版本。</description><content:encoded><![CDATA[<p>今天下午五点二十，DeepSeek Harness 发了 0.2.2-rc.1。</p>
<p>从 alpha 转 rc，听着像是个里程碑。我把两个版本都拆开比了一遍，结论是：一个字节都没改。</p>
<h2 id="先说结论">先说结论</h2>
<p><code>0.2.2-rc.1</code> 发布于北京时间 2026 年 11 月 4 日 17:20（<code>2026-11-04T09:20:32Z</code>）。</p>
<p>我拿它和 2026 年 11 月 3 日发布的 <code>0.2.2-alpha.3</code> 做了逐文件 md5 比对，范围是顶层包、六个核心子包、以及底层框架 <code>cordis</code>，一共 290 个文件对齐算了一遍。</p>
<p>结果：每个包里唯一变的文件，都是 <code>package.json</code>。</p>
<p><code>dist</code>、<code>src</code>、<code>locale</code>、<code>README</code> 全部一模一样。这不是「改动很小」，这是零改动。</p>
<h2 id="我怎么比的">我怎么比的</h2>
<p>命令行就三步，没用什么特殊工具：</p>
<p><code>npm pack @deepseek-ai/dsh@0.2.2-alpha.3</code> 和 <code>@0.2.2-rc.1</code> 各拉一份 tarball，解包到两个目录，然后按相对路径逐文件算 md5。</p>
<p>顶层包 20 个文件，六个子包各 30 到 60 个文件，<code>cordis</code> 32 个文件。两边同名文件全部对齐算一遍，只记下哈希不一致的。</p>
<table>
	<thead>
			<tr>
					<th>包</th>
					<th>文件数</th>
					<th>内容变化的文件</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>dsh（顶层）</td>
					<td>20</td>
					<td>1 个，package.json</td>
			</tr>
			<tr>
					<td>dsh-agent-preset</td>
					<td>48</td>
					<td>1 个，package.json</td>
			</tr>
			<tr>
					<td>dsh-plugin-manager</td>
					<td>60</td>
					<td>1 个，package.json</td>
			</tr>
			<tr>
					<td>dsh-web-app</td>
					<td>32</td>
					<td>1 个，package.json</td>
			</tr>
			<tr>
					<td>dsh-client-ui-cordis</td>
					<td>30</td>
					<td>1 个，package.json</td>
			</tr>
			<tr>
					<td>dsh-tool-fs</td>
					<td>34</td>
					<td>1 个，package.json</td>
			</tr>
			<tr>
					<td>dsh-tool-cordis</td>
					<td>34</td>
					<td>1 个，package.json</td>
			</tr>
			<tr>
					<td>cordis</td>
					<td>32</td>
					<td>1 个，package.json</td>
			</tr>
	</tbody>
</table>
<p>没有新增，没有删除。八个包八处改动，全是版本号和依赖范围那一行。</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig1.png"></p>
<h2 id="唯一真正变了的东西">唯一真正变了的东西</h2>
<p>代码没变。但依赖版本号全换了一茬，这是这次发布唯一的实质动作：</p>
<table>
	<thead>
			<tr>
					<th>包</th>
					<th>alpha.3</th>
					<th>rc.1</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>@deepseek-ai/cordis</code></td>
					<td>4.0.6-alpha.2</td>
					<td>4.1.0</td>
			</tr>
			<tr>
					<td><code>cordis-plugin-loader</code></td>
					<td>1.0.7-alpha.2</td>
					<td>1.1.0</td>
			</tr>
			<tr>
					<td><code>cordis-plugin-include</code></td>
					<td>1.0.11-alpha.2</td>
					<td>1.1.0</td>
			</tr>
			<tr>
					<td><code>cordis-plugin-timer</code></td>
					<td>1.1.8-alpha.2</td>
					<td>1.2.0</td>
			</tr>
			<tr>
					<td><code>@deepseek-ai/schemastery</code></td>
					<td>3.18.6-alpha.2</td>
					<td>3.19.0</td>
			</tr>
			<tr>
					<td><code>@deepseek-ai/cosmokit</code></td>
					<td>1.8.7-alpha.2</td>
					<td>1.9.0</td>
			</tr>
	</tbody>
</table>
<p>一句话总结：把整个 cordis 家族的 alpha 后缀去掉，抬成正式版本号。</p>
<p>但这里有个细节值得说。我把 <code>cordis</code> 4.0.6-alpha.2 和 4.1.0 也拆开比了，它的 32 个文件里同样只有 <code>package.json</code> 不同。也就是说连框架层都是原样搬过去的，只换了门牌号。这个比上面那张表更有意思。</p>
<p>那次「版本号从 alpha 变正式」，改动量是零。</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig2.png"></p>
<h2 id="所以这次发布到底图什么">所以这次发布到底图什么</h2>
<p>图一个 tag。</p>
<p>发布之前，<code>latest</code> 指向 <code>0.2.1-rc.5</code>，<code>next</code> 指向 <code>0.2.2-alpha.3</code>。想装最新的得手动写全版本号。</p>
<p>发布之后，<code>latest</code> 和 <code>next</code> 都指向 <code>0.2.2-rc.1</code>。这意味着 <code>npm i -g @deepseek-ai/dsh</code> 不加版本号，默认装的就是它。</p>
<p>对大项目来说这不是小事。alpha 线是「我们自己敢用」的标记，rc 是「你也来试试，接口已经定死了」的标记。改功能是开发，改标记是承诺。这一版做的是后者。</p>
<h2 id="升级花了不到一分钟">升级花了不到一分钟</h2>
<p>全局装一条命令，<code>npm i -g @deepseek-ai/dsh@0.2.2-rc.1</code>，跑完 <code>dsh --version</code> 就是 <code>0.2.2-rc.1</code>。</p>
<p>第二条命令不是升级，是修 profile。这条很多人会漏，我上次就漏了：</p>
<p><code>dsh plugin --profile web add @deepseek-ai/dsh-cua-bundle@0.2.2-rc.1 @deepseek-ai/dsh-experimental-plugin-market-bundle@0.2.2-rc.1 @deepseek-ai/dsh-experimental-client-ui-agent-team@0.2.2-rc.1</code></p>
<p>pnpm 报回来：解析 62 个包，复用 41 个，下载 21 个，新增 22 个，耗时 939 毫秒。</p>
<p>装完我先用 headless 模式冒烟了一次，让它「只回答两个字：收到」。它回了「收到」，说明 profile 能正常启动、模型路由通、没有插件卡在 disable 状态。这一步比看界面快。</p>
<p>为什么要重跑？因为 dsh 的 profile 是一层叠一层的插件堆，全局升了 dsh 之后，profile 里那些还钉在 alpha.3 的插件和主程序版本对不上，启动时会被版本校验直接 disable。</p>
<p>上次升 alpha.3 我只升了全局，结果六行插件被关掉，<code>storage-domain</code> 就在其中。它一挂，工作区、会话列表、定时任务跟着全卡死，网页版直接废掉，最后靠这条命令才救回来。</p>
<p>所以我的顺序固定了：先全局，再 profile，最后验证。少一步就是给自己找麻烦。</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig3.png"></p>
<h2 id="上次没救回来的那个这次回来了">上次没救回来的那个，这次回来了</h2>
<p>上次升级唯一遗留的伤是 <code>ui-agent-team</code>：它版本线特殊，npm 上找不到能和 alpha.3 对齐的包，只能一直禁用着。</p>
<p>这次它有对应版本了。装完之后 <code>profile_ping</code> 报的插件清单里，这三个都在：</p>
<p><code>@deepseek-ai/dsh-experimental-agent-team</code></p>
<p><code>@deepseek-ai/dsh-experimental-client-ui-agent-team</code></p>
<p><code>@deepseek-ai/dsh-experimental-tool-agent-team</code></p>
<p>上一版被禁的 <code>dsh-storage-domain</code> 也在列表里。插件页面上，「Agent teams」那个开关是打开的，新会话的标题栏上还多了一个 Agent team 的标识。</p>
<p>装的时候出了一句提示，我觉得值得原样记下来：<code>@deepseek-ai/dsh-experimental-client-ui-agent-team</code> 没有声明 <code>dsh.bundle</code>，所以它是作为普通依赖装的，不是一个 profile 层。</p>
<p>换句话说它能用，但它在体系里的位置和那些 bundle 不一样。这种话厂商不会写在发布说明里，只在安装输出里闪一句。</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig4.png"></p>
<h2 id="profile-里不全是官方包">profile 里不全是官方包</h2>
<p>顺手看了一眼 profile 的依赖清单，六条：</p>
<p>三个 bundle 现在是 rc.1，<code>agent-team-web-profile</code> 还停在 <code>0.1.6-alpha.2</code>，剩下两条是我自己装的，一个从本地 tgz 装，一个直接从 GitHub 仓库装。</p>
<p>这说明 dsh 的 profile 不是一个封闭的官方套装，是你自己拼的组合。好处是你想加什么加什么，代价是升级时得自己盯：哪些是官方的、哪些钉在某个版本、哪些根本不在官方的版本线上。</p>
<p><code>agent-team-web-profile</code> 就是例子。它是 agent-team 的界面层，版本线和主程序完全独立，主程序到 rc.1 了它还在 alpha.2。这种包不会跟着你升级，它自己走自己的。</p>
<h2 id="顺手验了一遍">顺手验了一遍</h2>
<p>升级完之后我在网页版开了一个新会话，只发一句：调用 profile_ping，把返回的 JSON 原样贴出来。</p>
<p>十秒完成，返回的开头是 <code>&quot;profile&quot;: &quot;web&quot;, &quot;dshVersion&quot;: &quot;0.2.2-rc.1&quot;, &quot;plugins&quot;: [</code> 后面跟着 212 个插件名。</p>
<p>这 212 行就是我这个 profile 现在的真实构成，从 <code>cordis-plugin-timer</code> 一直到我自己写的 <code>dsh-plugin-profile-ping</code>。想确认升级到底有没有生效，看这一个数字比看任何界面都准。</p>
<p>顺带一提，这个查询本身花了 56.3K token，缓存命中 86%，上下文只用了 3%。开销主要在插件清单太长，不在推理。</p>
<p><img loading="lazy" src="/images/dsh-022rc/fig5.png"></p>
<h2 id="一个查版本的经验">一个查版本的经验</h2>
<p>如果你也想追 dsh 的版本，有个坑得避开：子包的 <code>latest</code> 标签是滞后的。</p>
<p>比如 <code>dsh-experimental-client-ui-agent-team</code>，它的 <code>latest</code> 停在 <code>0.1.5-alpha.2</code>，<code>alpha</code> 停在 <code>0.2.2-alpha.3</code>，真正的新版本在 <code>next</code> 里。</p>
<p><code>npm view &lt;包名&gt; dist-tags</code> 三个标签一起看，别只看 <code>latest</code>。主包现在 <code>latest</code> 是对的，子包不是。</p>
<h2 id="我的判断">我的判断</h2>
<p>这类版本值不值得跟？我的答案是跟，但不用急。</p>
<p>代码零变化意味着风险接近零，升不升都不会坏你的环境。真正的收益不在 dsh 本身，而在那些跟着转正的插件：<code>ui-agent-team</code> 这种跨版本线的包，只有在整条线一起转 rc 的时候才会被放出来。</p>
<p>反过来说，如果你手上有个跑得好好的 alpha.3，没有任何插件被禁，那这次不用动。等 0.2.2 正式版出来再一次性跟上，中间这几天不会有新功能给你。</p>
<p>你升级的时候会先看发布说明，还是直接拆包比一遍？</p>
]]></content:encoded></item><item><title>让 dsh 自己给自己写插件，它花了九分钟</title><link>http://hitrevor.com/posts/dsh-creator/</link><pubDate>Tue, 03 Nov 2026 19:40:00 +0800</pubDate><guid>http://hitrevor.com/posts/dsh-creator/</guid><description>我让 dsh 的创造模式写一个插件，它查运行时、读源码、写包、申请安装，九分钟后工具真的出现在插件管理页里，新会话十秒调通。</description><content:encoded><![CDATA[<p>今天热点里那条「DeepSeek 上线创作者模式」，英文媒体写得很玄，说用聊天就能造插件。</p>
<p>我本机天天在用 dsh，跑的是 2026 年 10 月 28 日发的 0.2.2-alpha.2，看到这条的第一反应不是转述，是试一遍。</p>
<p>于是我在自己的 web profile 里开了一个 Creator mode 的会话。给它的需求只有一句话：写一个最小的插件，注册一个叫 profile_ping 的工具，返回当前 profile 名、dsh 版本号、以及这个 profile 里装了哪些插件。</p>
<p>九分钟之后，这个工具躺在插件管理页里，我新开一个会话调它，十秒拿到了结果。</p>
<h2 id="dsh-一共有四种模式">dsh 一共有四种模式</h2>
<p>打开 web 界面，输入框上面有个模式选择器，点开是四条：</p>
<ul>
<li>Standard，写代码、查资料、跑命令，适合大多数任务</li>
<li>PTC，包住 Standard 全部能力，额外适合「批量调工具再筛、去重、统计」这类活</li>
<li>Minimal，只给终端和读图，用来测基础能力</li>
<li>Creator，官方原话是「通过对话定制 dsh，让 agent 写插件加功能或界面，或者把工具和提示词组合成你自己的模式」</li>
</ul>
<p>我这次点的是第四条。</p>
<p><img alt="四种模式，第四种是让它改自己" loading="lazy" src="/images/dsh-creator/fig1.png"></p>
<h2 id="它没有一上来就写代码">它没有一上来就写代码</h2>
<p>这点是我最意外的。</p>
<p>接到需求之后，它先载入了一个叫 cordis-plugin-development 的技能包，然后开始查运行时。</p>
<p>界面右侧的 Trajectory 里能看到它一连串动作。先 inspect providers 看有哪些服务，再 query runtime 问 Service.listService 的方法签名，一遍遍查 pluginManager、profileContext、defineTool 的确切契约。</p>
<p>它甚至去翻了 @deepseek-ai/dsh-app-boot 的源码，来判断「插件里能不能 import 这个包」。它要拿 dsh 版本号，而版本号只能从安装目录的 package.json 里读。</p>
<p>这一步的判断过程我觉得比结果更值钱：它先确认「profile 里装的插件解析不到安装目录的包」，然后改用运行时已经提供的 profileContext，从 installAnchor 那个路径自己去读。</p>
<h2 id="写出来的东西是这个样子">写出来的东西是这个样子</h2>
<p>生成物在会话的工作区里，是个标准的 bundle 包：清单、补丁、入口、两份语言文件、一枚图标，外加一份 README。</p>
<p>package.json 里那句 <code>&quot;dsh&quot;: { &quot;bundle&quot;: { &quot;patch&quot;: &quot;./cordis.patch.yml&quot; } }</code>，就是它跟 dsh 打招呼的方式。补丁文件只有三行，插入一条 id 叫 profile-ping 的行。</p>
<p>index.js 六点九 KB，自己注册工具、定义参数和输出 schema、写 render。中文名字「Profile 探针」是它自己起的。</p>
<p><img alt="它生成的不是一个文件，是一个能装进 profile 的包" loading="lazy" src="/images/dsh-creator/fig3.png"></p>
<h2 id="中间我拒了它一次">中间我拒了它一次</h2>
<p>安装那一步它调的是 plugin_manager 的 install_bundle，界面上弹出一个授权框，写明「这会把改动写进 profile，装进去的 Host 代码在沙箱外面跑」。</p>
<p>我点了 Allow once。</p>
<p>后来它想把打包好的 tgz 从 /tmp 复制到 Documents 下做长期保存，又申请一次授权。这次我点了 Reject，因为这是我准备做完实验就清理的临时东西。</p>
<p>它的反应值得记一笔：它停下了，并且明确说「用户拒绝了这次提权，所以我不该绕开，解释清楚就行」。</p>
<p>这条规矩写在它的技能包里，不是模型的即兴发挥。原文是「不要手写 profile 的 package.json，不要自己在 profile 目录里跑 pnpm，这些事由 install_bundle 做」。</p>
<p><img alt="它自己走完的八十三个步骤" loading="lazy" src="/images/dsh-creator/fig2.png"></p>
<h2 id="跑通了">跑通了</h2>
<p>新开一个会话，我说「直接调用 profile_ping，把返回原文贴出来」。</p>
<p>十秒钟，它贴出来：</p>
<p>返回就三行开头：<code>Profile: web</code>、<code>dsh version: 0.2.2-alpha.2</code>、<code>Plugins (209):</code>。后面跟着两百零九个包名，排在末尾的那个，正是它自己写的。</p>
<p>插件管理页的「已安装」里也多了一行：Profile ping，带图标、带描述、开关是开的。</p>
<p><img alt="左边是调用结果，右边是它出现在插件管理页" loading="lazy" src="/images/dsh-creator/fig4.png"></p>
<h2 id="它的技能包里把规矩写死了">它的技能包里把规矩写死了</h2>
<p>跑完我才去读它载入的那份技能文档，读完明白它为什么这么走。文档里写道，这套流程分五步，第一步就是「先交付能用的版本」。</p>
<p>文档开头就写：先交一个能跑的版本，再谈好不好看。视觉需求如果没说落在哪，默认就是当前这个 Web 界面；不要先做预览页、别做几套设计稿、别写截图脚本。</p>
<p>然后是三条禁止：不要手写 profile 的 package.json，不要在 profile 目录里跑 pnpm，不要在 $DSH_HOME 下面造包。这些活统一交给插件管理器的 install_bundle，手工写一次就要一次审批。</p>
<p>还有一条验证规矩：装完看返回里的 application 字段和 warnings，别看日志、别看进程列表、别看页面是不是起了。</p>
<p>我这次全程没干预它的技术判断，唯一做的就是两次授权点击，一次同意一次拒绝。它在这套规矩里自己走完了。</p>
<h2 id="三个坑我得说清楚">三个坑，我得说清楚</h2>
<p>第一个，它创建的工作区目录名带了反斜杠。dsh 在 macOS 上把路径拼成了 <code>deepseek-harness\creator-lab</code>，那个反斜杠是真的字符，不是转义。结果 ESM 加载这种路径直接报错，插件根本 import 不进来。它自己发现的，改用打包成 tgz 安装绕过去了。</p>
<p>第二个，装完当回合调不到工具。工具列表是这个回合开始时就定死的，新注册的工具下一轮才出现。它在 Trajectory 里连着打转了好几次，自己在思考里写「我可能只能发出静态列表里的工具」，我另开一个会话，才验证成功。</p>
<p>第三个，安装记录里写的是 <code>file:/tmp/dsh-plugin-build/xxx.tgz</code>。也就是说，清了临时目录，这个插件下次就装不上。它想修的正是这一点，被我拒了。真要长期用，得把包放到工作区里，用 install_bundle 指目录安装。</p>
<h2 id="它现在能造什么造不出什么">它现在能造什么，造不出什么</h2>
<p>能造的：工具类插件，往 agent 的工具箱里加一个能力，我这次的 profile_ping 就是；配置类插件，改宿主某个服务的行为；界面类插件，往 Web 界面的插槽里挂一块。技能包里这三类各有模板和参考文档。</p>
<p>造不出的：需要新宿主服务的插件，它自己文档里写着这个插件不是内核，不加服务、不加会话事件类型、不加沙箱后端；还有跨 profile 的改动，一次只能动当前这个 profile。</p>
<p>一个更实际的限制是 token。这次九分钟的活，烧掉 890 万 token，缓存命中 99% 才压住了成本。你要让它干大活，得先想清楚值不值。</p>
<h2 id="我的判断">我的判断</h2>
<p>「用聊天造插件」这个说法容易让人以为它是个生成器：你描述，它吐代码。</p>
<p>不是。它更像一个懂这套框架的工程师：先查运行时有哪些能力，再读源码确认约束，然后照技能包里的流程施工，装的时候申请授权，装完自己验一遍。</p>
<p>真正稀缺的是那套流程本身。技能包里把「先交一个能跑的版本，再谈好看」「视觉需求默认落在当前界面」「安装结果看 application 字段而不是日志」这些规矩都写死了。模型会换，这套规矩不换。</p>
<p>一个能力边界放这儿：它九分钟里烧了 890 万 token，缓存命中 99%。这不是零成本的魔法，是一次真刀真枪的施工。</p>
<p>你手上有哪个工具，是每次都要手动翻一遍配置才知道状态的？那种东西，值得让它给你写一个插件。</p>
]]></content:encoded></item></channel></rss>