@goodandready/dsh-image-gen 0.10.7 → 0.10.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,9 @@
1
+
2
+ ## 🚀 Updates v0.10.8: Lossless JSON Unification, Dual-Output & Strict Validation (#199)
3
+ - **Lossless JSON & Dual-Output Unification**: `upscale_image`, `remove_background`, `blend_images`, and `vectorize_image` now consistently return `toLosslessJson` and formatted markdown `summary` for text-only LLMs.
4
+ - **Strict Input Image Validation**: `extract_design_tokens`, `image_to_css_gradient`, and `check_image_contrast` explicitly validate source image readability instead of silent fallback.
5
+ - **Unit Testing Suite**: Added `test/tool-consistency.test.mjs` covering tokenization, gradient generator, and PWA suite.
6
+
1
7
  # 📦 @goodandready/dsh-image-gen
2
8
 
3
9
  <div align="center">
package/README.ru.md CHANGED
@@ -116,6 +116,12 @@ graph LR
116
116
  ---
117
117
 
118
118
 
119
+
120
+ ## 🚀 Обновления v0.10.8: унификация Lossless JSON, Dual-Output и строгая валидация (#199)
121
+ - **Унификация Lossless JSON и Dual-Output**: Инструменты `upscale_image`, `remove_background`, `blend_images`, `vectorize_image` теперь гарантированно возвращают валидный `toLosslessJson` и генерируют структурированный `summary` с превью для текстовых LLM.
122
+ - **Строгая валидация входных изображений**: В инструментах `extract_design_tokens`, `image_to_css_gradient` и `check_image_contrast` добавлена явная проверка существования файла с информативной ошибкой (`Image not found or unreadable: <path>`).
123
+ - **Сквозное модульное тестирование**: Добавлен тест-сьют `test/tool-consistency.test.mjs` для проверки отказоустойчивости дизайн- и PWA-инструментов.
124
+
119
125
  ## 🚀 Обновления v0.10.4: жизненный цикл Cordis, полнота настроек и интернационализация
120
126
  - **Жизненный цикл Cordis (#136)**: регистрация всех 7 инструментов обёрнута в `ctx.effect` для штатной отписки при перезагрузке плагина.
121
127
  - **Полнота настроек в GUI (#137)**: в карточку настроек добавлены поля для Replicate, SeaDream, Gemini, Local ComfyUI/A1111, пресетов стилей и LLM-улучшителя.
package/lib/index.js CHANGED
@@ -1070,14 +1070,25 @@ export function apply(ctx, config) {
1070
1070
  const filePath = path.join(outDir, name)
1071
1071
  await writeFile(filePath, res.bytes)
1072
1072
  const localUrl = '/dsh-image-gen/image?id=' + encodeURIComponent(attachment.attachmentId)
1073
- return {
1073
+ const summary = buildDualOutputMarkdown({
1074
+ action: 'background removed',
1075
+ filePath,
1076
+ width: res.width || attachment.width,
1077
+ height: res.height || attachment.height,
1078
+ mediaType: 'image/png',
1079
+ provider: 'fal',
1080
+ model: args.model || 'fal-ai/birefnet',
1081
+ attachmentId: attachment.attachmentId,
1082
+ })
1083
+ return toLosslessJson({
1084
+ summary,
1074
1085
  path: filePath,
1075
1086
  url: localUrl,
1076
1087
  width: res.width || attachment.width,
1077
1088
  height: res.height || attachment.height,
1078
1089
  format: 'png',
1079
1090
  attachment,
1080
- }
1091
+ })
1081
1092
  },
1082
1093
  }),
1083
1094
  )
@@ -1128,14 +1139,25 @@ export function apply(ctx, config) {
1128
1139
  const filePath = path.join(outDir, name)
1129
1140
  await writeFile(filePath, res.bytes)
1130
1141
  const localUrl = '/dsh-image-gen/image?id=' + encodeURIComponent(attachment.attachmentId)
1131
- return {
1142
+ const summary = buildDualOutputMarkdown({
1143
+ action: `upscaled (${args.scale || 2}x)`,
1144
+ filePath,
1145
+ width: res.width || attachment.width,
1146
+ height: res.height || attachment.height,
1147
+ mediaType: 'image/png',
1148
+ provider: 'fal',
1149
+ model: 'fal-ai/clarity-upscaler',
1150
+ attachmentId: attachment.attachmentId,
1151
+ })
1152
+ return toLosslessJson({
1153
+ summary,
1132
1154
  path: filePath,
1133
1155
  url: localUrl,
1134
1156
  scale: args.scale || 2,
1135
1157
  width: res.width || attachment.width,
1136
1158
  height: res.height || attachment.height,
1137
1159
  attachment,
1138
- }
1160
+ })
1139
1161
  },
1140
1162
  }),
1141
1163
  )
@@ -1173,11 +1195,13 @@ export function apply(ctx, config) {
1173
1195
  await mkdir(outDir, { recursive: true })
1174
1196
  const filePath = path.join(outDir, name)
1175
1197
  await writeFile(filePath, res.bytes)
1176
- return {
1198
+ const summary = `### Vectorized SVG Export\n- **File**: ${filePath}\n- **Format**: SVG (XML vector)\n- **Color mode**: ${args.color_mode || 'color'}\n- **Palette size**: ${res.palette ? res.palette.length : 16} colors\n`
1199
+ return toLosslessJson({
1200
+ summary,
1177
1201
  path: filePath,
1178
1202
  svg: res.svg,
1179
1203
  format: 'svg',
1180
- }
1204
+ })
1181
1205
  },
