ima2-gen 1.1.9 → 1.1.10

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.
Files changed (204) hide show
  1. package/README.md +4 -0
  2. package/bin/commands/annotate.js +5 -3
  3. package/bin/commands/annotate.ts +13 -12
  4. package/bin/commands/cancel.js +5 -2
  5. package/bin/commands/cancel.ts +6 -3
  6. package/bin/commands/canvas-versions.js +4 -4
  7. package/bin/commands/canvas-versions.ts +10 -10
  8. package/bin/commands/cardnews.js +3 -1
  9. package/bin/commands/cardnews.ts +18 -17
  10. package/bin/commands/comfy.js +2 -2
  11. package/bin/commands/comfy.ts +4 -4
  12. package/bin/commands/config.ts +6 -6
  13. package/bin/commands/edit.js +11 -8
  14. package/bin/commands/edit.ts +12 -9
  15. package/bin/commands/gen.js +16 -13
  16. package/bin/commands/gen.ts +20 -17
  17. package/bin/commands/history.js +2 -1
  18. package/bin/commands/history.ts +11 -10
  19. package/bin/commands/ls.js +7 -4
  20. package/bin/commands/ls.ts +13 -10
  21. package/bin/commands/metadata.js +4 -1
  22. package/bin/commands/metadata.ts +5 -2
  23. package/bin/commands/multimode.js +4 -4
  24. package/bin/commands/multimode.ts +5 -6
  25. package/bin/commands/node.js +7 -7
  26. package/bin/commands/node.ts +14 -14
  27. package/bin/commands/observability.js +6 -6
  28. package/bin/commands/observability.ts +20 -20
  29. package/bin/commands/ping.js +4 -2
  30. package/bin/commands/ping.ts +5 -3
  31. package/bin/commands/prompt.js +14 -11
  32. package/bin/commands/prompt.ts +40 -38
  33. package/bin/commands/ps.js +9 -6
  34. package/bin/commands/ps.ts +14 -11
  35. package/bin/commands/session.js +4 -3
  36. package/bin/commands/session.ts +23 -22
  37. package/bin/commands/show.js +5 -2
  38. package/bin/commands/show.ts +8 -5
  39. package/bin/ima2.js +4 -4
  40. package/bin/ima2.ts +6 -6
  41. package/bin/lib/args.ts +20 -1
  42. package/bin/lib/client.ts +20 -7
  43. package/bin/lib/error-hints.ts +3 -3
  44. package/bin/lib/files.ts +8 -8
  45. package/bin/lib/output.js +19 -17
  46. package/bin/lib/output.ts +38 -23
  47. package/bin/lib/platform.js +3 -1
  48. package/bin/lib/platform.ts +9 -7
  49. package/bin/lib/star-prompt.ts +1 -1
  50. package/bin/lib/storage-doctor.ts +3 -2
  51. package/config.js +1 -1
  52. package/config.ts +8 -6
  53. package/docs/migration/runtime-test-inventory.md +135 -0
  54. package/lib/assetLifecycle.ts +8 -8
  55. package/lib/canvasVersionStore.ts +52 -12
  56. package/lib/cardNewsGenerator.js +7 -3
  57. package/lib/cardNewsGenerator.ts +117 -14
  58. package/lib/cardNewsJobStore.ts +47 -12
  59. package/lib/cardNewsManifestStore.js +13 -6
  60. package/lib/cardNewsManifestStore.ts +56 -14
  61. package/lib/cardNewsPlanner.js +11 -9
  62. package/lib/cardNewsPlanner.ts +86 -30
  63. package/lib/cardNewsPlannerClient.js +23 -10
  64. package/lib/cardNewsPlannerClient.ts +58 -17
  65. package/lib/cardNewsPlannerSchema.js +43 -36
  66. package/lib/cardNewsPlannerSchema.ts +120 -58
  67. package/lib/cardNewsTemplateStore.js +20 -35
  68. package/lib/cardNewsTemplateStore.ts +100 -58
  69. package/lib/codexDetect.js +5 -3
  70. package/lib/codexDetect.ts +5 -3
  71. package/lib/comfyBridge.js +3 -1
  72. package/lib/comfyBridge.ts +37 -16
  73. package/lib/db.ts +5 -5
  74. package/lib/errInfo.js +32 -0
  75. package/lib/errInfo.ts +43 -0
  76. package/lib/errorClassify.ts +2 -2
  77. package/lib/generationErrors.ts +37 -11
  78. package/lib/historyList.js +10 -6
  79. package/lib/historyList.ts +17 -13
  80. package/lib/imageMetadata.js +1 -1
  81. package/lib/imageMetadata.ts +8 -8
  82. package/lib/imageMetadataStore.ts +6 -6
  83. package/lib/imageModels.ts +6 -4
  84. package/lib/inflight.js +1 -8
  85. package/lib/inflight.ts +59 -16
  86. package/lib/localImportStore.ts +6 -5
  87. package/lib/logger.js +7 -5
  88. package/lib/logger.ts +34 -23
  89. package/lib/nodeStore.ts +18 -10
  90. package/lib/oauthLauncher.js +2 -2
  91. package/lib/oauthLauncher.ts +7 -6
  92. package/lib/oauthNormalize.ts +1 -1
  93. package/lib/oauthProxy/errors.js +93 -0
  94. package/lib/oauthProxy/errors.ts +128 -0
  95. package/lib/oauthProxy/generators.js +426 -0
  96. package/lib/oauthProxy/generators.ts +494 -0
  97. package/lib/oauthProxy/index.js +8 -0
  98. package/lib/oauthProxy/index.ts +28 -0
  99. package/lib/oauthProxy/prompts.js +84 -0
  100. package/lib/oauthProxy/prompts.ts +108 -0
  101. package/lib/oauthProxy/references.js +32 -0
  102. package/lib/oauthProxy/references.ts +45 -0
  103. package/lib/oauthProxy/runtime.js +101 -0
  104. package/lib/oauthProxy/runtime.ts +115 -0
  105. package/lib/oauthProxy/streams.js +211 -0
  106. package/lib/oauthProxy/streams.ts +232 -0
  107. package/lib/oauthProxy/types.js +6 -0
  108. package/lib/oauthProxy/types.ts +9 -0
  109. package/lib/oauthProxy.js +3 -911
  110. package/lib/oauthProxy.ts +3 -995
  111. package/lib/openDirectory.js +4 -2
  112. package/lib/openDirectory.ts +8 -6
  113. package/lib/pngInfo.ts +2 -2
  114. package/lib/promptImport/curatedSources.ts +4 -2
  115. package/lib/promptImport/discoveryRegistry.js +17 -9
  116. package/lib/promptImport/discoveryRegistry.ts +121 -28
  117. package/lib/promptImport/errors.ts +6 -6
  118. package/lib/promptImport/githubDiscovery.js +13 -7
  119. package/lib/promptImport/githubDiscovery.ts +84 -23
  120. package/lib/promptImport/githubFolder.js +22 -14
  121. package/lib/promptImport/githubFolder.ts +130 -41
  122. package/lib/promptImport/githubSource.js +3 -1
  123. package/lib/promptImport/githubSource.ts +32 -14
  124. package/lib/promptImport/gptImageHints.ts +10 -8
  125. package/lib/promptImport/parsePromptCandidates.js +2 -0
  126. package/lib/promptImport/parsePromptCandidates.ts +43 -17
  127. package/lib/promptImport/promptIndex.js +33 -23
  128. package/lib/promptImport/promptIndex.ts +124 -46
  129. package/lib/promptImport/rankPromptCandidates.js +2 -2
  130. package/lib/promptImport/rankPromptCandidates.ts +22 -6
  131. package/lib/promptImport/types.js +1 -0
  132. package/lib/promptImport/types.ts +103 -0
  133. package/lib/promptSafetyPolicy.js +5 -0
  134. package/lib/promptSafetyPolicy.ts +5 -0
  135. package/lib/providerOptions.ts +3 -2
  136. package/lib/referenceImageCompress.ts +15 -6
  137. package/lib/refs.js +2 -0
  138. package/lib/refs.ts +27 -11
  139. package/lib/requestLogger.ts +4 -3
  140. package/lib/responsesImageAdapter.js +16 -10
  141. package/lib/responsesImageAdapter.ts +109 -31
  142. package/lib/runtimeContext.js +100 -0
  143. package/lib/runtimeContext.ts +131 -0
  144. package/lib/runtimePorts.js +2 -1
  145. package/lib/runtimePorts.ts +28 -16
  146. package/lib/sessionStore.js +7 -5
  147. package/lib/sessionStore.ts +73 -37
  148. package/lib/storageMigration.js +18 -11
  149. package/lib/storageMigration.ts +63 -37
  150. package/lib/styleSheet.js +7 -6
  151. package/lib/styleSheet.ts +34 -23
  152. package/package.json +4 -2
  153. package/routes/annotations.js +9 -4
  154. package/routes/annotations.ts +35 -12
  155. package/routes/canvasVersions.js +8 -3
  156. package/routes/canvasVersions.ts +14 -9
  157. package/routes/cardNews.js +31 -18
  158. package/routes/cardNews.ts +66 -38
  159. package/routes/comfy.js +6 -3
  160. package/routes/comfy.ts +10 -6
  161. package/routes/edit.js +9 -5
  162. package/routes/edit.ts +30 -10
  163. package/routes/generate.js +33 -21
  164. package/routes/generate.ts +43 -29
  165. package/routes/health.js +7 -3
  166. package/routes/health.ts +15 -10
  167. package/routes/history.js +29 -14
  168. package/routes/history.ts +41 -23
  169. package/routes/imageImport.js +6 -2
  170. package/routes/imageImport.ts +9 -5
  171. package/routes/index.js +3 -1
  172. package/routes/index.ts +4 -1
  173. package/routes/metadata.js +9 -4
  174. package/routes/metadata.ts +13 -7
  175. package/routes/multimode.js +17 -11
  176. package/routes/multimode.ts +40 -19
  177. package/routes/nodes.js +30 -20
  178. package/routes/nodes.ts +73 -34
  179. package/routes/promptImport.js +42 -36
  180. package/routes/promptImport.ts +89 -64
  181. package/routes/prompts.js +62 -39
  182. package/routes/prompts.ts +120 -71
  183. package/routes/sessions.js +46 -24
  184. package/routes/sessions.ts +60 -35
  185. package/routes/storage.js +4 -2
  186. package/routes/storage.ts +13 -5
  187. package/server.js +25 -21
  188. package/server.ts +57 -37
  189. package/ui/dist/.vite/manifest.json +11 -10
  190. package/ui/dist/assets/{CardNewsWorkspace-BJOCey7Z.js → CardNewsWorkspace-C9Cpxuxc.js} +1 -1
  191. package/ui/dist/assets/{NodeCanvas-C3dzYNsk.js → NodeCanvas-BllpfcQW.js} +1 -1
  192. package/ui/dist/assets/{PromptImportDialog-Dqu1VpUh.js → PromptImportDialog-D8EMO--u.js} +2 -2
  193. package/ui/dist/assets/{PromptImportDiscoverySection-Dg8T9X0L.js → PromptImportDiscoverySection-BB2FrKuq.js} +1 -1
  194. package/ui/dist/assets/{PromptImportFolderSection-DBaqsFO4.js → PromptImportFolderSection-aVteBUcb.js} +1 -1
  195. package/ui/dist/assets/PromptLibraryPanel-Z-4B8RSs.js +2 -0
  196. package/ui/dist/assets/SettingsWorkspace-DBYdgpPI.js +1 -0
  197. package/ui/dist/assets/index-B2TDuGqy.css +1 -0
  198. package/ui/dist/assets/index-DFlbOIxI.js +25 -0
  199. package/ui/dist/assets/{index-Cvld7dUZ.js → index-Deo5wBiA.js} +1 -1
  200. package/ui/dist/index.html +2 -2
  201. package/ui/dist/assets/PromptLibraryPanel-p5QqR97M.js +0 -2
  202. package/ui/dist/assets/SettingsWorkspace-B5bSAZ6u.js +0 -1
  203. package/ui/dist/assets/index-C9cXwiWE.js +0 -25
  204. package/ui/dist/assets/index-CGMIkZXn.css +0 -1
