@goodandready/dsh-image-gen 0.11.21 → 0.11.22

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.
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path'
2
2
  import { mkdir, writeFile } from 'node:fs/promises'
3
3
  import { saveAttachmentSafe, buildSidecar, toLosslessJson } from './providers.js'
4
+ import { recordSpend } from './cost-meter.js'
4
5
  import { buildDualOutputMarkdown } from './resolve-image.js'
5
6
 
6
7
  export async function saveAndAttachResult(ctx, exec, cfg, {
@@ -47,6 +48,19 @@ export async function saveAndAttachResult(ctx, exec, cfg, {
47
48
  }), null, 2),
48
49
  )
49
50
 
51
+ const effectiveCost = Number(cost) || 0
52
+ if (effectiveCost > 0) {
53
+ recordSpend(effectiveCost, {
54
+ ctx,
55
+ meta: {
56
+ provider,
57
+ model: model || cfg.model || cfg.customModel || 'default',
58
+ prompt,
59
+ seed,
60
+ },
61
+ })
62
+ }
63
+
50
64
  const summary = buildDualOutputMarkdown({
51
65
  action,
52
66
  filePath,
package/lib/commands.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { makeProviders, resolveStylePreset, applyStylePreset } from './providers.js'
4
4
  import { ASPECT_RATIOS } from './providers/shared-helpers.js'
5
- import { calculateGenerationCost } from './cost-meter.js'
5
+ import { calculateGenerationCost, assertBudgetAvailable } from './cost-meter.js'
6
6
 
7
7
  /**
8
8
  * Parse raw slash command arguments: /image <prompt> [--flags]
@@ -74,6 +74,13 @@ export function registerImageCommand(ctx, deps) {
74
74
  }
75
75
 
76
76
  const cfg = typeof live === 'function' ? live() : (deps.config || {})
77
+ if (cfg && cfg.enabled === false) {
78
+ return {
79
+ kind: 'error',
80
+ text: 'Image generation is disabled in settings (dsh-image-gen.enabled is false); enable it in Settings → Image generation.',
81
+ toString() { return this.text },
82
+ }
83
+ }
77
84
  const provider = flags.provider || cfg.provider || 'fal'
78
85
  const size = flags.size || cfg.defaultSize || 'square'
79
86
  const format = flags.format || cfg.defaultFormat || 'png'
@@ -101,6 +108,14 @@ export function registerImageCommand(ctx, deps) {
101
108
  ctx,
102
109
  }
103
110
 
111
+ const cost = calculateGenerationCost({
112
+ provider,
113
+ model: cfg.model || cfg.customModel || 'default',
114
+ size,
115
+ count: 1,
116
+ })
117
+ assertBudgetAvailable(cost, cfg.dailyBudgetUsd)
118
+
104
119
  const providerFactory = deps.makeProviders || makeProviders
105
120
  const providers = providerFactory(pdeps, job)
106
121
  const generateFn = providers[provider] || providers.fal || providers.custom
@@ -113,13 +128,6 @@ export function registerImageCommand(ctx, deps) {
113
128
  const stem = `${safeSlug}-${Date.now().toString(36)}`
114
129
  const name = `${stem}.${format}`
115
130
 
116
- const cost = calculateGenerationCost({
117
- provider,
118
- model: cfg.model || cfg.customModel || 'default',
119
- size,
120
- count: 1,
121
- })
122
-
123
131
  const attached = await saveAndAttachResult(ctx, invocation, cfg, {
124
132
  bytes: result.bytes,
125
133
  mediaType: result.mediaType || 'image/png',
@@ -100,11 +100,17 @@ export const Config = z.object({
100
100
  .volatile(),
101
101
  pollIntervalMs: z
102
102
  .number()
103
+ .min(0)
104
+ .max(60000)
105
+ .step(1)
103
106
  .description('Polling interval for FAL queue status in milliseconds.')
104
107
  .default(500)
105
108
  .volatile(),
106
109
  timeoutMs: z
107
110
  .number()
111
+ .min(1000)
112
+ .max(600000)
113
+ .step(1)
108
114
  .description('Hard timeout for image generation in milliseconds.')
109
115
  .default(180000)
110
116
  .volatile(),
@@ -180,8 +186,8 @@ export const Config = z.object({
180
186
  .default('GEMINI_API_KEY')
181
187
  .volatile(),
182
188
  localKind: z
183
- .string()
184
- .description('Local generation backend kind: "automatic1111" (WebUI) or "comfyui".')
189
+ .union([z.const('automatic1111'), z.const('a1111'), z.const('comfyui')])
190
+ .description('Local generation backend kind: "automatic1111" / "a1111" (WebUI) or "comfyui".')
185
191
  .default('automatic1111')
186
192
  .volatile(),
187
193
  localBaseURL: z
@@ -196,11 +202,16 @@ export const Config = z.object({
196
202
  .volatile(),
197
203
  localSteps: z
198
204
  .number()
205
+ .min(1)
206
+ .max(150)
207
+ .step(1)
199
208
  .description('Sampling steps for local generation.')
200
209
  .default(20)
201
210
  .volatile(),
202
211
  localCfg: z
203
212
  .number()
213
+ .min(1)
214
+ .max(30)
204
215
  .description('CFG scale for local generation.')
205
216
  .default(7.0)
206
217
  .volatile(),
@@ -231,6 +242,9 @@ export const Config = z.object({
231
242
  .volatile(),
232
243
  enhanceBelowChars: z
233
244
  .number()
245
+ .min(0)
246
+ .max(5000)
247
+ .step(1)
234
248
  .description('Prompts shorter than this character count trigger prompt enhancement.')
235
249
  .default(40)
236
250
  .volatile(),
@@ -251,11 +265,17 @@ export const Config = z.object({
251
265
  .volatile(),
252
266
  historyLimit: z
253
267
  .number()
254
- .description('Maximum number of generation history records to retain in ~/.dsh/image-gen-history.json.')
268
+ .min(0)
269
+ .max(50000)
270
+ .step(1)
271
+ .description('Maximum number of generation history records to retain in ~/.dsh/image-gen-history.json (0 = disable history retention).')
255
272
  .default(500)
256
273
  .volatile(),
257
274
  pruneDays: z
258
275
  .number()
276
+ .min(0)
277
+ .max(3650)
278
+ .step(1)
259
279
  .description('Prune history records older than this many days (0 = disable time-based pruning).')
260
280
  .default(30)
261
281
  .volatile(),
@@ -266,11 +286,16 @@ export const Config = z.object({
266
286
  .volatile(),
267
287
  dailyBudgetUsd: z
268
288
  .number()
289
+ .min(0)
290
+ .max(100000)
269
291
  .description('Daily image generation spending budget in USD. 0 (default) disables limit enforcement.')
270
292
  .default(0)
271
293
  .volatile(),
272
294
  loopGuardLimit: z
273
295
  .number()
296
+ .min(0)
297
+ .max(1000)
298
+ .step(1)
274
299
  .description('Maximum consecutive image generations per session without user interaction. 0 disables protection.')
275
300
  .default(3)
276
301
  .volatile(),