1182
1206
  }),
1183
1207
  )
@@ -1232,13 +1256,24 @@ export function apply(ctx, config) {
1232
1256
  const filePath = path.join(outDir, name)
1233
1257
  await writeFile(filePath, res.bytes)
1234
1258
  const localUrl = '/dsh-image-gen/image?id=' + encodeURIComponent(attachment.attachmentId)
1235
- return {
1259
+ const summary = buildDualOutputMarkdown({
1260
+ action: 'blended composition',
1261
+ filePath,
1262
+ width: res.width || attachment.width,
1263
+ height: res.height || attachment.height,
1264
+ mediaType: 'image/png',
1265
+ provider: 'fal',
1266
+ model: 'fal-ai/image-blend',
1267
+ attachmentId: attachment.attachmentId,
1268
+ })
1269
+ return toLosslessJson({
1270
+ summary,
1236
1271
  path: filePath,
1237
1272
  url: localUrl,
1238
1273
  width: res.width || attachment.width,
1239
1274
  height: res.height || attachment.height,
1240
1275
  attachment,
1241
- }
1276
+ })
1242
1277
  },
1243
1278
  }),
1244
1279
  )
@@ -1610,12 +1645,13 @@ export function apply(ctx, config) {
1610
1645
  if (!colors || colors.length === 0) {
1611
1646
  if (args.image) {
1612
1647
  const source = await resolveSource(ctx, exec, args.image)
1613
- if (source && source.bytes) {
1614
- colors = extractSampleColorsFromBuffer(source.bytes)
1648
+ if (!source || !source.bytes) {
1649
+ throw new Error('Image not found or unreadable: ' + args.image)
1615
1650
  }
1651
+ colors = extractSampleColorsFromBuffer(source.bytes)
1616
1652
  }
1617
1653
  }
1618
- return extractDesignTokens(colors, { prefix: args.prefix })
1654
+ return toLosslessJson(extractDesignTokens(colors, { prefix: args.prefix }))
1619
1655
  },
1620
1656
  }),
1621
1657
  )
@@ -1648,12 +1684,13 @@ export function apply(ctx, config) {
1648
1684
  if (!colors || colors.length === 0) {
1649
1685
  if (args.image) {
1650
1686
  const source = await resolveSource(ctx, exec, args.image)
1651
- if (source && source.bytes) {
1652
- colors = extractSampleColorsFromBuffer(source.bytes)
1687
+ if (!source || !source.bytes) {
1688
+ throw new Error('Image not found or unreadable: ' + args.image)
1653
1689
  }
1690
+ colors = extractSampleColorsFromBuffer(source.bytes)
1654
1691
  }
1655
1692
  }
1656
- return generateCssGradient(colors, args.type || 'mesh')
1693
+ return toLosslessJson(generateCssGradient(colors, args.type || 'mesh'))
1657
1694
  },
1658
1695
  }),
1659
1696
  )
@@ -1689,12 +1726,13 @@ export function apply(ctx, config) {
1689
1726
  if (!colors || colors.length === 0) {
1690
1727
  if (args.image) {
1691
1728
  const source = await resolveSource(ctx, exec, args.image)
1692
- if (source && source.bytes) {
1693
- colors = extractSampleColorsFromBuffer(source.bytes)
1729
+ if (!source || !source.bytes) {
1730
+ throw new Error('Image not found or unreadable: ' + args.image)
1694
1731
  }
1732
+ colors = extractSampleColorsFromBuffer(source.bytes)
1695
1733
  }
1696
1734
  }
1697
- return checkWcagContrast(colors, args.text_color || '#ffffff')
1735
+ return toLosslessJson(checkWcagContrast(colors, args.text_color || '#ffffff'))
1698
1736
  },
1699
1737
  }),
1700
1738
  )
@@ -1769,7 +1807,7 @@ export function apply(ctx, config) {
1769
1807
  },
1770
1808
  },
1771
1809
  async execute(args, exec) {
1772
- return generatePwaIconSuite({
1810
+ const res = await generatePwaIconSuite({
1773
1811
  name: args.name || 'App',
1774
1812
  shortName: args.short_name || args.name || 'App',
1775
1813
  themeColor: args.theme_color || '#0f172a',
@@ -1777,6 +1815,7 @@ export function apply(ctx, config) {
1777
1815
  iconsDir: args.icons_dir || 'icons',
1778
1816
  sourcePath: args.image || null,
1779
1817
  })
1818
+ return toLosslessJson(res)
1780
1819
  },
1781
1820
  }),
1782
1821
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-image-gen",
3
- "version": "0.10.7",
3
+ "version": "0.10.8",
4
4
  "description": "Image generation for DeepSeek Harness: a generate_image tool with pluggable providers — the FAL queue, any OpenAI-compatible images API, or a ChatGPT/Grok subscription with no API key at all. The picture is shown inline in the conversation; the model receives either a link (works with any chat model) or the image itself (needs dsh-vision-bridge or a vision-capable model).",
5
5
  "keywords": [
6
6
  "deepseek-harness",