All plugins

DSH / BUNDLE / CLIENT-UI

dsh-image-tool

v0.2.0lance808 / dsh-image-toolc7d11767a9

InstallableBundlesUI & client pluginsCommunity · Topic auto-analysisWeb UI

Overview

dsh-image-tool

DeepSeek Harness 生图与改图工具插件:把任意 OpenAI 兼容的生图/改图服务接成平台工具,模型在对话里直接生成或修改图片,图片以平台原生 image 块输出并显示在工具卡里(缩略图 / 全屏 / 下载 / 在工作区打开)。生图 API 与对话模型 API 完全独立,可配置调用前确认。

README / EN

Package documentation

Registry summary

DeepSeek Harness 生图与改图工具插件:把任意 OpenAI 兼容的生图/改图服务接成平台工具,模型在对话里直接生成或修改图片,图片以平台原生 image 块输出并显示在工具卡里(缩略图 / 全屏 / 下载 / 在工作区打开)。生图 API 与对话模型 API 完全独立,可配置调用前确认。

dsh.pub verifies the pinned bundle contract, runtime facts, and distribution semantics. The complete README remains in the source repository.

Read the full README on GitHub

LIMITATIONS

Known limitations

- **可选服务必须用 `ctx.inject`,不能直接 `ctx.get('x')` / `ctx.x`。** 这是本项目踩过的最贵的坑:访问**未在 `inject` 里声明**的服务会抛 `cannot get property "webServer" without inject`,而这行如果在 `apply` 里, 整个插件的 entry 就直接变成「启动失败」—— 使用者只看到插件卡上那一个词, 看不到任何原因。只有 Web profile 才有 `webServer`,所以它天然是**可选依赖**: ```js // ❌ 抛错:webServer 不在 inject 里 const webServer = ctx.get('webServer') ?? ctx.webServer // ✅ 可选依赖的正确写法 ctx.inject(['connection', 'webServer'], routeCtx => { const ws = routeCtx.get('webServer') }) ``` `inject = [...]`(模块导出)是**硬依赖**:缺了插件就不激活。 `ctx.inject([...], cb)` 才是"可用时才执行"。另外 `apply` 里每一步都独立 try/catch (见 `lib/index.js` 的 `step()`)—— 工具挂不上不该拖垮路由,反之亦然。 - **改代码要重启 DSH;改配置不用。** 这两条是真的不一样: - **配置**走 `settings.describe()`,是每次调用现读的运行时值(`applies: 'live'`),写入即生效; - **代码**是模块,Node 会缓存已加载的 ESM(浏览器半同理,由外壳的模块加载器持有)。 只改配置文件不会重新加载 `lib/*.js` 或 `client.js`。 - 另外每次 volatile 更新都会**重新执行 `apply`**,所以诊断日志里会看到多次 `apply:` —— 这是平台的正常行为,不是重复装载(loader entry 只有一条)。 ⚠️ 正因如此,工具与路由的注册**必须经 `ctx.effect`**:同一层里重名注册会直接失败, 症状是插件卡显示「启动失败」,而且**第一次是好的、动过配置之后才坏**。 - **浏览器半依赖外壳的槽位与主题契约**(`settings.section`、`tool.call.toolview`、 `--dsw-alias-*`、宿主提供 `react`)。这些是平台内部约定,跨大版本升级时可能变; `npm run test:client` 会在加载层先把最典型的几种破裂挡下来。 全屏浮层还需要 `react-dom` 的 `createPortal`:拿不到时**降级**成就地 `fixed` 渲染 (功能可用,但浮层可能被祖先容器的 `transform` / `overflow` 裁切),不会让卡片崩掉 —— 卡片渲染抛错会被宿主永久弃用,代价远大于少一个浮层。 - **确认通道依赖 `userQuestions`**:没有它的 composition 里,开启确认会**拒绝生图**并说明原因 (fail-closed:宁可画不出来,也不要在"说了要问却没问"的情况下花钱)。 - **URL 过滤不是完整的 DNS 出站隔离**:当前检查 URL 主机名与 IP 字面量, 不做 DNS 解析校验;不可信上游的域名解析仍需由部署环境的网络策略限制。 - **`/image` 引用索引不是持久存储**:配置更新导致重新挂载或进程重启后, 旧 `fetchUrl` 会返回 404。长期保存请用 `includeData=1`。 - **另存的是附件里的版本**:图片附件可能经过平台规范化(缩放/重编码), 自动落盘保存的才是上游原图。 - **改图的原图只能来自会话或工作目录**:不接受远程 URL(那等于让模型指挥服务端去请求 任意地址)。要改远程图,先把它下载进工作目录。 工作目录内的路径会做**词法 + `realpath` 双重校验**,符号链接/junction 指向盘外时会被拒绝。 - **改图的 `mask` 只做软校验**:OpenAI 约定 mask 必须是 PNG、与原图同尺寸、透明处为编辑区; 本插件在**不是 PNG 时会给一句提示,但不会替你拦下** —— 不同中继的接受度差别很大, 硬拦反而挡住了能用的路径。另外**原图与 mask 的尺寸一致性由上游判定**, 这里不做像素级比对(那需要引入图像库)。 - **`/edit` 的 HTTP 调用必须自带 `cwd`**:会话工作目录在 HTTP 路径上并不存在, 而"哪些路径允许读"必须有一个边界,插件不会拿进程当前目录去猜。 - **`--set` 的值里不能有前导 `--`**:参数解析把 `--` 开头当作下一个 flag。 值里真需要时用 `--set key=--value` 形式(带 `=` 的那种解析路径不受影响)。 ---