{"_id":"@dopilot/dsh-plugin-image-gen","_rev":"2-98f1f83c7abaf8768a2d5573f5e19e61","name":"@dopilot/dsh-plugin-image-gen","dist-tags":{"bootstrap":"0.1.0-bootstrap.0","latest":"0.1.0"},"versions":{"0.1.0-bootstrap.0":{"name":"@dopilot/dsh-plugin-image-gen","version":"0.1.0-bootstrap.0","author":{"name":"Tony Huang"},"license":"MIT","_id":"@dopilot/dsh-plugin-image-gen@0.1.0-bootstrap.0","maintainers":[{"name":"tonyhuang2026","email":"zeemhome21@gmail.com"}],"homepage":"https://dopilot.ai","bugs":{"url":"https://github.com/tuofangzhe/dsh-plugin-image-gen/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"}},"dist":{"shasum":"45a12f683b7dc4d7d8f543a8e2f0a70c5ff61cda","tarball":"https://registry.npmjs.org/@dopilot/dsh-plugin-image-gen/-/dsh-plugin-image-gen-0.1.0-bootstrap.0.tgz","fileCount":11,"integrity":"sha512-LHgJfhntAsyrinCK2EBa0ZwXrgsTZlLxwhM9ro8hEZ54Se3pw0WnsV3h8sjsaQ1fETwqg59kMNaaZr6DBgjTtw==","signatures":[{"sig":"MEQCID1dk1dlz3mmYPmnNr9ZpXUpFinY6TCbHxi6sOeL77PaAiAHZRgTIQ41OUv9UJZumDTw1JVdAhhXujee0DL/X9XCVQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75872},"main":"lib/index.js","type":"module","types":"lib/index.d.ts","engines":{"node":"^22.19.0 || >=24"},"exports":{".":{"types":"./lib/index.d.ts","default":"./lib/index.js"}},"scripts":{"test":"node --import tsx --test test/*.test.ts","build":"tsc -p tsconfig.build.json","typecheck":"tsc -p tsconfig.json --noEmit","pack:verify":"node scripts/verify-pack.mjs"},"_npmUser":{"name":"tonyhuang2026","email":"zeemhome21@gmail.com"},"repository":{"url":"git+https://github.com/tuofangzhe/dsh-plugin-image-gen.git","type":"git"},"_npmVersion":"11.11.0","description":"OpenAI-compatible image generation tool for DeepSeek Harness","directories":{},"_nodeVersion":"25.8.0","dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"4.22.4","typescript":"5.9.3","@types/node":"22.19.19","@deepseek-ai/cordis":"4.0.1","@deepseek-ai/dsh-llm":"0.1.1-rc.2","@deepseek-ai/dsh-tools":"0.1.1-rc.2","@deepseek-ai/dsh-attachment":"0.1.1-rc.2","@deepseek-ai/dsh-credentials":"0.1.1-rc.2","@deepseek-ai/cordis-plugin-loader":"1.0.2","@deepseek-ai/cordis-plugin-include":"1.0.6"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-llm":"0.1.1-rc.2","@deepseek-ai/dsh-tools":"0.1.1-rc.2","@deepseek-ai/dsh-attachment":"0.1.1-rc.2","@deepseek-ai/dsh-credentials":"0.1.1-rc.2"},"_npmOperationalInternal":{"tmp":"tmp/dsh-plugin-image-gen_0.1.0-bootstrap.0_1787826939984_0.2226083051560126","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@dopilot/dsh-plugin-image-gen","version":"0.1.0","description":"OpenAI-compatible image generation tool for DeepSeek Harness","author":{"name":"Tony Huang"},"homepage":"https://dopilot.ai","repository":{"type":"git","url":"git+https://github.com/tuofangzhe/dsh-plugin-image-gen.git"},"bugs":{"url":"https://github.com/tuofangzhe/dsh-plugin-image-gen/issues"},"type":"module","main":"lib/index.js","types":"lib/index.d.ts","exports":{".":{"types":"./lib/index.d.ts","default":"./lib/index.js"}},"scripts":{"build":"tsc -p tsconfig.build.json","pack:verify":"node scripts/verify-pack.mjs","test":"node --import tsx --test test/*.test.ts","typecheck":"tsc -p tsconfig.json --noEmit"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-attachment":"0.1.1-rc.2","@deepseek-ai/dsh-credentials":"0.1.1-rc.2","@deepseek-ai/dsh-llm":"0.1.1-rc.2","@deepseek-ai/dsh-tools":"0.1.1-rc.2"},"dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"devDependencies":{"@deepseek-ai/cordis":"4.0.1","@deepseek-ai/cordis-plugin-include":"1.0.6","@deepseek-ai/cordis-plugin-loader":"1.0.2","@deepseek-ai/dsh-attachment":"0.1.1-rc.2","@deepseek-ai/dsh-credentials":"0.1.1-rc.2","@deepseek-ai/dsh-llm":"0.1.1-rc.2","@deepseek-ai/dsh-tools":"0.1.1-rc.2","@types/node":"22.19.19","tsx":"4.22.4","typescript":"5.9.3"},"engines":{"node":"^22.19.0 || >=24"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"}},"license":"MIT","gitHead":"3d90bdc7204ee65141e24bfd48c2dc7f9b92811d","_id":"@dopilot/dsh-plugin-image-gen@0.1.0","_nodeVersion":"22.19.0","_npmVersion":"11.19.1","dist":{"integrity":"sha512-EQnxFloKkqLAyMYWNvcEBRan3OeH6uWNKu6Bd4yysGVxXHE5ZsJmMgnSvJpxTOnrRIOKJ64LnIAzoC0IxRRiXg==","shasum":"c9fd6988c52ea6120557dedbbb94eccdbc92d503","tarball":"https://registry.npmjs.org/@dopilot/dsh-plugin-image-gen/-/dsh-plugin-image-gen-0.1.0.tgz","fileCount":11,"unpackedSize":75860,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dopilot%2fdsh-plugin-image-gen@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCuFqqNyL1TWwUb2FIWmHZ50zIFyU75KCPrF5x9CMilpAIgSEdcJiifXIA+VzhkiU3PYnIQp62Cx2HAWjrnVpYm/AQ="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a0a76fea-f5bb-48c6-aa1b-59c9abc30b78"}},"directories":{},"maintainers":[{"name":"tonyhuang2026","email":"zeemhome21@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-plugin-image-gen_0.1.0_1787827241105_0.9333207920254187"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-27T10:35:39.783Z","modified":"2026-08-27T10:40:41.614Z","0.1.0-bootstrap.0":"2026-08-27T10:35:40.131Z","0.1.0":"2026-08-27T10:40:41.236Z"},"bugs":{"url":"https://github.com/tuofangzhe/dsh-plugin-image-gen/issues"},"author":{"name":"Tony Huang"},"license":"MIT","homepage":"https://dopilot.ai","repository":{"type":"git","url":"git+https://github.com/tuofangzhe/dsh-plugin-image-gen.git"},"description":"OpenAI-compatible image generation tool for DeepSeek Harness","maintainers":[{"name":"tonyhuang2026","email":"zeemhome21@gmail.com"}],"readme":"# `@dopilot/dsh-plugin-image-gen`\n\n这是一个 DeepSeek Harness 插件，用于提供兼容 OpenAI 接口的 `generate_image` 工具。插件会同步请求一次 `POST <baseURL>/images/generations`，把接口返回的每张图片写入当前会话的工作区；附件服务可用时，还会保存附件引用，使支持图片输入的当前对话模型能够看到生成结果。\n\n本包仅支持 ESM，要求 Node.js 22.19 或更高版本。包内有意不提供 default export；Cordis 插件入口均为 named export，分别是 `name`、`inject`、`Config` 和 `apply`。\n\n## 安装\n\n把公开 bundle 安装到要使用的 profile：\n\n```sh\ndsh plugin --profile web add @dopilot/dsh-plugin-image-gen\n```\n\n安装后，该 profile 会出现一条休眠的 `dopilot-image-gen` 配置行。请在 profile 自己的 `cordis.patch.yml` 中按相同 id 覆盖；`config` 会整项替换而不是深合并，因此必须重述需要的全部字段。使用 `dsh plugin --profile web remove @dopilot/dsh-plugin-image-gen` 可同时移除依赖和 bundle 层。\n\n源码开发请依次运行 `npm ci`、`npm run typecheck`、`npm test` 和 `npm run build`。DSH rc.2 子包使用预发布版本发布，而 npm 的 `latest` 标签仍可能指向更老的构建。本仓库已固定测试用开发版本，并将四个 DSH peer 精确限定为 `0.1.1-rc.2`。\n\n## 兼容性\n\n| 插件版本 | 支持的 DSH 版本 |\n|---|---|\n| `0.1.x` | `0.1.1-rc.2` |\n\n## 配置\n\n在目标 profile 的 `cordis.patch.yml` 中按 id 覆盖 bundle 行：\n\n```yaml\n- id: dopilot-image-gen\n  config:\n    providers:\n      openai:\n        baseURL: https://api.openai.com/v1\n        apiKeyEnv: OPENAI_IMAGE_API_KEY\n        defaultModel: gpt-image-1\n        models: [gpt-image-1]\n        responseFormat: png\n        timeoutMs: 120000\n      gateway:\n        baseURL: https://images.example.com/openai/v1\n        apiKeyEnv: CUSTOM_IMAGE_API_KEY\n        defaultModel: organization/image-model\n        responseFormat: webp\n        timeoutMs: 180000\n    defaultProvider: openai\n    maxImagesPerCall: 4\n    outputDir: generated-images\n    maxResponseBytes: 134217728\n    maxImageBytes: 20971520\n    maxTotalImageBytes: 83886080\n    maxErrorChars: 500\n    toolTimeoutGraceMs: 30000\n```\n\n`apiKeyEnv` 是凭据引用名，不是密钥值。插件会在每次调用时通过 DSH credentials 服务重新解析该引用，并且不会缓存密钥。对于不需要 Authorization 请求头的网关，可以省略此字段。\n\n| 字段 | 含义与默认值 |\n|---|---|\n| `providers` | 可选的 provider 路由表；缺省或为空时插件休眠且不注册工具。 |\n| `providers.*.baseURL` | 不含用户名、密码、查询参数或 URL fragment 的绝对 HTTP(S) URL；末尾 `/` 会被移除。 |\n| `providers.*.apiKeyEnv` | 可选的凭据引用，必须符合 POSIX shell identifier 语法。 |\n| `providers.*.defaultModel` | 必填且不能为空的默认图片模型。 |\n| `providers.*.models` | 可选的模型白名单；省略或传空数组表示不限制。 |\n| `providers.*.responseFormat` | 接口实际返回的编码：`png`（默认）、`jpeg` 或 `webp`。插件不会转换图片格式。 |\n| `providers.*.timeoutMs` | provider 请求超时时间，默认 `120000` 毫秒。 |\n| `defaultProvider` | 配置多个 provider 时必填；只有一个 provider 时自动推断。 |\n| `maxImagesPerCall` | 每次调用允许生成的图片数，范围 `1`–`10`，默认 `4`。 |\n| `outputDir` | 安全、规范化的工作区相对路径，默认 `generated-images`。 |\n| `maxResponseBytes` | 流式 HTTP 响应体上限，默认 128 MiB。 |\n| `maxImageBytes` | 单张解码图片的上限，默认 20 MiB。 |\n| `maxTotalImageBytes` | 单次调用所有解码图片的总上限，默认 80 MiB。 |\n| `maxErrorChars` | provider 或附件错误摘要长度，范围 64–4000 个 Unicode code point，默认 500。 |\n| `toolTimeoutGraceMs` | 工具协作式 deadline 在最慢 provider 超时基础上的余量，默认 30000 毫秒。 |\n\n`providers` 缺省或为空是有意设计的休眠状态：DSH 正常启动，插件记录配置提示，但不注册 `generate_image`。一旦 `providers` 非空，非法字段仍会在加载阶段响亮失败。最慢 provider 的超时时间与工具余量之和不能超过 `2_147_483_647` 毫秒。字节上限必须满足 `maxImageBytes <= maxTotalImageBytes <= maxResponseBytes`。\n\n## 工具契约\n\n`generate_image` 接受以下参数：\n\n- `prompt`（必填字符串）：图片生成描述。\n- `output_paths`（必填字符串数组）：每张图片对应一个输出路径；数组长度就是 API 请求中的 `n`。\n- `provider`、`model` 和 `size`（可选字符串）：用于覆盖 provider、模型和图片尺寸；`size` 必须匹配 `\\d{3,4}x\\d{3,4}`。\n\n每个路径必须预先使用 `/` 规范化、彼此唯一，并且只能指向配置项 `outputDir` 的直接子文件。文件名只能使用 ASCII 字母、数字、`_` 或 `-`，扩展名必须根据 provider 的 `responseFormat` 使用 `.png`、`.jpg`/`.jpeg` 或 `.webp`。绝对路径、反斜杠、`.`/`..` 段、任意嵌套目录、Windows 设备名和已存在的目标文件都会被拒绝。插件以排他的 `wx` 语义创建文件，绝不覆盖现有文件。\n\n模型调用示例：\n\n```json\n{\n  \"prompt\": \"A red circle centered on a clean white background\",\n  \"provider\": \"openai\",\n  \"size\": \"1024x1024\",\n  \"output_paths\": [\"generated-images/red-circle-1.png\"]\n}\n```\n\n成功调用的 canonical value 会记录 provider、模型、磁盘原图的编码/字节数/尺寸，以及附件服务返回的附件事实。由于附件服务可能规范化保存的副本，因此附件元数据与磁盘事实是两组独立数据。文本结果始终描述磁盘原图及其路径。只有当前对话路由明确声明支持图片输入，并且存在完整附件引用时，结果中才会加入 image block。\n\n## 本地冒烟测试\n\n从源码 checkout 运行：\n\n```sh\ncd /path/to/dsh-plugin-image-gen\nnpm ci\nnpm run build\ncd ~/dsh/deepseek-harness-master\npnpm dsh plugin --profile web add /path/to/dsh-plugin-image-gen\nexport OPENAI_IMAGE_API_KEY='<real key>'\nexport IMAGE_API_MODEL='gpt-image-1'\npnpm dsh web --patch /path/to/dsh-plugin-image-gen/test/smoke.overlay.yml\n```\n\n如果使用已经安装的 DSH，请按相同顺序执行，但把 `pnpm dsh` 替换为 `dsh`。必须执行 `plugin ... add`；仅传入 `--patch` 既不会安装包，也不会激活 bundle。安装过程不应出现 “bundle-less dependency” 告警。配置 provider 前，profile 仍应正常启动，但插件保持休眠且不存在 `generate_image` schema。\n\n进入 Web UI 后，选择一个本地工作区并创建会话，然后输入：\n\n> Generate one image of a red circle on white background. Save it as generated-images/red-circle-smoke-1.png.\n\n逐项确认：\n\n- 工具列表中存在 `generate_image`，pending 调用卡会显示预定输出路径。\n- 调用成功后，会话工作区中出现内容正确的 PNG；回合结束后的“产出文件”行列出同一路径，并且可以点击打开。\n- 通用工具卡不会显示聊天内嵌缩略图；这是当前版本有意延后的功能。\n- 再次使用相同的 `output_paths` 会明确失败，并且原文件字节保持不变。\n- 使用 text-only 对话模型时，不会因 `UNSUPPORTED_CONTENT` 中断；模型只收到文本结果，不会收到 image block。\n- 在默认 base composition 下，不应仅因为插件通过 Node 文件系统写入而出现审批弹窗。自定义 `tools/pre-execute` 策略仍可能返回 `ask`；此时应遵守部署策略，并记录环境差异。\n- 条件允许时生成一张 2048×2048 图片。rc.2 默认配置不会只因为边长达到 2048 px 就拒绝它。如果因字节数、像素数、部署覆盖限制或存储故障而降级，结果警告必须准确说明原因，同时原图仍应成功落盘。\n\n在设置真实密钥前，还应独立执行一次失败路径冒烟。结果必须说明 `OPENAI_IMAGE_API_KEY` 尚未配置，并且不能泄露任何凭据值。该失败测试不能代替配置真实密钥后的成功冒烟。\n\n## 模型体验（Model Experience）\n\n### 工具 schema 与结果内容\n\n#### 模型能看到什么（What the model sees）\n\n完整的工具名称、description、参数 schema、配置的 `outputDir`、默认 provider，以及每个 provider 声明的图片格式都会进入模型的 system prompt。成功结果始终包含一个文本 envelope，列出持久化的工作区路径和磁盘原图事实。只有当前对话使用的 provider/model 路由明确支持图片输入，且附件保存成功时，结果中才会出现 image block；否则模型只会收到解释性警告和磁盘路径。\n\n#### Token 影响（Token effect）\n\n插件启用时，工具 schema 会占用 system prompt token。每次调用会向对话加入一段精简的文本摘要。provider 返回的 base64、API key、生成 prompt 和二进制字节不会被复制到 canonical value 或文本结果中。满足条件的 image block 会产生由当前 LLM adapter 决定的图片表示开销。\n\n#### KV Cache 影响（KV Cache effect）\n\n对于固定的插件配置，工具 schema 是确定性的。修改 provider 路由、图片格式、`outputDir` 或默认 provider，然后重新加载或通过 HMR 重新注册插件，会改变模型可见的 schema 文本，因此相关 system prompt 前缀及其 KV Cache 无法继续复用。普通工具调用不会修改 schema。\n\n## 已知限制与后续工作\n\n- 仅支持同步、OpenAI-compatible 的 `POST /images/generations` 与 `b64_json` 响应。不支持视频、异步轮询/job、图片编辑或变体接口。\n- 当前没有设置页 UI 卡片，也没有聊天卡片内嵌缩略图。未来若增加浏览器 client 插件，需要实现 keyed tool view，并通过 host RPC 加载附件。\n- DoPilot payload 集成有意延期；当前包和 smoke overlay 是独立的开发交付物。\n- 附件存储是可选能力，并有独立的部署限制。rc.2 的 attachment-local 默认允许：单张源图最多 20 MiB、单边最多 8192 px、最多 6400 万像素；每批最多 20 张、合计最多 200 MiB。保存副本可能被规范化到最长边 2048 px、最多 4 MiB。一张有效的 2048×2048 源图不会仅因尺寸为 2048 px 而被拒绝。附件拒绝时会降级为仅落盘。\n- 附件元数据描述的是规范化后的存储副本，所以编码、字节数、宽度和高度可能与磁盘原图不同。可选的 `name`，以及发生缩放时的 `originalDimensions`，都会严格按照附件服务返回的 ref 原值持久化和重组。\n- `attachments.saveImages()` 不接收 `AbortSignal`。取消和协作式 `TOOL_TIMEOUT` 只能在该操作收敛后被观察到，因此附件保存可能推迟超时的最终返回。\n- rc.2 的 `dsh-fs` 没有二进制写入方法，因此图片通过 Node 本地文件系统写入。该实现只支持可信本地主机，不兼容远程或沙箱文件系统抽象。父目录 `lstat` 检查与排他创建能减少误操作，但无法消除恶意本地进程造成的 TOCTOU 竞争。\n- 直接使用 Node 文件系统会绕过 `ctx.fs`，因此 `fs/write-intent` 审批与 `fs/observed` 策略遥测无法观察这些写入。默认 base composition 不会只因为这些写入而询问用户；自定义 `tools/pre-execute` 策略仍可要求批准整个工具调用。\n- provider 配置的 `responseFormat` 必须同时匹配接口实际返回的字节编码与请求的文件扩展名。插件会拒绝不一致的结果，并且不会执行转码。\n","readmeFilename":"README.zh-CN.md"}