DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Plugin Stm32 Workbench — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
P

dsh-plugin-stm32-workbench

Plugin Stm32 Workbench

STM32F103C8T6 开发工作台:在 DSH 会话里直接编译、烧录、看串口、规划引脚。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:Airls-bubble/dsh-plugin-stm32-workbench#421f468d5718c1b9d58e0eee08b96afa7c2e87a8
README兼容性版本

兼容性与来源证明

Plugin Stm32 Workbench 以 dsh-plugin-stm32-workbench 发布,当前版本为 0.3.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/18

版本

0.3.0stable
2026/10/18

相关插件

正在加载相关插件…

最新版
0.3.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
未声明
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/18
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 developer-tools 分类下经过校验的插件。

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Sdk Minimal@deepseek-ai/dsh-sdk-minimal独立的最小 SDK 配置包:JSON-RPC、一个 DeepSeek 适配器、持久化 Shell 和 JSONL 会话Sdk App@deepseek-ai/dsh-sdk-appdsh SDK 配置包:基于 dsh-base 提供 stdio JSON-RPC 服务和进程生命周期管理Experimental Terminal Bundle@deepseek-ai/dsh-experimental-terminal-bundle在多次工具调用之间保持 shell 进程运行。

README

stm32-workbench 插件(DSH 内建单片机工作台)

把 STM32 工作台做成 DSH 插件:不另开浏览器,在 DSH 里选工作区 → 会话进入「单片机模式」→ 工作台就在会话视图里。

当前版本 0.3.0,适配 DSH 0.2.1-rc.2(2026-10-18)。mcu 工具 17 个 action(新增 symbols / readmem / gfx)。升级适配的根因与修复见文末「适配 DSH 0.2.1」。

目录中为什么不是「挂在每个对话下面」

第一版我把面板注册进了 conversation.input.dock(会话级全宽插槽),结果每个对话下面都会冒出工作台——错的。

正确做法参考 dsh-openwrite(DSH 小说创作插件,含原生工作台):

它的做法本插件的对应实现
侧栏底部一个入口按钮(sidebar.footer.action,id openwrite.launch)侧栏底部「STM32 工作台」(id stm32.launch)
点击后选作品目录 → 建工作区 → 建会话 → ctx.remote.agentPresets.select(sessionId, 自己的预设) → 打开会话同样流程,预设 id = mcu
工作台是 conversation.view,并由 watchWritingScope 门控:只读当前主视图会话的 projectionValues.agentPreset,等于自己的预设才挂载watchScope(list, isMcuSession, mountView),非 mcu 会话不挂载
预设由插件自己注册(ctx.agentPresets.register(),见其 plugin.mjs)宿主半边 ctx.inject(['agentPresets'], …) 注册「单片机模式」
已有会话不会被自动切换预设同样:只影响入口新建/选中的那个会话

关键判据(照抄自它的 writing-scope.ts):state.byId[id].projectionValues.agentPreset,会话 id 取 retainedBy.mainView > 0 的那一行(0.1 是 state.current)。

半边职责

