@goodandready/dsh-image-gen 0.8.0 → 0.8.1

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 dsh-fal-image-gen contributors
3
+ Copyright (c) 2026 dsh-image-gen contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/lib/client.js CHANGED
@@ -796,30 +796,46 @@ window.__ModuleLoader__.load({
796
796
  FalImageCard,
797
797
  ),
798
798
  )
799
- // Свой раздел в боковом списке настроек, а не карточка во вкладке
800
- // «Настройки плагинов».
799
+ // Карточка во вкладке «Настройки плагинов» вместо строки в боковом
800
+ // списке: список плоский и перестал помещаться, а карточки вкладки
801
+ // сворачиваются. Ключ слота обязан равняться пространству настроек:
802
+ // вкладка перебирает именно их, с чужим ключом карточка исчезла бы молча.
803
+ // Заголовок, пояснение и сворачивание рисует сама карточка — ядро даёт
804
+ // только рамку списка.
801
805
  //
802
- // Ту вкладку рисует ядро, и в ней карточка появляется только если
803
- // namespace успел попасть в общий вид настроек. У части установок эта
804
- // вкладка остаётся пустой — плагины при этом грузятся штатно, ошибок нет,
805
- // и повлиять на неё из плагина нечем. Собственный раздел рисуем мы сами,
806
- // рядом с Vision и Голосом, и он от этой вкладки не зависит.
807
- //
808
- // Заодно отпадает возня с keyed-слотом: settings.section принимает id на
809
- // всех версиях ядра, и держать сразу id и key больше не нужно.
810
- ctx.slots.inject('settings.section', () =>
806
+ // Запасной путь: слот объявлен пакетом настроек ядра. Если в сборке его
807
+ // нет, регистрация бросает — тогда остаётся прежний раздел в боковом
808
+ // списке, чтобы настройки не пропали.
809
+ let placedInPluginsTab = false
810
+ try {
811
811
  ctx.slots.register(
812
812
  {
813
- name: 'settings.section',
814
- id: '@goodandready/dsh-image-gen',
815
- order: 34,
813
+ name: 'settings.plugin.item',
814
+ key: SETTINGS_NS,
816
815
  locale: NS,
817
- label: () => t('settings.navLabel'),
818
816
  inject: () => cardOnce().inject(),
819
817
  },
820
818
  FalSettingsCard,
821
- ),
822
- )
819
+ )
820
+ placedInPluginsTab = true
821
+ } catch (noPluginItemSlot) {
822
+ placedInPluginsTab = false
823
+ }
824
+ if (!placedInPluginsTab) {
825
+ ctx.slots.inject('settings.section', () =>
826
+ ctx.slots.register(
827
+ {
828
+ name: 'settings.section',
829
+ id: '@goodandready/dsh-image-gen',
830
+ order: 34,
831
+ locale: NS,
832
+ label: () => t('settings.navLabel'),
833
+ inject: () => cardOnce().inject(),
834
+ },
835
+ FalSettingsCard,
836
+ ),
837
+ )
838
+ }
823
839
  }
824
840
  exports.apply = apply
825
841
  exports.inject = inject
package/lib/index.js CHANGED
@@ -21,11 +21,12 @@ import {
21
21
  IMAGE_SIZES,
22
22
  OUTPUT_FORMATS,
23
23
  PROVIDER_KEYS,
24
+ buildSidecar,
24
25
  makeProviders,
25
26
  normalizeMediaType,
26
27
  } from './providers.js'
27
28
 
28
- export { IMAGE_SIZES, OUTPUT_FORMATS, PROVIDER_KEYS, normalizeMediaType }
29
+ export { IMAGE_SIZES, OUTPUT_FORMATS, PROVIDER_KEYS, buildSidecar, normalizeMediaType }
29
30
 
30
31
  export const name = 'dsh-image-gen'
31
32
 
@@ -335,6 +336,10 @@ export function apply(ctx, config) {
335
336
  { prompt: args.prompt, size, format, seed: args.seed, signal: exec.signal },
336
337
  )
337
338
  const generated = await providers[provider]()
339
+ // Подписочные провайдеры (codex/grok) отдают {ok:false, reason} вместо исключения:
340
+ // отказ должен дойти до модели текстом. Без проверки execute шёл дальше с пустыми
341
+ // байтами, и пользователь получал битую карточку вместо внятного отказа.
342
+ if (generated && generated.ok === false) throw new Error(generated.reason)
338
343
  const bytes = generated.bytes
339
344
 
340
345
  const mediaType = generated.mediaType
@@ -355,6 +360,24 @@ export function apply(ctx, config) {
355
360
  await mkdir(outDir, { recursive: true })
356
361
  const filePath = path.join(outDir, name)
357
362
  await writeFile(filePath, bytes)
363
+ // Sidecar: рядом с картинкой — метаданные генерации. Каталог становится
364
+ // самодокументируемым (галерея/повтор/диагностика читают их без БД).
365
+ await writeFile(
366
+ path.join(outDir, `${stem}.json`),
367
+ JSON.stringify(buildSidecar({
368
+ prompt: args.prompt,
369
+ size,
370
+ format,
371
+ seed: generated.seed,
372
+ provider,
373
+ deliverAs,
374
+ width: generated.width || attachment.width,
375
+ height: generated.height || attachment.height,
376
+ mediaType,
377
+ attachmentId: attachment.attachmentId,
378
+ url: deliverAs === 'image' && generated.sourceUrl ? generated.sourceUrl : localUrl,
379
+ }), null, 2),
380
+ )
358
381
 
359
382
  // The plugin's own route outlives the fal.media link, and the card
360
383
  // needs the reference anyway to verify the bytes.
package/lib/providers.js CHANGED
@@ -65,6 +65,14 @@ export function normalizeMediaType(contentType, fallbackFormat) {
65
65
  return 'image/png'
66
66
  }
67
67
 
68
+ /** Метаданные одной генерации для sidecar-файла рядом с картинкой. */
69
+ export function buildSidecar({
70
+ prompt, size, format, seed, provider, deliverAs,
71
+ width, height, mediaType, attachmentId, url, createdAt = new Date().toISOString(),
72
+ }) {
73
+ return { prompt, size, format, seed, provider, deliverAs, width, height, mediaType, attachmentId, url, createdAt }
74
+ }
75
+
68
76
  /** Submit a generation job to the FAL queue. */
69
77
  export async function submitJob(fetchImpl, baseURL, model, key, body, signal) {
70
78
  const res = await fetchImpl(`${baseURL}/${model}`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-image-gen",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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",