imagegen-smarto 0.2.0 → 0.2.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/README.md CHANGED
@@ -115,6 +115,12 @@ The command handles its relay protocol internally; the skill only prepares the
115
115
  final image prompt, supplies reference-image paths, and consumes the returned
116
116
  image result.
117
117
 
118
+ While the relay is producing an image, the command writes a periodic status
119
+ heartbeat with elapsed time to stderr. This is a waiting indicator, not an
120
+ upstream completion percentage. The final `IMAGE_PATH=` and `IMAGE_MARKDOWN=`
121
+ records remain on stdout. Pass `--quiet` to suppress status messages while
122
+ keeping those final records.
123
+
118
124
  ## Local profile switch
119
125
 
120
126
  The two profiles used by the author are:
package/bin/generate.js CHANGED
@@ -8,6 +8,17 @@ const path = require('node:path')
8
8
 
9
9
  const IMAGEGEN_MARKER = '__CODEX_VPS_IMAGEGEN__'
10
10
  const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000
11
+ const DEFAULT_PROGRESS_INTERVAL_MS = 15 * 1000
12
+
13
+ function progressIntervalMs() {
14
+ const configured = Number(process.env.IMAGEGEN_SMARTO_PROGRESS_INTERVAL_MS)
15
+ if (Number.isFinite(configured) && configured > 0) return configured
16
+ return DEFAULT_PROGRESS_INTERVAL_MS
17
+ }
18
+
19
+ function reportProgress(message, quiet) {
20
+ if (!quiet) console.error(`imagegen-smarto: ${message}`)
21
+ }
11
22
 
12
23
  function printGenerateHelp() {
13
24
  console.log(`imagegen-smarto generate - generate or edit an image through the active relay
@@ -23,6 +34,7 @@ Options:
23
34
  --model <model> Override the model from Codex config
24
35
  --base-url <url> Override the active provider base URL
25
36
  --timeout <seconds> Request timeout (default: 600)
37
+ --quiet Suppress status messages (final paths still print)
26
38
  --help Show this help`)
27
39
  }
28
40
 
@@ -34,6 +46,7 @@ function parseGenerateArgs(argv) {
34
46
  model: null,
35
47
  baseUrl: null,
36
48
  timeoutMs: DEFAULT_TIMEOUT_MS,
49
+ quiet: false,
37
50
  }
38
51
  const positional = []
39
52
 
@@ -84,6 +97,11 @@ function parseGenerateArgs(argv) {
84
97
  continue
85
98
  }
86
99
 
100
+ if (arg === '--quiet') {
101
+ options.quiet = true
102
+ continue
103
+ }
104
+
87
105
  if (arg.startsWith('-')) {
88
106
  throw new Error(`unknown option: ${arg}`)
89
107
  }
@@ -273,7 +291,7 @@ function outputPathForIndex(requestedPath, index) {
273
291
  return `${base.slice(0, -extension.length)}-${index + 1}${extension}`
274
292
  }
275
293
 
