@goodandready/dsh-image-gen 0.12.1 → 0.12.2

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
@@ -72,7 +72,7 @@ window.__ModuleLoader__.load({
72
72
  } else if (typeof AbortController !== 'undefined') {
73
73
  const ctrl = new AbortController()
74
74
  timer = setTimeout(() => {
75
- try { ctrl.abort(new Error('Request timeout')) } catch (_) {}
75
+ try { ctrl.abort(new Error('Request timeout')) } catch (_) { /* ignore abort race */ }
76
76
  }, timeoutMs)
77
77
  sig = ctrl.signal
78
78
  }
@@ -82,7 +82,7 @@ window.__ModuleLoader__.load({
82
82
  : (() => {
83
83
  const ctrl = new AbortController()
84
84
  timer = setTimeout(() => {
85
- try { ctrl.abort(new Error('Request timeout')) } catch (_) {}
85
+ try { ctrl.abort(new Error('Request timeout')) } catch (_) { /* ignore abort race */ }
86
86
  }, timeoutMs)
87
87
  return ctrl.signal
88
88
  })()
@@ -101,12 +101,12 @@ window.__ModuleLoader__.load({
101
101
  if (typeof signal.addEventListener === 'function') {
102
102
  signal.addEventListener('abort', onCallerAbort, { once: true })
103
103
  removeCallerAbort = () => {
104
- try { signal.removeEventListener('abort', onCallerAbort) } catch (_) {}
104
+ try { signal.removeEventListener('abort', onCallerAbort) } catch (_) { /* ignore listener cleanup */ }
105
105
  }
106
106
  }
107
107
  }
108
108
  timer = setTimeout(() => {
109
- try { ctrl.abort(new Error('Request timeout')) } catch (_) {}
109
+ try { ctrl.abort(new Error('Request timeout')) } catch (_) { /* ignore abort race */ }
110
110
  }, timeoutMs)
111
111
  sig = ctrl.signal
112
112
  }
package/lib/index.js CHANGED
@@ -229,7 +229,7 @@ export function apply(ctx, rawConfig) {
229
229
  if (typeof settingsSvc?.describe !== 'function') return null
230
230
  try {
231
231
  const row = settingsSvc.describe().find((r) => r?.ns === NS)
232
- if (!row || row.status !== 'ready' || !row.value) return null
232
+ if (!row || !row.value || (row.status !== undefined && row.status !== 'ready')) return null
233
233
  return Config(structuredClone(plainConfig(row.value)))
234
234
  } catch (_) {
235
235
  return null
@@ -658,6 +658,11 @@ export function apply(ctx, rawConfig) {
658
658
 
659
659
  // Reset loop guard when user sends a new message (#370)
660
660
  if (typeof ctx.on === 'function') {
661
+ ctx.on('user/message', (session) => {
662
+ const sid = session?.id || session?.sessionId || session?.conversationId
663
+ if (sid) resetLoopGuard(sid)
664
+ else resetLoopGuard()
665
+ })
661
666
  ctx.on('chat/message', (session) => {
662
667
  if (session?.id) resetLoopGuard(session.id)
663
668
  })
@@ -1,3 +1,4 @@
1
+ import { resolveInside } from '../security.js'
1
2
  import { buildDualOutputMarkdown } from '../resolve-image.js'
2
3
  // character-sheet.js — generate_character_sheet tool (#181, #358).
3
4
  // Generates multi-angle character model sheets and turnarounds with visual consistency.
@@ -184,7 +185,7 @@ export function registerCharacterSheetTools(ctx, deps) {
184
185
 
185
186
  const sessionCwd = execCtx?.agent?.session?.header?.cwd
186
187
  const targetDir = cfg.outputDir || 'generated/images'
187
- const outDir = path.resolve(sessionCwd || process.cwd(), targetDir)
188
+ const outDir = resolveInside(sessionCwd || process.cwd(), targetDir)
188
189
  await mkdir(outDir, { recursive: true })
189
190
  diskPath = path.join(outDir, filename)
190
191
  await writeFile(diskPath, Buffer.from(finalBytes))
@@ -1,3 +1,4 @@
1
+ import { resolveInside } from '../security.js'
1
2
  // diagram.js — beautify_diagram tool (#185, #359).
2
3
  // Transforms raw Mermaid / Graphviz diagrams into high-end 3D isometric architectural illustrations.
3
4
 
@@ -180,7 +181,7 @@ export function registerDiagramTools(ctx, deps) {
180
181
 
181
182
  const sessionCwd = execCtx?.agent?.session?.header?.cwd
182
183
  const targetDir = cfg.outputDir || 'generated/images'
183
- const outDir = path.resolve(sessionCwd || process.cwd(), targetDir)
184
+ const outDir = resolveInside(sessionCwd || process.cwd(), targetDir)
184
185
  await mkdir(outDir, { recursive: true })
185
186
  diskPath = path.join(outDir, filename)
186
187
  await writeFile(diskPath, Buffer.from(finalBytes))
@@ -6,7 +6,7 @@ import path from 'node:path'
6
6
  import { mkdir, writeFile } from 'node:fs/promises'
7
7
  import { makeProviders, toLosslessJson, saveAttachmentSafe, sizeToPixels } from '../providers.js'
8
8
  import { trackAndAssertLoopGuard } from '../loop-guard.js'
9
- import { sanitizeErrorAndLogs } from '../security.js'
9
+ import { sanitizeErrorAndLogs, resolveInside } from '../security.js'
10
10
  import {
11
11
  buildSeamlessPrompt,
12
12
  buildPatternCss,
@@ -95,7 +95,7 @@ export function registerPatternTools(ctx, deps) {
95
95
  })
96
96
 
97
97
  const sessionCwd = exec?.agent?.session?.header?.cwd
98
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
98
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
99
99
  await mkdir(outDir, { recursive: true })
100
100
  const filePath = path.join(outDir, name)
101
101
  await writeFile(filePath, gen.bytes)
@@ -160,7 +160,7 @@ export function registerProcessingAdvancedTools(ctx, deps) {
160
160
  const { attachment, localUrl } = await saveAttachmentSafe(ctx, { bytes: svgBuffer, mediaType: 'image/svg+xml', name })
161
161
 
162
162
  const sessionCwd = exec?.agent?.session?.header?.cwd
163
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
163
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
164
164
  await mkdir(outDir, { recursive: true })
165
165
  const filePath = path.join(outDir, name)
166
166
  await writeFile(filePath, svgBuffer)
@@ -330,7 +330,7 @@ export function registerProcessingAdvancedTools(ctx, deps) {
330
330
  const { attachment, localUrl } = await saveAttachmentSafe(ctx, { bytes: svgBuffer, mediaType: 'image/svg+xml', name })
331
331
 
332
332
  const sessionCwd = exec?.agent?.session?.header?.cwd
333
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
333
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
334
334
  await mkdir(outDir, { recursive: true })
335
335
  const filePath = path.join(outDir, name)
336
336
  await writeFile(filePath, svgBuffer)
@@ -17,7 +17,7 @@ import {
17
17
  import { buildDualOutputMarkdown } from '../resolve-image.js'
18
18
  import { assertBudgetAvailable, reserveSpend, recordSpend } from '../cost-meter.js'
19
19
  import { trackAndAssertLoopGuard } from '../loop-guard.js'
20
- import { sanitizeErrorAndLogs } from '../security.js'
20
+ import { sanitizeErrorAndLogs, resolveInside } from '../security.js'
21
21
 
22
22
  export function registerProcessingTools(ctx, deps) {
23
23
  const {
@@ -69,7 +69,7 @@ export function registerProcessingTools(ctx, deps) {
69
69
  const name = `${stem}.png`
70
70
  const { attachment, localUrl } = await saveAttachmentSafe(ctx, { bytes: res.bytes, mediaType: 'image/png', name })
71
71
  const sessionCwd = exec?.agent?.session?.header?.cwd
72
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
72
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
73
73
  await mkdir(outDir, { recursive: true })
74
74
  const filePath = path.join(outDir, name)
75
75
  await writeFile(filePath, res.bytes)
@@ -151,7 +151,7 @@ export function registerProcessingTools(ctx, deps) {
151
151
  const name = `${stem}.png`
152
152
  const { attachment, localUrl } = await saveAttachmentSafe(ctx, { bytes: res.bytes, mediaType: 'image/png', name })
153
153
  const sessionCwd = exec?.agent?.session?.header?.cwd
154
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
154
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
155
155
  await mkdir(outDir, { recursive: true })
156
156
  const filePath = path.join(outDir, name)
157
157
  await writeFile(filePath, res.bytes)
@@ -219,7 +219,7 @@ export function registerProcessingTools(ctx, deps) {
219
219
  const stem = `${slugify(args.output_name || 'vector')}-${Date.now().toString(36)}`
220
220
  const name = `${stem}.svg`
221
221
  const sessionCwd = exec?.agent?.session?.header?.cwd
222
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
222
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
223
223
  await mkdir(outDir, { recursive: true })
224
224
  const filePath = path.join(outDir, name)
225
225
  await writeFile(filePath, res.bytes)
@@ -302,7 +302,7 @@ export function registerProcessingTools(ctx, deps) {
302
302
  const name = `${stem}.png`
303
303
  const { attachment, localUrl } = await saveAttachmentSafe(ctx, { bytes: res.bytes, mediaType: 'image/png', name })
304
304
  const sessionCwd = exec?.agent?.session?.header?.cwd
305
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
305
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
306
306
  await mkdir(outDir, { recursive: true })
307
307
  const filePath = path.join(outDir, name)
308
308
  await writeFile(filePath, res.bytes)
@@ -6,7 +6,7 @@ import path from 'node:path'
6
6
  import { mkdir, writeFile } from 'node:fs/promises'
7
7
  import { makeProviders, toLosslessJson, saveAttachmentSafe } from '../providers.js'
8
8
  import { trackAndAssertLoopGuard } from '../loop-guard.js'
9
- import { sanitizeErrorAndLogs } from '../security.js'
9
+ import { sanitizeErrorAndLogs, resolveInside } from '../security.js'
10
10
  import {
11
11
  ANIMATION_PRESETS,
12
12
  clampFrames,
@@ -112,7 +112,7 @@ export function registerSpritesheetTools(ctx, deps) {
112
112
  })
113
113
 
114
114
  const sessionCwd = exec?.agent?.session?.header?.cwd
115
- const outDir = path.resolve(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
115
+ const outDir = resolveInside(sessionCwd || process.cwd(), cfg.outputDir || 'generated/images')
116
116
  await mkdir(outDir, { recursive: true })
117
117
  const filePath = path.join(outDir, sheetName)
118
118
  await writeFile(filePath, svgBuffer)
@@ -1,4 +1,4 @@
1
- import { sanitizeErrorAndLogs } from '../security.js'
1
+ import { sanitizeErrorAndLogs, resolveInside } from '../security.js'
2
2
  // style-matrix.js — generate_style_matrix tool (#286).
3
3
  // Parallel 2×2 style matrix explorer with blind A/B compare and one-click preset selection.
4
4
 
@@ -154,7 +154,7 @@ export function registerStyleMatrixTools(ctx, deps) {
154
154
 
155
155
  const sessionCwd = exec?.agent?.session?.header?.cwd
156
156
  const targetDir = cfg.outputDir || 'generated/images'
157
- const outDir = path.resolve(sessionCwd || process.cwd(), targetDir)
157
+ const outDir = resolveInside(sessionCwd || process.cwd(), targetDir)
158
158
  await mkdir(outDir, { recursive: true })
159
159
 
160
160
  const tasks = activeStyles.map((styleName, idx) => async () => {
@@ -1,3 +1,4 @@
1
+ import { resolveInside } from '../security.js'
1
2
  // theme-pair.js — generate_theme_pair tool (#189, #361).
2
3
  // Generates complementary Light & Dark mode asset variants with synchronized compositions.
3
4
 
@@ -161,7 +162,7 @@ export function registerThemePairTools(ctx, deps) {
161
162
 
162
163
  const sessionCwd = exec?.agent?.session?.header?.cwd
163
164
  const targetDir = cfg.outputDir || 'generated/images'
164
- const outDir = path.resolve(sessionCwd || process.cwd(), targetDir)
165
+ const outDir = resolveInside(sessionCwd || process.cwd(), targetDir)
165
166
  await mkdir(outDir, { recursive: true })
166
167
 
167
168
  const slug = args.output_name ? slugify(args.output_name) : slugify(prompt).slice(0, 36)
@@ -1,4 +1,4 @@
1
- import { sanitizeErrorAndLogs } from '../security.js'
1
+ import { sanitizeErrorAndLogs, resolveInside } from '../security.js'
2
2
  // ui-asset.js — generate_ui_asset tool (#284).
3
3
  // Specialized generator for UI icons, stickers, illustrations, and layout-aware banners with negative space.
4
4
 
@@ -221,7 +221,7 @@ export function registerUiAssetTools(ctx, deps) {
221
221
 
222
222
  const sessionCwd = exec?.agent?.session?.header?.cwd
223
223
  const targetDir = cfg.outputDir || 'generated/images'
224
- const outDir = path.resolve(sessionCwd || process.cwd(), targetDir)
224
+ const outDir = resolveInside(sessionCwd || process.cwd(), targetDir)
225
225
  await mkdir(outDir, { recursive: true })
226
226
  const filePath = path.join(outDir, filename)
227
227
  await writeFile(filePath, finalBytes)
@@ -1,3 +1,4 @@
1
+ import { resolveInside } from '../security.js'
1
2
  // vision-ocr.js — replace_image_text tool (#180, #360).
2
3
  // Grounding OCR, background inpainting, and typography re-rendering for visual localization.
3
4
 
@@ -295,7 +296,7 @@ export function registerVisionOcrTools(ctx, deps) {
295
296
  const usedProvider = gen._fallback?.providerUsed || provider
296
297
  const sessionCwd = execCtx?.agent?.session?.header?.cwd
297
298
  const targetDir = cfg.outputDir || 'generated/images'
298
- const outDir = path.resolve(sessionCwd || process.cwd(), targetDir)
299
+ const outDir = resolveInside(sessionCwd || process.cwd(), targetDir)
299
300
  await mkdir(outDir, { recursive: true })
300
301
  const timestamp = Date.now()
301
302
  const safeSlug = slugify(String(args.image || '').slice(0, 20)) || 'localized-image'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-image-gen",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
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",