半边文件干什么
宿主index.js① ctx.agentPresets.register() 注册「单片机模式」预设;② 拉起/停止 MCU 后端进程;③ 注册同源路由 /mcu/state、/mcu/start、/mcu/stop、/mcu/api/* 代理
客户端client.js① 侧栏入口按钮 + 选工作区对话框;② 门控的 conversation.view 工作台(引脚图/构建台/串口/计算器)
后端backend/真编译(Keil AC5)、真烧录(OpenOCD + ST-Link)、串口桥;固件源码与 BSP 随仓库提供,便携 OpenOCD 由 backend/scripts/fetch-openocd.ps1 一键下载(不随仓库分发)

入口一旦某个会话被选定,「单片机模式」下 agent 就能直接用编译/烧录/串口:端口与 routes 都在 /mcu 下。

安装与生效

  • 从 GitHub 克隆后:先准备便携 OpenOCD(仓库故意不提交这 7.6 MB 第三方二进制): powershell -ExecutionPolicy Bypass -File backend\scripts\fetch-openocd.ps1 (从官方 xPack release 下载并校验 sha256,只需一次)
  • 安装:plugin_manager: install_bundle target=<你放本插件的绝对路径>
  • 改动插件代码后必须重启 DSH:装载器不会热替换已装载的包代际(客户端资源还要刷新页面)
  • 生效后:侧栏底部出现「STM32 工作台」;模式选择器里出现「单片机模式」(由插件注册,不再依赖 profile 补丁)
  • 换台电脑怎么装、需要什么前置条件、数据落在哪:见 PORTING.md

许可证

  • 本仓库代码:MIT,见 LICENSE。
  • 第三方组件:ARM CMSIS(Apache-2.0)、STMicroelectronics STM32F10x 标准外设库(ST 许可)、 xPack OpenOCD(GPLv2,不随仓库分发,由脚本按需下载)——详见 THIRD_PARTY_NOTICES.md。

目录结构与数据落点(2026-10-14 重构)

插件本体只装代码,运行期不往自己目录里写任何东西。 一个目录拷到别的机器就是干净的一份。

stm32-workbench/              ← 约 1.8 MB(不含便携 OpenOCD;它由 fetch-openocd.ps1 按需下载)
├── package.json              ← files: 白名单;dsh.bundle / dsh.client / exports 声明(含 locale/icon)
├── cordis.patch.yml          ← backendDir 故意留空 → 取「index.js 旁边那份 backend/」
├── index.js / client.js      ← 宿主半边(mcu 工具 + preset + 路由)/ 客户端半边(工作台面板)
├── locale/{en,zh}.json       ← 设置页/插件页的显示标题与描述(DSH 0.2.1 起必需)
├── icon.svg                  ← 插件图标(原创,≤256 KiB)
├── .gitignore                ← 兜底老版本遗留
├── PORTING.md / README.md
└── backend/
    ├── server/               ← 后端模块(16 个)
    ├── scripts/              ← 诊断脚本(35 个,全相对路径)
    ├── data/                 ← pins.json / peripherals.json
    ├── tools/openocd/        ← 便携 OpenOCD(不提交进仓库;fetch-openocd.ps1 下载到此)
    ├── firmware/{bsp,projects,template}   ← 自带测试工程(只读)
    └── web/                  ← 老版独立浏览器界面(留作调试)

数据分两处,规则在 backend/server/paths.js:

<你打开的 Keil 工程目录>/.dsh-mcu/     ← 跟这个工程有关的一切,都可以安全删除
    .gitignore(内容 `*`)  build/keil/<工程>-<哈希>/  last-build.json
    logs/  src-backup/<时间戳>/  uv4-ref/  tmp/

<DSH_HOME>/mcu/                       ← 默认 ~/.dsh/mcu
    recent.json                        ← 上次打开哪个工程(唯一一处跨工程记忆)
    build/<工程>/                       ← 自带测试工程的构建输出

三条好处,都是这次重构换来的:

  1. 插件瘦了 30 多倍(61.0 MB → 1.8 MB 代码;7.6 MB 的便携 OpenOCD 改为按需下载),而且拷走不用清理;
  2. 换工作区 = 换一整套缓存,切回去直接读得到,不用重新编译;
  3. 顺手修掉一个真错误:以前构建账本是全局唯一一份,切换工程后会把旧工程的 programSize/map 拿来当新工程的占用数据展示(界面上还挂着新工程的名字)。 按工程分开存之后,这个错误在结构上不可能发生。

工程目录只读时(网络盘、受保护目录),.dsh-mcu/ 自动退到 <DSH_HOME>/mcu/projects/<路径哈希>/ 并留一张 WHY-HERE.txt。

怎么证明插件本体没被写脏:跑完一整套操作后对插件目录做文件快照比对, 应当「新增 0 / 删除 0 / 变化 0」。这是设计保证,也可以自己核对。

设计约束(创造模式随包技能给的硬规矩)

  1. 禁止 iframe:插件 UI 必须是真 React 组件、用 --dsw-alias-* 主题令牌。
  2. 禁止 import Harness 客户端包(如 dsh-client-ui-primitives):随版本变动,且抛错会让插槽整块崩掉。
  3. 资源注册要用 effect 管生命周期:ctx.slots.inject(ownerKey, () => ctx.slots.register(...)),卸载即撤销。
  4. 不写 profile 的 package.json / cordis.patch.yml:交给 plugin_manager。

踩过的坑

  1. type: "module" 毒死 CommonJS 后端 → backend/package.json 写 {"type":"commonjs"} 就近隔离。
  2. DSH 路由匹配 = 精确表优先 + 前缀取最长,所以 /mcu/state(精确)不会被 /mcu(前缀代理)吃掉。
  3. 插件资源 URL 带内容哈希 rev(/plugins/<包名>/client.js?rev=<sha1前12>),无 rev 一律 404,不能凭猜 URL 验证客户端。
  4. 重复 preset id 会让声明加载失败:所以本插件注册预设后,必须把先前手写进 profile 补丁的那段撤掉(已撤,备份 cordis.patch.yml.bak-mcu-*)。

待验证(如实记录)

  • 客户端渲染只能在 DSH 页面里验证;本会话无浏览器控制能力,不能截图或读 DOM。
  • 若侧栏没有出现入口、或工作台没起来,看浏览器控制台是否有 slot entry crashed in 'conversation.view' 或我打印的 [stm32-workbench] 警告。
  • 烧录与串口仍需 ST-Link 与实物才能验证(没插设备时界面会如实显示「未检测到 ST-Link」)。

回滚

plugin_manager: set_bundle enabled=false target=dsh-plugin-stm32-workbench

Keil 工程接入(2026-10-13)

原来只能编插件自带的 firmware/projects/<id>/,编译参数写死。现在可以直接吃用户的 Keil 工程, 产物与 Keil UV4 逐字节一致(已用真实毕设工程验证:53788 字节、0 差异)。

新增模块

文件干什么
server/uvprojx.js解析 .uvprojx:芯片 / 宏 / 头路径 / 文件清单 / 优化 / 输出设置 + 配置诊断
server/keil-build.js用工程自己的配置驱动 armcc → armasm → armlink → fromelf
server/srcfile.js编码感知的源码读写(GBK / UTF-8 / BOM 自动识别)
server/pinmap.js从源码静态解析引脚配置:跟踪 SPL 结构体状态、关联 EXTI、检出冲突
server/regread.jsSWD 直读寄存器:RCC 时钟树、外设时钟使能、GPIO 实际配置,并与源码对照

新增接口

  • POST /api/keil/open — 打开工程(进程内记住,界面刷新不丢)
  • GET /api/keil/state — 当前工程摘要
  • GET /api/keil/pins — 引脚配置(静态解析结果)
  • POST /api/keil/registers — 寄存器 + 「源码分析 vs 芯片实际」对照
  • POST /api/keil/build — 按工程编译(NDJSON 流式日志)

踩到的坑(全是实测踩出来的)

  1. Keil 的 <Optim> 是 1-based。工程写 <Optim>3</Optim> 要用 -O2 而不是 -O3。 实测 -O3 编出的 main 是 610 B,Keil 自己编的是 590 B;换 -O2 后逐字节一致。 映射:1→-O0、2→-O1、3→-O2、4→-O3。
  2. Keil 生成的 scatter 多一行 .ANY (+XO),照抄才能对齐链接结果。
  3. .uvprojx 里没有器件宏。Keil 按 <Device> 自动定义 STM32F10X_MD 之类, 外部工具链必须自己补,否则 #error "Please select first the target STM32F10x device"。
  4. <Cpu>CLOCK(...) 不是晶振频率,只是 Keil IDE 的显示值。真实值要看源码 stm32f10x.h 的 HSE_VALUE(还要区分 STM32F10X_CL 分支是否生效)。 信错了会把 8 MHz×9=72 MHz 算成 12 MHz×9=108 MHz,看起来像严重超频。
  5. 中文路径:armcc 是 ANSI 程序,命令行里出现中文会被拆坏。所以 cwd 设为工程目录、 源文件与 -I 一律用工程文件里的相对路径原文,只有 -o/--scatter 用绝对路径, 且落在纯 ASCII 的插件工作区(不污染用户工程)。
  6. Keil 工程源码常常是 GBK。按 UTF-8 读会 invalid UTF-8 或乱码,改完写回去会在 Keil 里 显示成乱码。读写都必须走 srcfile.js;写回依赖 scripts/transcode.ps1(Node 无内置 GBK 编码器)。
  7. 插件根 package.json 是 type: module。放在插件根目录的辅助 .js 脚本要么写 ESM, 要么改名 .cjs(backend/ 已用 {"type":"commonjs"} 就近隔离)。

验证方式(可复现)

拿 Keil 自己的命令行 UV4 在临时副本上做参照构建,再逐地址比对两份 hex:

仅 Keil 有 / 仅插件有 / 公共地址上不同的字节:0 / 0 / 0
✅ 内容完全一致

单文件定位差异用 scripts/keil-param-experiment.js(参数二分)、 scripts/keil-symdiff.js(符号表逐函数比大小)。

闭环补强(2026-10-14)—— P0 四项

对着「写代码 → 编译 → 烧录 → 观测 → 回归」逐环核对,结论是:编译与烧录两环有实测证据, 写代码那一环是断的。四项 P0 已补,全部有可复现的验证。

病根:同一个逻辑走了两条路

宿主半边原来只注册了路由和 preset,一个 DSH 工具都没有。于是界面走 HTTP, AI 自己跑 node scripts/*.js 或手拼 URL —— 同一个逻辑两条路,这才是「Flash 面板数字不对」 「诊断脚本比对到 D 盘旧产物还打印一致」这类事故的共同原因。

① 宿主注册 mcu 工具(index.js)

一个工具、17 个 action: status/open/scan/files/read/write/build/flash/size/pins/regs/chip/reset/spec/symbols/readmem/gfx。 AI 与界面从此落在同一套 /api 上。

2026-10-18 新增三项(对应用户《工具问题记录》第 1/3/13 条):

  • symbols:解析构建产物 .axf 的符号表(fromelf --text -s),变量/函数的地址与大小不用再靠段偏移推算;
  • readmem:读目标内存,addr 可写十六进制地址或符号名;后端 init → mdw → exit,不 halt;
  • gfx:把 SSD1306 显存解成 128×64 点阵,返回 ASCII 图并落一份标准 PBM(默认自动找 shadow 等显存符号)。

同时修了两个工具输出/写入问题:

  • write 失败时返回的 data 以前带 undefined(currentMtime),会被 DSH 判成 value is not lossless JSON、把真实错误盖掉;现在 done() 统一清洗成无损 JSON,失败原因如实返回。
  • write 现在支持新建文件:文件不存在时按 encoding 新建(默认含中文=UTF-8 BOM、纯 ASCII=UTF-8), 返回 created:true;只有修改已有文件才有备份。

注册形态照 DSH 自己的 dsh-tool-todo:不导入 @deepseek-ai/dsh-tools (插件不能依赖 Harness 的包)。defineTool 那套 DSL 编译完就是原生 JSON Schema (见其 parameterSchemaSpecToJsonSchema),所以直接写原生 JSON Schema 注册等价,且少一个版本耦合点。 注意 DSH 只接受这个子集:type/oneOf/properties/required/additionalProperties/items/enum/const, 且 required 必须是字符串数组,不是逐属性的 required: true。

index.js 因此多导出 mcuToolDefinition,好让 scripts/test-mcu-tool.js 能脱离 DSH 直接测它。

② 源码读写路由(编码感知,server.js + srcfile.js)

srcfile.js 的 writeText() 早就写好了,但零调用者 —— 没有任何路由用它, 所以工作台看不见也改不了代码,GBK 写回全靠手工跑 transcode.ps1。

新增三条路由:

路由作用
GET /api/keil/src/list列出工程源文件与各自编码(不泄露绝对路径)
GET /api/keil/src/read?path=读单个文件,自动识别 GBK/UTF-8/BOM
POST /api/keil/src/write写回(按原编码),写前自动备份,支持并发防呆

两条安全规矩:

  1. 只能碰当前打开的工程目录里的文件,用 path.relative 判包含关系挡住 ..\..\ 穿越,越界 403;
  2. 写之前把原件备份到 build/src-backup/<时间戳>/,备份路径回给调用方。 另外 expectMtime 对不上就 409 拒绝覆盖(防止读了之后被别处改过又盲写)。

③ 编译诊断结构化(新模块 server/diag.js)

以前 errors 只有 {file:'..\\User\\main.c'} —— 行号、错误码、原文全丢,界面等于什么都没说; keil-build.js:142 那句 warnings.push(f.raw) 更把警告也压成了一个文件名。

diag.js 把真实输出里的 "..\Hardware\IRSensor.c", line 97: Warning: #1-D: ... 解析成 {level, file, line, col, code, message, raw, tool},并且:

  • 认 armcc / armasm / armlink(含 L6218E 这类链接错误码)三种形态;
  • 容忍工作台落盘日志的 [build] 前缀;
  • 不把 .\x.c: 1 warning, 0 errors 这种每文件汇总行当成一条诊断;
  • 解析不出来就返回 null,绝不编造行号。

构建返回因此多了 diagnostics(按严重度分好的 warnings/errors 也在), 并且成功构建时的警告不再被丢掉 —— 毕设工程本来就有 8 条警告一直看不见。

界面(client.js)新增「编译诊断」面板:按严重度分组列出,点一条直接看那一行的源码 (走 src/read,GBK 也能正确解码,带前后 6 行上下文)。

④ 诊断脚本不再写死开发期路径(scripts/_ctx.js)

scripts/ 里那些是「为什么和 Keil 差 48 字节」那次调试的产物,当时把调试现场的绝对路径写死进了源码:

const OURS_DIR = 'D:\\AI workspace\\stm32-workbench\\backend\\firmware\\build\\keil\\Project-b04c7833';

危险的不是跑不起来,而是跑起来更像对的:插件真正在用的是 .dsh 下那份, 而 outputKey() 是「工程名 + 路径哈希」,两份目录同名 —— 脚本会去读 D 盘那份陈旧产物, 比出一个与当前代码毫无关系的「0 差异」。

现在一切路径从 _ctx.js(即脚本自己的位置)推出来:projectFile() 读 build/keil-recent.json,latestBuildDir() 取本插件最新的产物目录,keilTools() 从 toolchain.detectEnv() 取 armcc/fromelf 并反推 UV4。规矩是缺东西就中止(退出码 3), 并且 staleness() 会在产物比源码旧时直接拦下来(--allow-stale 可强制放行)。 命令行可用 --project / --build-dir / MCU_PROJECT / MCU_BUILD_DIR 覆盖。

⑤ 功能契约:让用户读人话,不读代码(server/features.js)

用户不读 C 文件。 他要知道的是「这块板子现在能干什么」—— 上电先看到什么、按了键有什么反应、串口吐什么格式、哪个功能是半成品。 这一层以前完全不存在:代码改完,用户只能靠模型在对话里随口说两句。

现在它是一个受管理的契约层:

<工程>\.dsh-mcu\features.json            模块化的自然语言功能描述(跟工程走)
<工程>\.dsh-mcu\features-history\*.json  最近 20 版,面板上可一键退回

模块形状 {id, name, summary, detail, files[], status}, status ∈ done 已实现 / partial 部分实现 / todo 还没做 / broken 有问题。

闭环是三段,缺一段这套东西就没意义:

段谁做走哪条路
模型改完代码 → 重写描述模型mcu action=spec + content={modules:[…]}
用户读描述 → 直接改描述人工作台「功能清单」页,就地编辑
改完 → 回去改代码人 → 模型面板发一条自然语言需求到当前会话(ctx.sessions)

三个不那么显然的设计决定:

  1. 过期横幅是这套机制的牙齿。staleness() 拿描述的时间戳和工程里源码的 mtime 比: 描述之后还有人改代码 → 面板顶上一条警告,并指出是哪个文件。 没有它,用户读到的可能是关于旧代码的描述 —— 那比没有描述更糟。 .dsh-mcu/ 整个目录跳过,否则每次编译的产物都会把它误判成「过期」。
  2. 发给模型的那段话由后端生成(buildMessage / buildRefreshMessage / buildDescribeMessage), 不在前端拼字符串。措辞是机制的核心(改代码 + 回写描述 + 用自然语言汇报), 只能有一份、必须可测试。
  3. 投递失败要如实失败。客户端半边用 ctx.sessions.binding(id).session.prompt(...) 发消息;拿不到会话就退化成「复制粘贴」,不假装发成功。

路由:

路由作用
GET /api/features读描述 + 过期判定 + 渲染好的 Markdown + 历史列表
POST /api/features保存(自动留历史,内容没变不新增版本)
POST /api/features/message把「用户改的描述」编译成一条发给模型的话(revise/refresh/describe)
POST /api/features/restore退回某个历史版本(恢复本身也进历史,所以「撤销撤销」可用)

features.json 坏掉时如实报 broken、不删不改 —— 用户的字不替用户做主。

怎么复现验证

node backend/scripts/test-diag.js       # 拿真 armcc 编一个故意写错的文件,验证诊断解析
node backend/scripts/test-src-api.js    # 造沙箱工程,端到端验证源码读写(含 GBK 往返、越界、并发)
node backend/scripts/test-mcu-tool.js   # 脱离 DSH 直接跑 mcu 工具的全部 action
node backend/scripts/test-features.js   # 功能契约:纯逻辑 + 沙箱端到端 + mcu action=spec 读写
node backend/scripts/keil-verify.js     # 与 Keil 产物逐字节比对(自动拒绝陈旧产物)

四个 test 脚本都在插件自己的工作区里造沙箱/落产物,不写用户的 Keil 工程; test-src-api.js 与 test-features.js 会快照并还原全局 recent.json (打开沙箱工程会覆盖「上次打开的工程」,踩过两次)。 test-mcu-tool.js 里 symbols 依赖一次成功构建,readmem/gfx/chip/reset 依赖实物 ST-Link —— 没有硬件时这四项会如实失败(脚本断言的是「如实」,不是「必须成功」)。

适配 DSH 0.2.1(2026-10-18)

DSH 从 0.2.0 升到 0.2.1-rc.2(Electron 44.7.0)后,用户报告 mcu 工具全部失败。查明是 插件自身的启动逻辑问题被新版启动时序放大,与新版宿主 API 无关(/mcu/state、工具注册、 preset 注册在新版都正常):

根因(三层,缺一不可)

  1. 健康检查选错端点:waitForHealth() 用 /api/env 探活,每次只等 1.5 s;而 /api/env 要枚举串口, 在 Windows 上会起一个 powershell.exe(实测 ~2 s)—— 每一次探测都超时,20 s 后判「后端启动超时」。
  2. 超时后不清理:健康检查失败时不 kill 子进程、也不复位 child/childPort,后端变孤儿;下一次 start() 又 spawn 一个 → EADDRINUSE 死循环。
  3. port: 0 是假的:index.js 只在 port > 0 时才把 MCU_AGENT_PORT 传给后端,0(本意=系统分配) 没生效,后端退回默认 8788 固定端口 —— 多实例/DSH 热重启必抢。

修复

位置改动
backend/server/server.js新增 GET /api/ping(不碰磁盘、不起子进程,毫秒级);LISTEN 行改为最先打印;EADDRINUSE 给人话提示
backend/server/toolchain.jsdetectEnv() 加 5 s 缓存(?fresh=1 强制刷新),/api/env 不再每次起 powershell
index.jswaitForHealth 改打 /api/ping(30 s 窗口);总是传 MCU_AGENT_PORT(0 有效);失败即清理子进程;固定端口启动前用 /api/ping 认领已在跑的旧后端(adopted:true);autoStartDelayMs 默认 1200 ms,避开 DSH 启动期忙
index.js /mcu/state多报 adopted / backendPid / 解析到的后端目录,排查时一眼看到后端是谁

新版规范性

  • 补 locale/en.json、locale/zh.json(meta.title/meta.description)与 icon.svg; exports 增加 ./locale/*.json、./icon,files 白名单补上(DSH 0.2.1 的 host-plugin.md「Display metadata and icon」清单要求,缺了会退回包名/默认图)。
  • 版本 0.2.0 → 0.3.0。
  • 改完插件代码必须重启 DSH(装载器不热替换已装载的包代际),客户端资源还要刷新页面; 只同步 local-plugins 副本不重启,运行中的仍是旧代码。

安装后自检(不开 DSH 也能跑)

# 宿主半边:假 Cordis ctx 直接加载 index.js,验证 preset/工具/路由/后端生命周期
node -e "import('./index.js').then(m=>console.log(Object.keys(m)))"   # 至少能加载
# 后端半边:直起一个隔离实例
$env:MCU_AGENT_PORT='18990'; node backend/server/server.js
# 然后 curl http://127.0.0.1:18990/api/ping

/api/ping 返回 {ok:true,name:"stm32-workbench-backend",pid,cwd,port,startedAt,uptimeMs}, cwd 用来确认后端是不是插件自己那份。