276
- async function requestImage({ prompt, images, output, model, baseUrl, timeoutMs }) {
294
+ async function requestImage({ prompt, images, output, model, baseUrl, timeoutMs, quiet }) {
277
295
  const codexHome = resolveCodexHome()
278
296
  const active = readActiveProviderConfig(codexHome)
279
297
  const apiKey = readApiKey(codexHome)
@@ -290,6 +308,14 @@ async function requestImage({ prompt, images, output, model, baseUrl, timeoutMs
290
308
  }
291
309
  const controller = new AbortController()
292
310
  const timeout = setTimeout(() => controller.abort(), timeoutMs)
311
+ const startedAt = Date.now()
312
+ const interval = progressIntervalMs()
313
+ const elapsedSeconds = () => Math.floor((Date.now() - startedAt) / 1000)
314
+ const progressTimer = setInterval(() => {
315
+ reportProgress(`still waiting for the image relay (${elapsedSeconds()}s elapsed)`, quiet)
316
+ }, interval)
317
+ const clearProgress = () => clearInterval(progressTimer)
318
+ reportProgress('starting image generation', quiet)
293
319
  let response
294
320
  try {
295
321
  response = await fetch(normalizeResponsesUrl(baseUrl || active.baseUrl), {
@@ -303,37 +329,52 @@ async function requestImage({ prompt, images, output, model, baseUrl, timeoutMs
303
329
  })
304
330
  } catch (error) {
305
331
  clearTimeout(timeout)
332
+ clearProgress()
306
333
  if (error.name === 'AbortError') throw new Error(`image request timed out after ${timeoutMs / 1000}s`)
307
334
  throw new Error(`cannot reach the image relay: ${error.message}`)
308
335
  }
309
336
 
310
337
  if (!response.ok) {
311
338
  clearTimeout(timeout)
339
+ clearProgress()
312
340
  const errorText = (await response.text()).slice(0, 2000)
313
341
  throw new Error(`image relay returned HTTP ${response.status}: ${errorText}`)
314
342
  }
315
343
  if (!response.body) {
316
344
  clearTimeout(timeout)
345
+ clearProgress()
317
346
  throw new Error('image relay returned no response body')
318
347
  }
319
348
 
349
+ reportProgress('relay connected; waiting for streamed image result', quiet)
350
+
320
351
  const results = []
321
352
  let relayError = null
322
353
  let buffer = ''
354
+ let reader = null
355
+ let streamDone = false
356
+ let resultReported = false
323
357
  const decoder = new TextDecoder()
324
358
  const onEvent = (event) => {
325
359
  relayError ||= extractError(event)
326
360
  const item = event.type === 'response.output_item.done' ? event.item : null
327
361
  if (item?.type === 'image_generation_call' && item.result) {
328
362
  results.push(item.result)
363
+ if (!resultReported) {
364
+ resultReported = true
365
+ reportProgress(`image result received after ${elapsedSeconds()}s; finishing response`, quiet)
366
+ }
329
367
  }
330
368
  }
331
369
 
332
370
  try {
333
- const reader = response.body.getReader()
371
+ reader = response.body.getReader()
334
372
  while (true) {
335
373
  const { value, done } = await reader.read()
336
- if (done) break
374
+ if (done) {
375
+ streamDone = true
376
+ break
377
+ }
337
378
  buffer += decoder.decode(value, { stream: true })
338
379
  buffer = consumeSseText(buffer, onEvent)
339
380
  }
@@ -346,6 +387,21 @@ async function requestImage({ prompt, images, output, model, baseUrl, timeoutMs
346
387
  throw new Error(`failed while reading the image relay response: ${error.message}`)
347
388
  } finally {
348
389
  clearTimeout(timeout)
390
+ clearProgress()
391
+ if (reader && !streamDone) {
392
+ try {
393
+ await reader.cancel()
394
+ } catch {
395
+ // The original request error is more useful than cleanup failures.
396
+ }
397
+ }
398
+ if (reader) {
399
+ try {
400
+ reader.releaseLock()
401
+ } catch {
402
+ // The stream may already have released the lock after an abort.
403
+ }
404
+ }
349
405
  }
350
406
 
351
407
  if (relayError) throw new Error(relayError)
@@ -360,6 +416,7 @@ async function requestImage({ prompt, images, output, model, baseUrl, timeoutMs
360
416
  fs.writeFileSync(filePath, decodeImageResult(results[index]), { mode: 0o600 })
361
417
  paths.push(filePath)
362
418
  }
419
+ reportProgress(`saved ${paths.length} image${paths.length === 1 ? '' : 's'} in ${elapsedSeconds()}s`, quiet)
363
420
  return paths
364
421
  }
365
422
 
@@ -381,6 +438,7 @@ async function generateImage(argv) {
381
438
  model: options.model,
382
439
  baseUrl: options.baseUrl,
383
440
  timeoutMs: options.timeoutMs,
441
+ quiet: options.quiet,
384
442
  })
385
443
  for (const filePath of paths) {
386
444
  console.log(`IMAGE_PATH=${filePath}`)
@@ -34,9 +34,18 @@ reading these instructions.
34
34
 
35
35
  5. If the user named an output location, pass `--output <path>`. Do not
36
36
  overwrite an existing asset unless replacement was explicitly requested.
37
- 6. On success, include every printed `IMAGE_MARKDOWN=...` value in the answer
37
+ 6. Keep the default status heartbeat while the relay is working. Image
38
+ generation can take a minute or more; silence is not evidence of failure.
39
+ If another tool wrapper yields while the command is still running, wait for
40
+ the process to finish instead of interrupting it. Use `--quiet` only when a
41
+ caller needs machine-clean stderr.
42
+ 7. On success, include every printed `IMAGE_MARKDOWN=...` value in the answer
38
43
  so Codex displays the generated file. Report the saved path. For a failed
39
44
  command, report the actual error and do not claim that an image exists.
40
45
 
46
+ The CLI writes status heartbeats to stderr and keeps the final `IMAGE_PATH=`
47
+ and `IMAGE_MARKDOWN=` records on stdout. The heartbeat reports elapsed waiting
48
+ time; it is not an upstream completion percentage or a health guarantee.
49
+
41
50
  For edits, state invariants explicitly in the prompt: `change only X; keep Y
42
51
  unchanged`. When iterating, make one targeted change and repeat the invariants.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imagegen-smarto",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Install the imagegen-smarto Codex skill on Windows and Linux.",
5
5
  "bin": {
6
6
  "imagegen-smarto": "bin/cli.js"
@@ -11,7 +11,8 @@
11
11
  "README.md"
12
12
  ],
13
13
  "scripts": {
14
- "postinstall": "node bin/cli.js install --postinstall"
14
+ "postinstall": "node bin/cli.js install --postinstall",
15
+ "test": "node --test"
15
16
  },
16
17
  "engines": {
17
18
  "node": ">=18"