@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 +6 -0
- package/lib/index.js +4 -1
- package/lib/providers.js +7 -3
- package/lib/security.js +5 -4
- package/lib/settings-route.js +1 -1
- package/lib/tools/character-sheet.js +1 -5
- package/lib/tools/diagram.js +1 -5
- package/lib/tools/generation.js +19 -20
- package/lib/tools/style-matrix.js +1 -1
- package/lib/tools/ui-asset.js +1 -1
- package/lib/tools/vision-ocr.js +7 -11
- package/lib/updater.js +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
}
|
package/lib/settings-route.js
CHANGED
|
@@ -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: (
|
|
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
|
package/lib/tools/diagram.js
CHANGED
|
@@ -85,11 +85,7 @@ export function registerDiagramTools(ctx, deps) {
|
|
|
85
85
|
},
|
|
86
86
|
},
|
|
87
87
|
},
|
|
88
|
-
render: (
|
|
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() : {})
|
package/lib/tools/generation.js
CHANGED
|
@@ -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']
|
package/lib/tools/ui-asset.js
CHANGED
|
@@ -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 },
|
package/lib/tools/vision-ocr.js
CHANGED
|
@@ -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: "
|
|
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: (
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-image-gen",
|
|
3
|
-
"version": "0.11.
|
|
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",
|