@goodandready/dsh-image-gen 0.11.12 → 0.11.13

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/lib/client.js CHANGED
@@ -3076,6 +3076,12 @@ window.__ModuleLoader__.load({
3076
3076
  'smart_crop_image',
3077
3077
  'export_asset_pack',
3078
3078
  'generate_ui_asset',
3079
+ 'generate_character_sheet',
3080
+ 'replace_image_text',
3081
+ 'beautify_diagram',
3082
+ 'generate_seamless_pattern',
3083
+ 'generate_spritesheet',
3084
+ 'sketch_to_image',
3079
3085
  ]
3080
3086
 
3081
3087
  for (const toolName of toolviewTools) {
package/lib/index.js CHANGED
@@ -312,7 +312,10 @@ export function apply(ctx, rawConfig) {
312
312
  }
313
313
  try {
314
314
  const u = new URL(req.url, 'http://localhost')
315
- const provider = u.searchParams.get('provider') || config.provider || 'fal'
315
+ const qProvider = u.searchParams.get('provider')
316
+ const provider = (typeof qProvider === 'string' && qProvider.trim())
317
+ || (typeof config?.provider === 'string' && config.provider.trim())
318
+ || 'fal'
316
319
  const deps = {
317
320
  fetchImpl: fetch,
318
321
  resolveKey: (ref) => resolveApiKey(ctx, ref),
package/lib/providers.js CHANGED
@@ -378,7 +378,7 @@ export async function blendImagesFal({ fetchImpl, resolveKey, cfg }, { images, w
378
378
  /** @internal — direct provider call path for integration tests */
379
379
  export async function editImageDirect(deps, job) {
380
380
  const providers = makeProviders(deps, job)
381
- const providerKey = job.provider || deps.cfg.defaultProvider || 'fal'
381
+ const providerKey = (typeof job?.provider === 'string' && job.provider.trim()) || (typeof deps?.cfg?.provider === 'string' && deps.cfg.provider.trim()) || (typeof deps?.cfg?.defaultProvider === 'string' && deps.cfg.defaultProvider.trim()) || 'fal'
382
382
  const fn = providers[providerKey] || providers.fal || providers.custom
383
383
  if (!fn) throw new Error(`Provider "${providerKey}" does not support image editing`)
384
384
  return fn(job.seed, job.prompt)
@@ -391,7 +391,7 @@ export async function varyImageDirect(deps, job) {
391
391
  ...job,
392
392
  strength: job.variationStrength ?? job.strength ?? 0.35,
393
393
  })
394
- const providerKey = job.provider || deps.cfg.defaultProvider || 'fal'
394
+ const providerKey = (typeof job?.provider === 'string' && job.provider.trim()) || (typeof deps?.cfg?.provider === 'string' && deps.cfg.provider.trim()) || (typeof deps?.cfg?.defaultProvider === 'string' && deps.cfg.defaultProvider.trim()) || 'fal'
395
395
  const fn = providers[providerKey] || providers.fal || providers.custom
396
396
  if (!fn) throw new Error(`Provider "${providerKey}" does not support image variations`)
397
397
  return fn(job.seed, job.prompt)
@@ -428,7 +428,11 @@ export function toLosslessJson(val) {
428
428
  export async function testProviderConnection(deps, providerId) {
429
429
  const { fetchImpl = fetch, resolveKey, cfg } = deps
430
430
  const start = Date.now()
431
- const p = (providerId || cfg.defaultProvider || 'fal').toLowerCase()
431
+ const rawProvider = (typeof providerId === 'string' && providerId.trim())
432
+ || (typeof cfg?.provider === 'string' && cfg.provider.trim())
433
+ || (typeof cfg?.defaultProvider === 'string' && cfg.defaultProvider.trim())
434
+ || 'fal'
435
+ const p = rawProvider.toLowerCase()
432
436
 
433
437
  try {
434
438
  if (p === 'fal') {
package/lib/security.js CHANGED
@@ -64,7 +64,8 @@ export function enforceSecurePermissions(filePath) {
64
64
  try {
65
65
  chmodSync(filePath, 0o600)
66
66
  return true
67
- } catch {
67
+ } catch (_err) {
68
+ /* non-fatal: permission change error */
68
69
  return false
69
70
  }
70
71
  }
@@ -143,7 +144,7 @@ export function extractHostName(hostHeader) {
143
144
  try {
144
145
  const url = new URL(`http://${trimmed}`)
145
146
  return url.hostname.replace(/^\[|\]$/g, '')
146
- } catch {
147
+ } catch (_err) {
147
148
  return trimmed.replace(/^\[|\]$/g, '').split(':')[0] || ''
148
149
  }
149
150
  }
@@ -210,7 +211,7 @@ export function isTrustedLocalRequest(req) {
210
211
  if (url.host.toLowerCase() !== host.toLowerCase()) {
211
212
  return false
212
213
  }
213
- } catch {
214
+ } catch (_err) {
214
215
  return false
215
216
  }
216
217
  }
@@ -226,7 +227,7 @@ export function isTrustedLocalRequest(req) {
226
227
  if (!isLoopbackAddress(refererHostName) && !isPrivateLanAddress(refererHostName)) {
227
228
  return false
228
229
  }
229
- } catch {
230
+ } catch (_err) {
230
231
  return false
231
232
  }
232
233
  }
@@ -79,7 +79,7 @@ export function registerSettingsRoutes(ctx, { live, getSettingsApi, setLiveConfi
79
79
  let payload
80
80
  try {
81
81
  payload = JSON.parse(buf.toString('utf8') || '{}')
82
- } catch {
82
+ } catch (_err) {
83
83
  res.writeHead(400, { 'Content-Type': 'application/json' })
84
84
  res.end(JSON.stringify({ ok: false, error: 'invalid JSON' }))
85
85
  return
@@ -89,11 +89,7 @@ export function registerCharacterSheetTools(ctx, deps) {
89
89
  },
90
90
  },
91
91
  },
92
- render: (result, renderCtx) =>
93
- renderToolOutput(result, renderCtx, {
94
- title: (res) => `Character Sheet (${res.layout || 'turnaround'}) — ${res.style || 'concept_art'}`,
95
- subtitle: (res) => (res.views ? `Views: ${res.views.join(' | ')}` : ''),
96
- }),
92
+ render: (_args, value) => renderToolOutput(value),
97
93
  },
98
94
  async execute(args, execCtx) {
99
95
  const cfg = live.value
@@ -85,11 +85,7 @@ export function registerDiagramTools(ctx, deps) {
85
85
  },
86
86
  },
87
87
  },
88
- render: (result, renderCtx) =>
89
- renderToolOutput(result, renderCtx, {
90
- title: (res) => `3D Architecture Diagram — ${res.title || res.style || 'Isometric'}`,
91
- subtitle: (res) => (res.detectedNodes?.length ? `Nodes: ${res.detectedNodes.slice(0, 4).join(', ')}` : ''),
92
- }),
88
+ render: (_args, value) => renderToolOutput(value),
93
89
  },
94
90
  async execute(args, execCtx) {
95
91
  const cfg = live?.value || (typeof live === 'function' ? live() : {})
@@ -1,22 +1,4 @@
1
1
  import { registerGenerationPackTool } from './generation-pack.js'
2
-
3
- /** Run an array of async task functions with a concurrency cap */
4
- async function asyncPool(tasks, concurrency = 3) {
5
- const results = new Array(tasks.length)
6
- let nextIdx = 0
7
- async function worker() {
8
- while (nextIdx < tasks.length) {
9
- const idx = nextIdx++
10
- results[idx] = await tasks[idx]()
11
- }
12
- }
13
- const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker())
14
- await Promise.all(workers)
15
- return results
16
- }
17
-
18
- // generation — image-gen tools (generate_image, generate_image_pack). Extracted from apply() (#216).
19
-
20
2
  import { defineTool } from '@deepseek-ai/dsh-tools'
21
3
  import path from 'node:path'
22
4
  import { mkdir, writeFile } from 'node:fs/promises'
@@ -53,6 +35,22 @@ import {
53
35
  cachedResult,
54
36
  } from '../history.js'
55
37
 
38
+ // generation — image-gen tools (generate_image, generate_image_pack). Extracted from apply() (#216).
39
+
40
+ /** Run an array of async task functions with a concurrency cap */
41
+ async function asyncPool(tasks, concurrency = 3) {
42
+ const results = new Array(tasks.length)
43
+ let nextIdx = 0
44
+ async function worker() {
45
+ while (nextIdx < tasks.length) {
46
+ const idx = nextIdx++
47
+ results[idx] = await tasks[idx]()
48
+ }
49
+ }
50
+ const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker())
51
+ await Promise.all(workers)
52
+ return results
53
+ }
56
54
 
57
55
  export function registerGenerationTools(ctx, deps) {
58
56
  const {
@@ -344,7 +342,7 @@ export function registerGenerationTools(ctx, deps) {
344
342
  // Subscriptions service is optional: without it, subscription providers
345
343
  // gracefully decline while other providers continue functioning.
346
344
  let subscriptionImages
347
- try { subscriptionImages = ctx.get && ctx.get('subscriptionImages') } catch { subscriptionImages = undefined }
345
+ try { subscriptionImages = ctx.get && ctx.get('subscriptionImages') } catch (_err) { subscriptionImages = undefined }
348
346
 
349
347
  const source = args.source_image ? await resolveSource(ctx, exec, args.source_image) : undefined
350
348
  const mask = args.mask ? await resolveSource(ctx, exec, args.mask) : undefined
@@ -358,7 +356,8 @@ export function registerGenerationTools(ctx, deps) {
358
356
  if (faceRefInput) {
359
357
  try {
360
358
  resolvedFaceRef = await resolveSource(ctx, exec, faceRefInput)
361
- } catch {
359
+ } catch (_err) {
360
+ /* non-fatal: face_reference resolve fallback */
362
361
  resolvedFaceRef = { url: faceRefInput }
363
362
  }
364
363
  const facePlan = buildFaceConsistencyPrompt({
@@ -126,7 +126,7 @@ export function registerStyleMatrixTools(ctx, deps) {
126
126
 
127
127
  const provider = PROVIDER_KEYS.includes(cfg.provider) ? cfg.provider : 'fal'
128
128
  let subscriptionImages
129
- try { subscriptionImages = ctx.get && ctx.get('subscriptionImages') } catch { subscriptionImages = undefined }
129
+ try { subscriptionImages = ctx.get && ctx.get('subscriptionImages') } catch (_err) { subscriptionImages = undefined }
130
130
 
131
131
  const chain = resolveFallbackChain(provider, cfg.fallbackProviders, PROVIDER_KEYS)
132
132
  const quadrantIds = ['A', 'B', 'C', 'D']
@@ -145,7 +145,7 @@ export function registerUiAssetTools(ctx, deps) {
145
145
 
146
146
  const provider = PROVIDER_KEYS.includes(cfg.provider) ? cfg.provider : 'fal'
147
147
  let subscriptionImages
148
- try { subscriptionImages = ctx.get && ctx.get('subscriptionImages') } catch { subscriptionImages = undefined }
148
+ try { subscriptionImages = ctx.get && ctx.get('subscriptionImages') } catch (_err) { subscriptionImages = undefined }
149
149
 
150
150
  const providers = makeProviders(
151
151
  { fetchImpl: fetch, resolveKey: (ref) => resolveApiKey(ctx, ref), cfg, subscriptionImages },
@@ -39,7 +39,7 @@ export function registerVisionOcrTools(ctx, deps) {
39
39
  },
40
40
  replacements: {
41
41
  type: 'array',
42
- description: 'Array of text replacements: [{ find: "Old Text", replace: "Новый Текст", bbox: [x1,y1,x2,y2] }].',
42
+ description: 'Array of text replacements: [{ find: "Old Text", replace: "New Text", bbox: [x1,y1,x2,y2] }].',
43
43
  items: {
44
44
  type: 'object',
45
45
  additionalProperties: true,
@@ -109,11 +109,7 @@ export function registerVisionOcrTools(ctx, deps) {
109
109
  },
110
110
  },
111
111
  },
112
- render: (result, renderCtx) =>
113
- renderToolOutput(result, renderCtx, {
114
- title: (res) => `Text Replaced (${res.replacedCount || 0} inscriptions)`,
115
- subtitle: (res) => (res.bboxes?.length ? `Detected regions: ${res.bboxes.length}` : ''),
116
- }),
112
+ render: (_args, value) => renderToolOutput(value),
117
113
  },
118
114
  async execute(args, execCtx) {
119
115
  const cfg = live?.value || (typeof live === 'function' ? live() : {})
@@ -145,8 +141,8 @@ export function registerVisionOcrTools(ctx, deps) {
145
141
  if (gRes?.bbox && gRes.bbox.length === 4) {
146
142
  bbox = gRes.bbox
147
143
  }
148
- } catch {
149
- // non-fatal vision_ground fallback
144
+ } catch (_err) {
145
+ /* non-fatal: vision_ground fallback */
150
146
  }
151
147
  }
152
148
  }
@@ -168,7 +164,7 @@ export function registerVisionOcrTools(ctx, deps) {
168
164
  const defaultBox = [150, 420, 850, 520]
169
165
  bboxes.push(defaultBox)
170
166
  overlayItems.push({
171
- text: args.target_language === 'ru' ? 'Заголовок' : 'Localized Text',
167
+ text: 'Localized Text',
172
168
  bbox: defaultBox,
173
169
  color: args.font_color || '#FFFFFF',
174
170
  })
@@ -216,8 +212,8 @@ export function registerVisionOcrTools(ctx, deps) {
216
212
  )
217
213
  inpaintedBytes = inpaintResult.bytes
218
214
  inpaintedUrl = inpaintResult.url
219
- } catch {
220
- // If inpaint provider unavailable, keep source bytes and proceed with overlay
215
+ } catch (_err) {
216
+ /* non-fatal: inpaint provider unavailable; use source bytes */
221
217
  inpaintedBytes = source.bytes
222
218
  }
223
219
 
package/lib/updater.js CHANGED
@@ -39,7 +39,7 @@ export function isTrustedUpdateRequest(request) {
39
39
  (isLoopbackAddress(hostName) || isPrivateLanAddress(hostName)) &&
40
40
  url.host.toLowerCase() === host.toLowerCase()
41
41
  )
42
- } catch {
42
+ } catch (_err) {
43
43
  return false
44
44
  }
45
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-image-gen",
3
- "version": "0.11.12",
3
+ "version": "0.11.13",
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",