@@ -4,6 +4,7 @@ import { fileToDataUri, dataUriToFile, defaultOutName } from "../lib/files.js";
4
4
  import { out, die, dieWithError, color, json } from "../lib/output.js";
5
5
  import { config } from "../../config.js";
6
6
 
7
+ import { errInfo } from "../../lib/errInfo.js";
7
8
  const VALID_MODES = new Set(["auto", "direct"]);
8
9
  const VALID_MODERATION = new Set(["auto", "low"]);
9
10
  const KNOWN_IMAGE_MODELS = new Set(["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark"]);
@@ -47,19 +48,19 @@ const HELP = `
47
48
  --web-search / --no-web-search Override default web-search toggle
48
49
  `;
49
50
 
50
- export default async function editCmd(argv) {
51
+ export default async function editCmd(argv: string[]) {
51
52
  const args = parseArgs(argv, SPEC);
52
53
  if (args.help) { out(HELP); return; }
53
54
  const input = args.positional[0];
54
55
  if (!input) die(2, "input image path required");
55
56
  if (!args.prompt) die(2, "--prompt is required");
56
- if (!VALID_MODES.has(args.mode)) die(2, "--mode must be one of: auto, direct");
57
- if (!VALID_MODERATION.has(args.moderation)) die(2, "--moderation must be one of: auto, low");
58
- if (args.model && !KNOWN_IMAGE_MODELS.has(args.model)) {
57
+ if (!VALID_MODES.has(String(args.mode))) die(2, "--mode must be one of: auto, direct");
58
+ if (!VALID_MODERATION.has(String(args.moderation))) die(2, "--moderation must be one of: auto, low");
59
+ if (args.model && !KNOWN_IMAGE_MODELS.has(String(args.model))) {
59
60
  die(2, "--model must be one of: gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex-spark");
60
61
  }
61
62
  const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh"]);
62
- if (args["reasoning-effort"] && !VALID_REASONING.has(args["reasoning-effort"])) {
63
+ if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
63
64
  die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh");
64
65
  }
65
66
  if (args["web-search"] && args["no-web-search"]) {
@@ -69,14 +70,15 @@ export default async function editCmd(argv) {
69
70
  let server;
70
71
  try { server = await resolveServer({ serverFlag: args.server }); }
71
72
  catch (e) {
72
- if (args.json) json({ ok: false, error: e.message, code: e.code, status: e.status });
73
+ const err = errInfo(e);
74
+ if (args.json) json({ ok: false, error: err.message, code: err.code, status: err.status });
73
75
  dieWithError(e);
74
76
  }
75
77
 
76
78
  const imageDataUri = await fileToDataUri(input);
77
79
  const imageB64 = imageDataUri.split(",")[1];
78
80
 
79
- const timeoutMs = (parseInt(args.timeout) || 180) * 1000;
81
+ const timeoutMs = (parseInt(String(args.timeout)) || 180) * 1000;
80
82
  let resp;
81
83
  try {
82
84
  const editBody: any = {
@@ -98,14 +100,15 @@ export default async function editCmd(argv) {
98
100
  timeoutMs,
99
101
  });
100
102
  } catch (e) {
101
- if (args.json) json({ ok: false, error: e.message, code: e.code });
103
+ const err = errInfo(e);
104
+ if (args.json) json({ ok: false, error: err.message, code: err.code });
102
105
  dieWithError(e);
103
106
  }
104
107
 
105
108
  const image = resp.image;
106
109
  if (!image) die(1, "server returned no image");
107
110
  const target = args.out || `${config.storage.generatedDir}/${defaultOutName(0, 1)}`;
108
- await dataUriToFile(image, target);
111
+ await dataUriToFile(image, String(target));
109
112
 
110
113
  if (args.json) {
111
114
  json({ ok: true, path: target, requestId: resp.requestId, elapsed: resp.elapsed });
@@ -3,6 +3,7 @@ import { resolveServer, request, normalizeGenerate } from "../lib/client.js";
3
3
  import { fileToDataUri, dataUriToFile, defaultOutName, readStdin } from "../lib/files.js";
4
4
  import { out, die, dieWithError, color, json } from "../lib/output.js";
5
5
  import { config } from "../../config.js";
6
+ import { errInfo } from "../../lib/errInfo.js";
6
7
  const VALID_MODES = new Set(["auto", "direct"]);
7
8
  const VALID_MODERATION = new Set(["auto", "low"]);
8
9
  const KNOWN_IMAGE_MODELS = new Set(["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark"]);
@@ -75,32 +76,33 @@ export default async function genCmd(argv) {
75
76
  }
76
77
  if (!prompt)
77
78
  die(2, "prompt is required (positional or via --stdin)");
78
- const refs = args.ref || [];
79
+ const refs = (Array.isArray(args.ref) ? args.ref : []);
79
80
  if (refs.length > 5)
80
81
  die(2, "max 5 --ref attachments");
81
- if (!VALID_MODES.has(args.mode))
82
+ if (!VALID_MODES.has(String(args.mode)))
82
83
  die(2, "--mode must be one of: auto, direct");
83
- if (!VALID_MODERATION.has(args.moderation))
84
+ if (!VALID_MODERATION.has(String(args.moderation)))
84
85
  die(2, "--moderation must be one of: auto, low");
85
- if (args.model && !KNOWN_IMAGE_MODELS.has(args.model)) {
86
+ if (args.model && !KNOWN_IMAGE_MODELS.has(String(args.model))) {
86
87
  die(2, "--model must be one of: gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex-spark");
87
88
  }
88
89
  const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh"]);
89
- if (args["reasoning-effort"] && !VALID_REASONING.has(args["reasoning-effort"])) {
90
+ if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
90
91
  die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh");
91
92
  }
92
93
  if (args["web-search"] && args["no-web-search"]) {
93
94
  die(2, "--web-search and --no-web-search are mutually exclusive");
94
95
  }
95
- const n = Math.max(1, Math.min(8, parseInt(args.count) || 1));
96
- const timeoutMs = (parseInt(args.timeout) || 180) * 1000;
96
+ const n = Math.max(1, Math.min(8, parseInt(String(args.count)) || 1));
97
+ const timeoutMs = (parseInt(String(args.timeout)) || 180) * 1000;
97
98
  let server;
98
99
  try {
99
100
  server = await resolveServer({ serverFlag: args.server });
100
101
  }
101
102
  catch (e) {
103
+ const err = errInfo(e);
102
104
  if (args.json)
103
- json({ ok: false, error: e.message, code: e.code, status: e.status });
105
+ json({ ok: false, error: err.message, code: err.code, status: err.status });
104
106
  dieWithError(e);
105
107
  }
106
108
  const references = await Promise.all(refs.map((p) => fileToDataUri(p)));
@@ -126,8 +128,9 @@ export default async function genCmd(argv) {
126
128
  resp = await request(server.base, "/api/generate", { method: "POST", body, timeoutMs });
127
129
  }
128
130
  catch (e) {
131
+ const err = errInfo(e);
129
132
  if (args.json)
130
- json({ ok: false, error: e.message, code: e.code, status: e.status });
133
+ json({ ok: false, error: err.message, code: err.code, status: err.status });
131
134
  dieWithError(e);
132
135
  }
133
136
  const norm = normalizeGenerate(resp);
@@ -135,7 +138,7 @@ export default async function genCmd(argv) {
135
138
  die(1, "server returned no images");
136
139
  // --no-save path
137
140
  if (args["no-save"]) {
138
- const totalBytes = norm.images.reduce((s, im) => s + im.image.length, 0);
141
+ const totalBytes = norm.images.reduce((s, im) => s + (im.image?.length ?? 0), 0);
139
142
  if (process.stdout.isTTY && totalBytes > 2 * 1024 * 1024 && !args.force) {
140
143
  die(2, "refusing to print >2MB of b64 to TTY; use --force or drop --no-save");
141
144
  }
@@ -144,8 +147,8 @@ export default async function genCmd(argv) {
144
147
  return;
145
148
  }
146
149
  // Save path
147
- const outDir = args["out-dir"] || null;
148
- const explicitOut = args.out || null;
150
+ const outDir = args["out-dir"] ? String(args["out-dir"]) : null;
151
+ const explicitOut = args.out ? String(args.out) : null;
149
152
  if (explicitOut && norm.images.length > 1) {
150
153
  die(2, "--out only supports a single image; use --out-dir for n>1");
151
154
  }
@@ -162,7 +165,7 @@ export default async function genCmd(argv) {
162
165
  else {
163
166
  target = `${config.storage.generatedDir}/${defaultOutName(i, norm.images.length)}`;
164
167
  }
165
- await dataUriToFile(im.image, target);
168
+ await dataUriToFile(String(im.image), target);
166
169
  savedPaths.push(target);
167
170
  }
168
171
  if (args.json) {
@@ -4,6 +4,7 @@ import { fileToDataUri, dataUriToFile, defaultOutName, readStdin } from "../lib/
4
4
  import { out, die, dieWithError, color, json } from "../lib/output.js";
5
5
  import { config } from "../../config.js";
6
6
 
7
+ import { errInfo } from "../../lib/errInfo.js";
7
8
  const VALID_MODES = new Set(["auto", "direct"]);
8
9
  const VALID_MODERATION = new Set(["auto", "low"]);
9
10
  const KNOWN_IMAGE_MODELS = new Set(["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark"]);
@@ -65,7 +66,7 @@ const HELP = `
65
66
  cat prompt.txt | ima2 gen --stdin -n 2 -d ./out
66
67
  `;
67
68
 
68
- export default async function genCmd(argv) {
69
+ export default async function genCmd(argv: string[]) {
69
70
  const args = parseArgs(argv, SPEC);
70
71
  if (args.help) { out(HELP); return; }
71
72
 
@@ -76,33 +77,34 @@ export default async function genCmd(argv) {
76
77
  }
77
78
  if (!prompt) die(2, "prompt is required (positional or via --stdin)");
78
79
 
79
- const refs = args.ref || [];
80
+ const refs = (Array.isArray(args.ref) ? args.ref : []) as string[];
80
81
  if (refs.length > 5) die(2, "max 5 --ref attachments");
81
- if (!VALID_MODES.has(args.mode)) die(2, "--mode must be one of: auto, direct");
82
- if (!VALID_MODERATION.has(args.moderation)) die(2, "--moderation must be one of: auto, low");
83
- if (args.model && !KNOWN_IMAGE_MODELS.has(args.model)) {
82
+ if (!VALID_MODES.has(String(args.mode))) die(2, "--mode must be one of: auto, direct");
83
+ if (!VALID_MODERATION.has(String(args.moderation))) die(2, "--moderation must be one of: auto, low");
84
+ if (args.model && !KNOWN_IMAGE_MODELS.has(String(args.model))) {
84
85
  die(2, "--model must be one of: gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex-spark");
85
86
  }
86
87
  const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh"]);
87
- if (args["reasoning-effort"] && !VALID_REASONING.has(args["reasoning-effort"])) {
88
+ if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
88
89
  die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh");
89
90
  }
90
91
  if (args["web-search"] && args["no-web-search"]) {
91
92
  die(2, "--web-search and --no-web-search are mutually exclusive");
92
93
  }
93
94
 
94
- const n = Math.max(1, Math.min(8, parseInt(args.count) || 1));
95
- const timeoutMs = (parseInt(args.timeout) || 180) * 1000;
95
+ const n = Math.max(1, Math.min(8, parseInt(String(args.count)) || 1));
96
+ const timeoutMs = (parseInt(String(args.timeout)) || 180) * 1000;
96
97
 
97
98
  let server;
98
99
  try {
99
100
  server = await resolveServer({ serverFlag: args.server });
100
101
  } catch (e) {
101
- if (args.json) json({ ok: false, error: e.message, code: e.code, status: e.status });
102
+ const err = errInfo(e);
103
+ if (args.json) json({ ok: false, error: err.message, code: err.code, status: err.status });
102
104
  dieWithError(e);
103
105
  }
104
106
 
105
- const references = await Promise.all(refs.map((p) => fileToDataUri(p)));
107
+ const references = await Promise.all(refs.map((p: string) => fileToDataUri(p)));
106
108
 
107
109
  const body: any = {
108
110
  prompt,
@@ -123,7 +125,8 @@ export default async function genCmd(argv) {
123
125
  try {
124
126
  resp = await request(server.base, "/api/generate", { method: "POST", body, timeoutMs });
125
127
  } catch (e) {
126
- if (args.json) json({ ok: false, error: e.message, code: e.code, status: e.status });
128
+ const err = errInfo(e);
129
+ if (args.json) json({ ok: false, error: err.message, code: err.code, status: err.status });
127
130
  dieWithError(e);
128
131
  }
129
132
 
@@ -132,7 +135,7 @@ export default async function genCmd(argv) {
132
135
 
133
136
  // --no-save path
134
137
  if (args["no-save"]) {
135
- const totalBytes = norm.images.reduce((s, im) => s + im.image.length, 0);
138
+ const totalBytes = norm.images.reduce((s: number, im) => s + (im.image?.length ?? 0), 0);
136
139
  if (process.stdout.isTTY && totalBytes > 2 * 1024 * 1024 && !args.force) {
137
140
  die(2, "refusing to print >2MB of b64 to TTY; use --force or drop --no-save");
138
141
  }
@@ -141,16 +144,16 @@ export default async function genCmd(argv) {
141
144
  }
142
145
 
143
146
  // Save path
144
- const outDir = args["out-dir"] || null;
145
- const explicitOut = args.out || null;
147
+ const outDir = args["out-dir"] ? String(args["out-dir"]) : null;
148
+ const explicitOut = args.out ? String(args.out) : null;
146
149
  if (explicitOut && norm.images.length > 1) {
147
150
  die(2, "--out only supports a single image; use --out-dir for n>1");
148
151
  }
149
152
 
150
- const savedPaths = [];
153
+ const savedPaths: string[] = [];
151
154
  for (let i = 0; i < norm.images.length; i++) {
152
155
  const im = norm.images[i];
153
- let target;
156
+ let target: string;
154
157
  if (explicitOut) {
155
158
  target = explicitOut;
156
159
  } else if (outDir) {
@@ -158,7 +161,7 @@ export default async function genCmd(argv) {
158
161
  } else {
159
162
  target = `${config.storage.generatedDir}/${defaultOutName(i, norm.images.length)}`;
160
163
  }
161
- await dataUriToFile(im.image, target);
164
+ await dataUriToFile(String(im.image), target);
162
165
  savedPaths.push(target);
163
166
  }
164
167
 
@@ -36,7 +36,8 @@ async function getServer(args) {
36
36
  }
37
37
  }
38
38
  function handle(e) {
39
- die(exitCodeForError(e), `${e.message}${e.code ? ` (${e.code})` : ""}`);
39
+ const err = e;
40
+ die(exitCodeForError(e), `${err.message}${err.code ? ` (${err.code})` : ""}`);
40
41
  }
41
42
  async function readLine() {
42
43
  return new Promise((resolve) => {
@@ -1,6 +1,6 @@
1
1
  import { readFile } from "fs/promises";
2
2
  import { extname, basename } from "path";
3
- import { parseArgs } from "../lib/args.js";
3
+ import { parseArgs, type ParsedArgs } from "../lib/args.js";
4
4
  import { resolveServer, request } from "../lib/client.js";
5
5
  import { out, die, color, json, exitCodeForError } from "../lib/output.js";
6
6
  import { getCliBrowserId } from "../lib/browser-id.js";
@@ -29,20 +29,21 @@ const COMMON_FLAGS = {
29
29
  help: { short: "h", type: "boolean" },
30
30
  };
31
31
 
32
- async function getServer(args) {
32
+ async function getServer(args: ParsedArgs) {
33
33
  try { return await resolveServer({ serverFlag: args.server }); }
34
34
  catch (e: any) { die(exitCodeForError(e), e.message); throw e; }
35
35
  }
36
36
 
37
- function handle(e) {
38
- die(exitCodeForError(e), `${e.message}${e.code ? ` (${e.code})` : ""}`);
37
+ function handle(e: unknown) {
38
+ const err = e as { message?: string; code?: string };
39
+ die(exitCodeForError(e), `${err.message}${err.code ? ` (${err.code})` : ""}`);
39
40
  }
40
41
 
41
42
  async function readLine(): Promise<string> {
42
43
  return new Promise((resolve) => {
43
44
  let buf = "";
44
45
  process.stdin.setEncoding("utf-8");
45
- const onData = (chunk) => {
46
+ const onData = (chunk: Buffer | string) => {
46
47
  buf += chunk;
47
48
  const nl = buf.indexOf("\n");
48
49
  if (nl !== -1) {
@@ -56,7 +57,7 @@ async function readLine(): Promise<string> {
56
57
  });
57
58
  }
58
59
 
59
- async function rmSub(argv) {
60
+ async function rmSub(argv: string[]) {
60
61
  const args = parseArgs(argv, { flags: COMMON_FLAGS });
61
62
  const filename = args.positional[0];
62
63
  if (!filename) die(2, "filename required");
@@ -76,7 +77,7 @@ async function rmSub(argv) {
76
77
  if (resp?.trashId) out(color.dim(` trashId: ${resp.trashId}`));
77
78
  }
78
79
 
79
- async function restoreSub(argv) {
80
+ async function restoreSub(argv: string[]) {
80
81
  const args = parseArgs(argv, { flags: COMMON_FLAGS });
81
82
  const filename = args.positional[0];
82
83
  if (!filename) die(2, "filename required");
@@ -91,7 +92,7 @@ async function restoreSub(argv) {
91
92
  out(color.green("✓ restored"));
92
93
  }
93
94
 
94
- async function favoriteSub(argv) {
95
+ async function favoriteSub(argv: string[]) {
95
96
  const args = parseArgs(argv, { flags: COMMON_FLAGS });
96
97
  const filename = args.positional[0];
97
98
  if (!filename) die(2, "filename required");
@@ -106,7 +107,7 @@ async function favoriteSub(argv) {
106
107
  out(color.green(resp.isFavorite ? "✓ favorited" : "✓ unfavorited"));
107
108
  }
108
109
 
109
- async function importSub(argv) {
110
+ async function importSub(argv: string[]) {
110
111
  const args = parseArgs(argv, { flags: COMMON_FLAGS });
111
112
  const filepath = args.positional[0];
112
113
  if (!filepath) die(2, "filename required");
@@ -136,7 +137,7 @@ const SUB: Record<string, (argv: any[]) => Promise<void>> = {
136
137
  import: importSub,
137
138
  };
138
139
 
139
- export default async function historyCmd(argv) {
140
+ export default async function historyCmd(argv: string[]) {
140
141
  const sub = argv[0];
141
142
  if (!sub || sub === "--help" || sub === "-h") { out(HELP); return; }
142
143
  const handler = SUB[sub];
@@ -1,6 +1,7 @@
1
1
  import { parseArgs } from "../lib/args.js";
2
2
  import { resolveServer, request } from "../lib/client.js";
3
3
  import { out, die, color, json, table, exitCodeForError } from "../lib/output.js";
4
+ import { errInfo } from "../../lib/errInfo.js";
4
5
  const SPEC = {
5
6
  flags: {
6
7
  count: { short: "n", type: "string", default: "20" },
@@ -22,12 +23,13 @@ export default async function lsCmd(argv) {
22
23
  server = await resolveServer({ serverFlag: args.server });
23
24
  }
24
25
  catch (e) {
25
- die(exitCodeForError(e), e.message);
26
+ const err = errInfo(e);
27
+ die(exitCodeForError(e), err.message);
26
28
  }
27
- const limit = parseInt(args.count) || 20;
29
+ const limit = parseInt(String(args.count)) || 20;
28
30
  const qs = new URLSearchParams();
29
31
  if (args.session)
30
- qs.set("sessionId", args.session);
32
+ qs.set("sessionId", String(args.session));
31
33
  qs.set("limit", String(Math.max(limit, args.favorites ? 200 : limit)));
32
34
  const path = `/api/history?${qs.toString()}`;
33
35
  let resp;
@@ -35,7 +37,8 @@ export default async function lsCmd(argv) {
35
37
  resp = await request(server.base, path);
36
38
  }
37
39
  catch (e) {
38
- die(exitCodeForError(e), e.message);
40
+ const err = errInfo(e);
41
+ die(exitCodeForError(e), err.message);
39
42
  }
40
43
  let items = (resp.items || resp.history || []);
41
44
  if (args.favorites)
@@ -2,6 +2,7 @@ import { parseArgs } from "../lib/args.js";
2
2
  import { resolveServer, request } from "../lib/client.js";
3
3
  import { out, die, color, json, table, exitCodeForError } from "../lib/output.js";
4
4
 
5
+ import { errInfo } from "../../lib/errInfo.js";
5
6
  const SPEC = {
6
7
  flags: {
7
8
  count: { short: "n", type: "string", default: "20" },
@@ -13,7 +14,7 @@ const SPEC = {
13
14
  },
14
15
  };
15
16
 
16
- export default async function lsCmd(argv) {
17
+ export default async function lsCmd(argv: string[]) {
17
18
  const args = parseArgs(argv, SPEC);
18
19
  if (args.help) {
19
20
  out("ima2 ls [-n count] [--session <id>] [--favorites] [--json]");
@@ -22,19 +23,21 @@ export default async function lsCmd(argv) {
22
23
 
23
24
  let server;
24
25
  try { server = await resolveServer({ serverFlag: args.server }); }
25
- catch (e) { die(exitCodeForError(e), e.message); }
26
+ catch (e) {
27
+ const err = errInfo(e); die(exitCodeForError(e), err.message); }
26
28
 
27
- const limit = parseInt(args.count) || 20;
29
+ const limit = parseInt(String(args.count)) || 20;
28
30
  const qs = new URLSearchParams();
29
- if (args.session) qs.set("sessionId", args.session);
31
+ if (args.session) qs.set("sessionId", String(args.session));
30
32
  qs.set("limit", String(Math.max(limit, args.favorites ? 200 : limit)));
31
33
  const path = `/api/history?${qs.toString()}`;
32
34
  let resp;
33
35
  try { resp = await request(server.base, path); }
34
- catch (e) { die(exitCodeForError(e), e.message); }
36
+ catch (e) {
37
+ const err = errInfo(e); die(exitCodeForError(e), err.message); }
35
38
 
36
- let items = (resp.items || resp.history || []);
37
- if (args.favorites) items = items.filter((it) => it.isFavorite === true);
39
+ let items: Record<string, unknown>[] = (resp.items || resp.history || []);
40
+ if (args.favorites) items = items.filter((it: Record<string, unknown>) => it.isFavorite === true);
38
41
  items = items.slice(0, limit);
39
42
 
40
43
  if (args.json) { json({ items }); return; }
@@ -47,12 +50,12 @@ export default async function lsCmd(argv) {
47
50
  { key: "filename", label: "FILENAME" },
48
51
  { key: "quality", label: "Q" },
49
52
  { key: "size", label: "SIZE" },
50
- { key: "createdAt", label: "WHEN", format: (v) => {
53
+ { key: "createdAt", label: "WHEN", format: (v: unknown) => {
51
54
  if (!v) return "";
52
- const d = new Date(v);
55
+ const d = new Date(v as string);
53
56
  return d.toISOString().replace("T", " ").slice(0, 19);
54
57
  } },
55
- { key: "prompt", label: "PROMPT", format: (v) => {
58
+ { key: "prompt", label: "PROMPT", format: (v: unknown) => {
56
59
  const s = String(v || "").replace(/\s+/g, " ");
57
60
  return s.length > 48 ? s.slice(0, 45) + "…" : s;
58
61
  } },
@@ -36,7 +36,10 @@ export default async function metadataCmd(argv) {
36
36
  const resp = await request(server.base, "/api/metadata/read", {
37
37
  method: "POST",
38
38
  body: { dataUrl },
39
- }).catch((e) => die(exitCodeForError(e), `${e.message}${e.code ? ` (${e.code})` : ""}`));
39
+ }).catch((e) => {
40
+ const err = e;
41
+ die(exitCodeForError(e), `${err.message}${err.code ? ` (${err.code})` : ""}`);
42
+ });
40
43
  if (args.json) {
41
44
  json(resp);
42
45
  }
@@ -18,7 +18,7 @@ const HELP = `
18
18
  POSTs { dataUrl } to /api/metadata/read.
19
19
  `;
20
20
 
21
- export default async function metadataCmd(argv) {
21
+ export default async function metadataCmd(argv: string[]) {
22
22
  const args = parseArgs(argv, SPEC);
23
23
  if (args.help) { out(HELP); return; }
24
24
  const file = args.positional[0];
@@ -30,7 +30,10 @@ export default async function metadataCmd(argv) {
30
30
  const resp = await request(server.base, "/api/metadata/read", {
31
31
  method: "POST",
32
32
  body: { dataUrl },
33
- }).catch((e) => die(exitCodeForError(e), `${e.message}${e.code ? ` (${e.code})` : ""}`));
33
+ }).catch((e: unknown) => {
34
+ const err = e as { message?: string; code?: string };
35
+ die(exitCodeForError(e), `${err.message}${err.code ? ` (${err.code})` : ""}`);
36
+ });
34
37
  if (args.json) { json(resp); }
35
38
  else out(JSON.stringify(resp, null, 2));
36
39
  }
@@ -54,7 +54,7 @@ export default async function multimodeCmd(argv) {
54
54
  if (!prompt)
55
55
  die(2, "prompt required");
56
56
  const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh"]);
57
- if (args["reasoning-effort"] && !VALID_REASONING.has(args["reasoning-effort"])) {
57
+ if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
58
58
  die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh");
59
59
  }
60
60
  if (args["web-search"] && args["no-web-search"]) {
@@ -68,7 +68,7 @@ export default async function multimodeCmd(argv) {
68
68
  die(exitCodeForError(e), e.message);
69
69
  throw e;
70
70
  }
71
- const maxImages = Math.max(1, Math.min(8, parseInt(args["max-images"]) || 4));
71
+ const maxImages = Math.max(1, Math.min(8, parseInt(String(args["max-images"])) || 4));
72
72
  const body = {
73
73
  prompt,
74
74
  quality: args.quality,
@@ -124,8 +124,8 @@ export default async function multimodeCmd(argv) {
124
124
  die(exitCodeForError(e), `${e.message}${e.code ? ` (${e.code})` : ""}`);
125
125
  }
126
126
  // Save images
127
- const outDir = args["out-dir"] || null;
128
- const explicitOut = args.out || null;
127
+ const outDir = args["out-dir"] ? String(args["out-dir"]) : null;
128
+ const explicitOut = args.out ? String(args.out) : null;
129
129
  if (explicitOut && images.length > 1) {
130
130
  if (!args.json)
131
131
  out(color.yellow(`(received ${images.length} images, --out only saves first)`));
@@ -1,4 +1,3 @@
1
- import { writeFile } from "fs/promises";
2
1
  import { parseArgs } from "../lib/args.js";
3
2
  import { resolveServer } from "../lib/client.js";
4
3
  import { streamSse } from "../lib/sse.js";
@@ -48,14 +47,14 @@ const HELP = `
48
47
  --timeout <sec> Default: 600
49
48
  `;
50
49
 
51
- export default async function multimodeCmd(argv) {
50
+ export default async function multimodeCmd(argv: string[]) {
52
51
  const args = parseArgs(argv, SPEC);
53
52
  if (args.help) { out(HELP); return; }
54
53
  const prompt = args.positional.join(" ");
55
54
  if (!prompt) die(2, "prompt required");
56
55
 
57
56
  const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh"]);
58
- if (args["reasoning-effort"] && !VALID_REASONING.has(args["reasoning-effort"])) {
57
+ if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
59
58
  die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh");
60
59
  }
61
60
  if (args["web-search"] && args["no-web-search"]) {
@@ -66,7 +65,7 @@ export default async function multimodeCmd(argv) {
66
65
  try { server = await resolveServer({ serverFlag: args.server }); }
67
66
  catch (e: any) { die(exitCodeForError(e), e.message); throw e; }
68
67
 
69
- const maxImages = Math.max(1, Math.min(8, parseInt(args["max-images"]) || 4));
68
+ const maxImages = Math.max(1, Math.min(8, parseInt(String(args["max-images"])) || 4));
70
69
  const body: any = {
71
70
  prompt,
72
71
  quality: args.quality,
@@ -117,8 +116,8 @@ export default async function multimodeCmd(argv) {
117
116
  }
118
117
 
119
118
  // Save images
120
- const outDir = args["out-dir"] || null;
121
- const explicitOut = args.out || null;
119
+ const outDir = args["out-dir"] ? String(args["out-dir"]) : null;
120
+ const explicitOut = args.out ? String(args.out) : null;
122
121
  if (explicitOut && images.length > 1) {
123
122
  if (!args.json) out(color.yellow(`(received ${images.length} images, --out only saves first)`));
124
123
  }
@@ -53,9 +53,9 @@ async function generateSub(argv) {
53
53
  const prompt = args.positional.join(" ");
54
54
  if (!prompt)
55
55
  die(2, "prompt required");
56
- const refs = args.ref || [];
56
+ const refs = (Array.isArray(args.ref) ? args.ref : []);
57
57
  const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh"]);
58
- if (args["reasoning-effort"] && !VALID_REASONING.has(args["reasoning-effort"])) {
58
+ if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
59
59
  die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh");
60
60
  }
61
61
  if (args["web-search"] && args["no-web-search"]) {
@@ -67,7 +67,7 @@ async function generateSub(argv) {
67
67
  prompt,
68
68
  quality: args.quality,
69
69
  size: args.size,
70
- n: Math.max(1, Math.min(8, parseInt(args.count) || 1)),
70
+ n: Math.max(1, Math.min(8, parseInt(String(args.count)) || 1)),
71
71
  references,
72
72
  moderation: args.moderation,
73
73
  sessionId: args.session,
@@ -86,8 +86,8 @@ async function generateSub(argv) {
86
86
  const resp = await request(server.base, "/api/node/generate", {
87
87
  method: "POST",
88
88
  body,
89
- timeoutMs: (parseInt(args.timeout) || 600) * 1000,
90
- }).catch((e) => die(exitCodeForError(e), `${e.message}${e.code ? ` (${e.code})` : ""}`));
89
+ timeoutMs: (parseInt(String(args.timeout)) || 600) * 1000,
90
+ }).catch((e) => { const err = e; die(exitCodeForError(e), `${err.message}${err.code ? ` (${err.code})` : ""}`); });
91
91
  if (args.json) {
92
92
  json(resp);
93
93
  return;
@@ -137,7 +137,7 @@ async function generateSub(argv) {
137
137
  if (!im.image)
138
138
  continue;
139
139
  const target = args.out && i === 0
140
- ? args.out
140
+ ? String(args.out)
141
141
  : `${config.storage.generatedDir}/${defaultOutName(i, images.length)}`;
142
142
  await dataUriToFile(im.image, target);
143
143
  savedPaths.push(target);
@@ -156,7 +156,7 @@ async function showSub(argv) {
156
156
  if (!id)
157
157
  die(2, "nodeId required");
158
158
  const server = await getServer(args);
159
- const resp = await request(server.base, `/api/node/${encodeURIComponent(id)}`).catch((e) => die(exitCodeForError(e), e.message));
159
+ const resp = await request(server.base, `/api/node/${encodeURIComponent(id)}`).catch((e) => { const err = e; die(exitCodeForError(e), err.message); });
160
160
  json(resp);
161
161
  }
162
162
  const SUB = {