ima2-gen 1.1.21 → 1.1.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.
Files changed (208) hide show
  1. package/README.md +30 -4
  2. package/bin/ima2.js +14 -4
  3. package/bin/lib/platform.js +34 -5
  4. package/docs/README.ko.md +31 -0
  5. package/lib/agentQueueWorker.js +6 -0
  6. package/lib/agentRuntime.js +3 -2
  7. package/lib/atomicWrite.js +14 -0
  8. package/lib/grokProxyLauncher.js +5 -3
  9. package/lib/inflight.js +1 -1
  10. package/lib/oauthLauncher.js +5 -0
  11. package/lib/videoFrameExtract.js +3 -3
  12. package/package.json +5 -7
  13. package/routes/edit.js +2 -1
  14. package/routes/generate.js +4 -3
  15. package/routes/health.js +4 -3
  16. package/routes/multimode.js +2 -1
  17. package/routes/video.js +4 -2
  18. package/server.js +29 -2
  19. package/ui/dist/.vite/manifest.json +12 -12
  20. package/ui/dist/assets/{AgentWorkspace-B_hq9CLg.js → AgentWorkspace-COxQ5TjU.js} +1 -1
  21. package/ui/dist/assets/{CardNewsWorkspace-wD12J7qk.js → CardNewsWorkspace-B0OkcuVz.js} +1 -1
  22. package/ui/dist/assets/{NodeCanvas-CI_wuPMf.js → NodeCanvas-BSsclEBh.js} +1 -1
  23. package/ui/dist/assets/{PromptBuilderPanel-CUTujJUV.js → PromptBuilderPanel-DpC9A5Rz.js} +1 -1
  24. package/ui/dist/assets/{PromptImportDialog-CUi66jPK.js → PromptImportDialog-CVwT0rLd.js} +2 -2
  25. package/ui/dist/assets/{PromptImportDiscoverySection-Cm3vrjY4.js → PromptImportDiscoverySection-BDCkRCRs.js} +1 -1
  26. package/ui/dist/assets/{PromptImportFolderSection-DOtWTD9n.js → PromptImportFolderSection-QoKbZD83.js} +1 -1
  27. package/ui/dist/assets/{PromptLibraryPanel-BMjQegRa.js → PromptLibraryPanel-BhFgeKnY.js} +2 -2
  28. package/ui/dist/assets/SettingsWorkspace-CfjrlH5R.js +1 -0
  29. package/ui/dist/assets/index-C-mur7pa.css +1 -0
  30. package/ui/dist/assets/index-CCP5nUOj.js +42 -0
  31. package/ui/dist/assets/{index-31uVIdt4.js → index-Cxhzi3bs.js} +1 -1
  32. package/ui/dist/index.html +2 -2
  33. package/bin/commands/annotate.ts +0 -119
  34. package/bin/commands/cancel.ts +0 -48
  35. package/bin/commands/canvas-versions.ts +0 -80
  36. package/bin/commands/capabilities.ts +0 -110
  37. package/bin/commands/cardnews.ts +0 -249
  38. package/bin/commands/comfy.ts +0 -54
  39. package/bin/commands/config.ts +0 -186
  40. package/bin/commands/defaults.ts +0 -192
  41. package/bin/commands/doctor.ts +0 -202
  42. package/bin/commands/edit.ts +0 -150
  43. package/bin/commands/gen.ts +0 -214
  44. package/bin/commands/grok.ts +0 -90
  45. package/bin/commands/history.ts +0 -146
  46. package/bin/commands/ls.ts +0 -64
  47. package/bin/commands/metadata.ts +0 -39
  48. package/bin/commands/multimode.ts +0 -196
  49. package/bin/commands/node.ts +0 -166
  50. package/bin/commands/observability.ts +0 -176
  51. package/bin/commands/ping.ts +0 -31
  52. package/bin/commands/prompt-sub/build.ts +0 -101
  53. package/bin/commands/prompt.ts +0 -492
  54. package/bin/commands/ps.ts +0 -81
  55. package/bin/commands/session.ts +0 -266
  56. package/bin/commands/show.ts +0 -72
  57. package/bin/commands/skill.ts +0 -70
  58. package/bin/commands/video.ts +0 -442
  59. package/bin/ima2.ts +0 -430
  60. package/bin/lib/args.ts +0 -92
  61. package/bin/lib/browser-id.ts +0 -16
  62. package/bin/lib/client.ts +0 -122
  63. package/bin/lib/config-store.ts +0 -120
  64. package/bin/lib/destructive-confirm.ts +0 -19
  65. package/bin/lib/doctor-checks.ts +0 -91
  66. package/bin/lib/error-hints.ts +0 -23
  67. package/bin/lib/files.ts +0 -39
  68. package/bin/lib/output.ts +0 -73
  69. package/bin/lib/platform.ts +0 -99
  70. package/bin/lib/recover-output.ts +0 -139
  71. package/bin/lib/sse.ts +0 -73
  72. package/bin/lib/star-prompt.ts +0 -97
  73. package/bin/lib/storage-doctor.ts +0 -39
  74. package/bin/lib/ui-build.ts +0 -85
  75. package/config.ts +0 -354
  76. package/lib/agentCommandParser.ts +0 -69
  77. package/lib/agentGenerationPlanner.ts +0 -273
  78. package/lib/agentQuestionResponder.ts +0 -266
  79. package/lib/agentQueueStore.ts +0 -270
  80. package/lib/agentQueueWorker.ts +0 -89
  81. package/lib/agentRuntime.ts +0 -604
  82. package/lib/agentSettings.ts +0 -72
  83. package/lib/agentStore.ts +0 -422
  84. package/lib/agentStoreRows.ts +0 -136
  85. package/lib/agentTypes.ts +0 -154
  86. package/lib/apiCachePolicy.ts +0 -11
  87. package/lib/assetLifecycle.ts +0 -146
  88. package/lib/canvasVersionStore.ts +0 -223
  89. package/lib/capabilities.ts +0 -126
  90. package/lib/cardNewsGenerator.ts +0 -271
  91. package/lib/cardNewsJobStore.ts +0 -142
  92. package/lib/cardNewsManifestStore.ts +0 -154
  93. package/lib/cardNewsPlanner.ts +0 -236
  94. package/lib/cardNewsPlannerClient.ts +0 -155
  95. package/lib/cardNewsPlannerPrompt.ts +0 -62
  96. package/lib/cardNewsPlannerSchema.ts +0 -321
  97. package/lib/cardNewsRoleTemplateStore.ts +0 -47
  98. package/lib/cardNewsTemplateStore.ts +0 -252
  99. package/lib/codexDetect.ts +0 -71
  100. package/lib/comfyBridge.ts +0 -235
  101. package/lib/composerSnapshot.ts +0 -33
  102. package/lib/configKeys.ts +0 -62
  103. package/lib/db.ts +0 -295
  104. package/lib/errInfo.ts +0 -43
  105. package/lib/errorClassify.ts +0 -100
  106. package/lib/generationCancel.ts +0 -28
  107. package/lib/generationErrors.ts +0 -238
  108. package/lib/grokImageAdapter.ts +0 -513
  109. package/lib/grokMultimodeAdapter.ts +0 -84
  110. package/lib/grokProxyLauncher.ts +0 -153
  111. package/lib/grokRuntime.ts +0 -23
  112. package/lib/grokSizeMapper.ts +0 -71
  113. package/lib/grokVideoAdapter.ts +0 -458
  114. package/lib/grokVideoCanvas.ts +0 -26
  115. package/lib/grokVideoDownload.ts +0 -59
  116. package/lib/grokVideoPlannerPrompt.ts +0 -67
  117. package/lib/historyIndex.ts +0 -51
  118. package/lib/historyList.ts +0 -181
  119. package/lib/imageMetadata.ts +0 -113
  120. package/lib/imageMetadataStore.ts +0 -67
  121. package/lib/imageModels.ts +0 -165
  122. package/lib/inflight.ts +0 -281
  123. package/lib/localImportStore.ts +0 -114
  124. package/lib/logger.ts +0 -161
  125. package/lib/nodeStore.ts +0 -91
  126. package/lib/oauthLauncher.ts +0 -94
  127. package/lib/oauthNormalize.ts +0 -30
  128. package/lib/oauthProxy/errors.ts +0 -128
  129. package/lib/oauthProxy/generators.ts +0 -494
  130. package/lib/oauthProxy/index.ts +0 -28
  131. package/lib/oauthProxy/prompts.ts +0 -123
  132. package/lib/oauthProxy/references.ts +0 -45
  133. package/lib/oauthProxy/runtime.ts +0 -115
  134. package/lib/oauthProxy/streams.ts +0 -232
  135. package/lib/oauthProxy/types.ts +0 -9
  136. package/lib/oauthProxy.ts +0 -3
  137. package/lib/openDirectory.ts +0 -47
  138. package/lib/pngInfo.ts +0 -26
  139. package/lib/promptBuilder/attachments.ts +0 -74
  140. package/lib/promptBuilder/client.ts +0 -130
  141. package/lib/promptBuilder/constants.ts +0 -9
  142. package/lib/promptBuilder/context.ts +0 -36
  143. package/lib/promptBuilder/errors.ts +0 -12
  144. package/lib/promptBuilder/requestSchema.ts +0 -56
  145. package/lib/promptBuilder/responseParser.ts +0 -219
  146. package/lib/promptBuilder/systemPrompt.ts +0 -135
  147. package/lib/promptBuilder/transport.ts +0 -94
  148. package/lib/promptBuilder/types.ts +0 -109
  149. package/lib/promptImport/curatedSources.ts +0 -141
  150. package/lib/promptImport/discoveryRegistry.ts +0 -329
  151. package/lib/promptImport/errors.ts +0 -18
  152. package/lib/promptImport/githubDiscovery.ts +0 -309
  153. package/lib/promptImport/githubFolder.ts +0 -397
  154. package/lib/promptImport/githubSource.ts +0 -257
  155. package/lib/promptImport/gptImageHints.ts +0 -70
  156. package/lib/promptImport/parsePromptCandidates.ts +0 -179
  157. package/lib/promptImport/promptIndex.ts +0 -326
  158. package/lib/promptImport/rankPromptCandidates.ts +0 -65
  159. package/lib/promptImport/types.ts +0 -103
  160. package/lib/promptSafetyPolicy.ts +0 -5
  161. package/lib/providerOptions.ts +0 -56
  162. package/lib/referenceImageCompress.ts +0 -84
  163. package/lib/refs.ts +0 -133
  164. package/lib/requestLogger.ts +0 -49
  165. package/lib/responsesDoctor.ts +0 -456
  166. package/lib/responsesErrors.ts +0 -83
  167. package/lib/responsesFallback.ts +0 -114
  168. package/lib/responsesImageAdapter.ts +0 -466
  169. package/lib/responsesParse.ts +0 -452
  170. package/lib/responsesTools.ts +0 -28
  171. package/lib/runtimeContext.ts +0 -146
  172. package/lib/runtimePorts.ts +0 -105
  173. package/lib/sessionStore.ts +0 -308
  174. package/lib/storageMigration.ts +0 -310
  175. package/lib/styleSheet.ts +0 -139
  176. package/lib/systemTrash.ts +0 -20
  177. package/lib/videoContinuity.ts +0 -180
  178. package/lib/videoFrameExtract.ts +0 -78
  179. package/lib/videoSeriesChain.ts +0 -29
  180. package/lib/visibleTextLanguagePolicy.ts +0 -7
  181. package/routes/agent.ts +0 -308
  182. package/routes/annotations.ts +0 -118
  183. package/routes/canvasVersions.ts +0 -69
  184. package/routes/capabilities.ts +0 -18
  185. package/routes/cardNews.ts +0 -211
  186. package/routes/comfy.ts +0 -43
  187. package/routes/edit.ts +0 -352
  188. package/routes/generate.ts +0 -492
  189. package/routes/grok.ts +0 -24
  190. package/routes/health.ts +0 -123
  191. package/routes/history.ts +0 -221
  192. package/routes/imageImport.ts +0 -37
  193. package/routes/index.ts +0 -52
  194. package/routes/metadata.ts +0 -77
  195. package/routes/multimode.ts +0 -499
  196. package/routes/nodes.ts +0 -578
  197. package/routes/promptBuilder.ts +0 -37
  198. package/routes/promptImport.ts +0 -379
  199. package/routes/prompts.ts +0 -428
  200. package/routes/quota.ts +0 -89
  201. package/routes/sessions.ts +0 -317
  202. package/routes/storage.ts +0 -47
  203. package/routes/video.ts +0 -300
  204. package/routes/videoExtended.ts +0 -284
  205. package/server.ts +0 -293
  206. package/ui/dist/assets/SettingsWorkspace-PiaVnsdA.js +0 -1
  207. package/ui/dist/assets/index-CjgnNtgt.css +0 -1
  208. package/ui/dist/assets/index-Da2s4_-5.js +0 -36
@@ -1,452 +0,0 @@
1
- import { setJobPhase } from "./inflight.js";
2
- import { logEvent } from "./logger.js";
3
-
4
- export interface ParsedImage {
5
- b64: string;
6
- revisedPrompt: string | null;
7
- }
8
-
9
- export type FinalImageHandler = (image: ParsedImage, index: number) => Promise<void> | void;
10
-
11
- export interface ResponseOutputSummary {
12
- eventType: string;
13
- itemType: string | null;
14
- status: string | null;
15
- hasResult: boolean;
16
- resultChars: number;
17
- revisedPromptChars: number;
18
- hasError: boolean;
19
- errorCode: string | null;
20
- errorType: string | null;
21
- errorParam: string | null;
22
- }
23
-
24
- export interface ResponseDiagnostics {
25
- eventTypes: Record<string, number>;
26
- streamStats: {
27
- chunkCount: number;
28
- bytesRead: number;
29
- maxChunkBytes: number;
30
- lfBoundaryCount: number;
31
- crlfBoundaryCount: number;
32
- parseSkipCount: number;
33
- finalBufferChars: number;
34
- sawDoneSentinel: boolean;
35
- sawResponseCompleted: boolean;
36
- };
37
- outputItemSummary: ResponseOutputSummary[];
38
- imageCallSeen: boolean;
39
- imageCallCompleted: boolean;
40
- imageCallFailed: boolean;
41
- imageResultCount: number;
42
- webSearchCallSeen: boolean;
43
- messageOutputSeen: boolean;
44
- outputTextChars: number;
45
- }
46
-
47
- export interface ParsedResponsesResult {
48
- images: ParsedImage[];
49
- usage: Record<string, number> | null;
50
- webSearchCalls: number;
51
- eventCount: number;
52
- eventTypes: Record<string, number>;
53
- extraIgnored: number;
54
- text: string | null;
55
- diagnostics: ResponseDiagnostics;
56
- }
57
-
58
- interface SseItem {
59
- type?: string;
60
- partial_image_b64?: string;
61
- partial_image_index?: number;
62
- partial_image?: string;
63
- image?: string;
64
- result?: string;
65
- index?: number;
66
- revised_prompt?: string;
67
- status?: string;
68
- error?: {
69
- code?: string;
70
- type?: string;
71
- param?: string;
72
- };
73
- content?: Array<{ type?: string; text?: string }>;
74
- }
75
-
76
- interface SseData {
77
- type?: string;
78
- delta?: string;
79
- text?: string;
80
- item?: SseItem;
81
- partial_image_b64?: string;
82
- partial_image_index?: number;
83
- partial_image?: string;
84
- image?: string;
85
- result?: string;
86
- index?: number;
87
- response?: {
88
- usage?: Record<string, number>;
89
- output?: SseItem[];
90
- tool_usage?: { web_search?: { num_requests?: number } };
91
- };
92
- error?: { code?: string };
93
- }
94
-
95
- interface ParseState {
96
- images: ParsedImage[];
97
- eventTypes: Record<string, number>;
98
- outputItemSummary: ResponseOutputSummary[];
99
- usage: Record<string, number> | null;
100
- textOutput: string;
101
- finalTextOutput: string | null;
102
- webSearchCalls: number;
103
- eventCount: number;
104
- extraIgnored: number;
105
- chunkCount: number;
106
- bytesRead: number;
107
- maxChunkBytes: number;
108
- lfBoundaryCount: number;
109
- crlfBoundaryCount: number;
110
- parseSkipCount: number;
111
- finalBufferChars: number;
112
- sawDoneSentinel: boolean;
113
- sawResponseCompleted: boolean;
114
- imageCallSeen: boolean;
115
- imageCallCompleted: boolean;
116
- imageCallFailed: boolean;
117
- imageResultCount: number;
118
- webSearchCallSeen: boolean;
119
- messageOutputSeen: boolean;
120
- }
121
-
122
- function createState(): ParseState {
123
- return {
124
- images: [],
125
- eventTypes: {},
126
- outputItemSummary: [],
127
- usage: null,
128
- textOutput: "",
129
- finalTextOutput: null,
130
- webSearchCalls: 0,
131
- eventCount: 0,
132
- extraIgnored: 0,
133
- chunkCount: 0,
134
- bytesRead: 0,
135
- maxChunkBytes: 0,
136
- lfBoundaryCount: 0,
137
- crlfBoundaryCount: 0,
138
- parseSkipCount: 0,
139
- finalBufferChars: 0,
140
- sawDoneSentinel: false,
141
- sawResponseCompleted: false,
142
- imageCallSeen: false,
143
- imageCallCompleted: false,
144
- imageCallFailed: false,
145
- imageResultCount: 0,
146
- webSearchCallSeen: false,
147
- messageOutputSeen: false,
148
- };
149
- }
150
-
151
- const MAX_DIAGNOSTIC_LABEL_CHARS = 120;
152
- const UNSAFE_DIAGNOSTIC_LABEL = /(bearer\s+|sk-[a-z0-9_-]{4,}|data:image\/|https?:\/\/|[a-z][a-z0-9+.-]*:\/\/|@|[\r\n])/i;
153
- const SAFE_DIAGNOSTIC_LABEL = /^[A-Za-z0-9_.:[\]-]+$/;
154
-
155
- export function safeDiagnosticLabel(value: unknown, fallback: string | null = null): string | null {
156
- if (typeof value !== "string" || value.length === 0) return fallback;
157
- const trimmed = value.slice(0, MAX_DIAGNOSTIC_LABEL_CHARS);
158
- if (UNSAFE_DIAGNOSTIC_LABEL.test(trimmed)) return "_redacted";
159
- if (!SAFE_DIAGNOSTIC_LABEL.test(trimmed)) return "_redacted";
160
- return trimmed;
161
- }
162
-
163
- function extractSseData(block: string): string {
164
- let eventData = "";
165
- for (const rawLine of block.split(/\r?\n/)) {
166
- const line = rawLine.replace(/\r$/, "");
167
- if (line.startsWith("data:")) eventData += line.slice(5).trimStart();
168
- }
169
- return eventData;
170
- }
171
-
172
- function nextSseBlock(buffer: string): { block: string; rest: string; delimiter: string } | null {
173
- const match = /\r?\n\r?\n/.exec(buffer);
174
- if (!match) return null;
175
- return {
176
- block: buffer.slice(0, match.index),
177
- rest: buffer.slice(match.index + match[0].length),
178
- delimiter: match[0],
179
- };
180
- }
181
-
182
- function extractPartialImage(data: SseData): { b64: string; index: number | null } | null {
183
- if (typeof data?.type !== "string" || !data.type.includes("partial")) return null;
184
- const item = data.item || {};
185
- const b64 =
186
- data.partial_image_b64 ||
187
- data.partial_image ||
188
- data.image ||
189
- data.result ||
190
- item.partial_image_b64 ||
191
- item.partial_image ||
192
- item.image ||
193
- item.result;
194
- if (typeof b64 !== "string" || b64.length === 0) return null;
195
- const index =
196
- typeof data.partial_image_index === "number" && Number.isFinite(data.partial_image_index)
197
- ? data.partial_image_index
198
- : typeof data.index === "number" && Number.isFinite(data.index)
199
- ? data.index
200
- : typeof item.partial_image_index === "number" && Number.isFinite(item.partial_image_index)
201
- ? item.partial_image_index
202
- : typeof item.index === "number" && Number.isFinite(item.index)
203
- ? item.index
204
- : null;
205
- return { b64, index };
206
- }
207
-
208
- function extractTextDelta(data: SseData): string | null {
209
- if (data.type === "response.output_text.delta" && typeof data.delta === "string") return data.delta;
210
- return null;
211
- }
212
-
213
- function extractFinalText(data: SseData): string | null {
214
- if (data.type === "response.output_text.done" && typeof data.text === "string") return cleanTextOutput(data.text);
215
- if (data.type === "response.output_item.done" && data.item?.type === "message") {
216
- return extractJsonItemText(data.item);
217
- }
218
- return null;
219
- }
220
-
221
- function extractJsonItemText(item: { type?: string; text?: string; content?: Array<{ type?: string; text?: string }> }): string | null {
222
- if (item.type === "output_text" && typeof item.text === "string") return cleanTextOutput(item.text);
223
- if (!Array.isArray(item.content)) return null;
224
- const text = item.content
225
- .filter((part) => part.type === "output_text" && typeof part.text === "string")
226
- .map((part) => part.text)
227
- .join("\n\n");
228
- return cleanTextOutput(text);
229
- }
230
-
231
- function cleanTextOutput(value: string): string | null {
232
- const trimmed = value.trim();
233
- return trimmed ? trimmed.slice(0, 4_000) : null;
234
- }
235
-
236
- function summarizeItem(eventType: string, item: SseItem): ResponseOutputSummary {
237
- const result = typeof item.result === "string" ? item.result : "";
238
- const revised = typeof item.revised_prompt === "string" ? item.revised_prompt : "";
239
- return {
240
- eventType: safeDiagnosticLabel(eventType, "_unknown") || "_unknown",
241
- itemType: safeDiagnosticLabel(item.type),
242
- status: safeDiagnosticLabel(item.status),
243
- hasResult: result.length > 0,
244
- resultChars: result.length,
245
- revisedPromptChars: revised.length,
246
- hasError: Boolean(item.error),
247
- errorCode: safeDiagnosticLabel(item.error?.code),
248
- errorType: safeDiagnosticLabel(item.error?.type),
249
- errorParam: safeDiagnosticLabel(item.error?.param),
250
- };
251
- }
252
-
253
- function recordOutputItem(state: ParseState, eventType: string, item: SseItem | undefined): void {
254
- if (!item) return;
255
- const summary = summarizeItem(eventType, item);
256
- state.outputItemSummary.push(summary);
257
- if (item.type === "image_generation_call") {
258
- state.imageCallSeen = true;
259
- state.imageCallCompleted = state.imageCallCompleted || eventType === "response.output_item.done" || item.status === "completed";
260
- state.imageCallFailed = state.imageCallFailed || item.status === "failed" || Boolean(item.error);
261
- if (summary.hasResult) state.imageResultCount++;
262
- }
263
- if (item.type === "web_search_call") state.webSearchCallSeen = true;
264
- if (item.type === "message") state.messageOutputSeen = true;
265
- }
266
-
267
- function diagnosticsFromState(state: ParseState): ResponseDiagnostics {
268
- const outputTextChars = (state.finalTextOutput ?? cleanTextOutput(state.textOutput) ?? "").length;
269
- return {
270
- eventTypes: state.eventTypes,
271
- streamStats: {
272
- chunkCount: state.chunkCount,
273
- bytesRead: state.bytesRead,
274
- maxChunkBytes: state.maxChunkBytes,
275
- lfBoundaryCount: state.lfBoundaryCount,
276
- crlfBoundaryCount: state.crlfBoundaryCount,
277
- parseSkipCount: state.parseSkipCount,
278
- finalBufferChars: state.finalBufferChars,
279
- sawDoneSentinel: state.sawDoneSentinel,
280
- sawResponseCompleted: state.sawResponseCompleted,
281
- },
282
- outputItemSummary: state.outputItemSummary,
283
- imageCallSeen: state.imageCallSeen,
284
- imageCallCompleted: state.imageCallCompleted,
285
- imageCallFailed: state.imageCallFailed,
286
- imageResultCount: state.imageResultCount,
287
- webSearchCallSeen: state.webSearchCallSeen,
288
- messageOutputSeen: state.messageOutputSeen,
289
- outputTextChars,
290
- };
291
- }
292
-
293
- function resultFromState(state: ParseState): ParsedResponsesResult {
294
- const text = state.finalTextOutput ?? cleanTextOutput(state.textOutput);
295
- return {
296
- images: state.images,
297
- usage: state.usage,
298
- webSearchCalls: state.webSearchCalls,
299
- eventCount: state.eventCount,
300
- eventTypes: state.eventTypes,
301
- extraIgnored: state.extraIgnored,
302
- text,
303
- diagnostics: diagnosticsFromState(state),
304
- };
305
- }
306
-
307
- interface ParseStreamOptions {
308
- requestId?: string | null;
309
- scope: string;
310
- maxImages?: number;
311
- onPartialImage?: ((partial: { b64: string; index: number | null | undefined }) => void) | null;
312
- onFinalImage?: FinalImageHandler | null;
313
- }
314
-
315
- function makeStreamError(message: string, code: string, eventCount: number, eventType: string): Error {
316
- const err = new Error(message) as Error & { code?: string; upstreamCode?: string; status?: number; eventCount?: number; eventType?: string };
317
- err.code = "RESPONSES_STREAM_ERROR";
318
- err.upstreamCode = code;
319
- err.status = 502;
320
- err.eventCount = eventCount;
321
- err.eventType = eventType;
322
- Object.defineProperty(err, "ima2ResponsesError", { value: true });
323
- return err;
324
- }
325
-
326
- async function appendFinalImageFromItem(
327
- state: ParseState,
328
- item: SseItem,
329
- maxImages: number,
330
- requestId: string | null | undefined,
331
- onFinalImage: FinalImageHandler | null,
332
- ): Promise<void> {
333
- if (item.type !== "image_generation_call" || typeof item.result !== "string" || !item.result) return;
334
- if (state.images.some((image) => image.b64 === item.result)) return;
335
- if (state.images.length < maxImages) {
336
- const image = {
337
- b64: item.result,
338
- revisedPrompt: typeof item.revised_prompt === "string" ? item.revised_prompt : null,
339
- };
340
- const index = state.images.length;
341
- state.images.push(image);
342
- if (requestId) setJobPhase(requestId, "decoding");
343
- await onFinalImage?.(image, index);
344
- } else {
345
- state.extraIgnored++;
346
- }
347
- }
348
-
349
- export async function parseStream(res: Response, {
350
- requestId,
351
- scope,
352
- maxImages = 1,
353
- onPartialImage = null,
354
- onFinalImage = null,
355
- }: ParseStreamOptions): Promise<ParsedResponsesResult> {
356
- const reader = res.body!.getReader();
357
- const decoder = new TextDecoder();
358
- const state = createState();
359
- let buffer = "";
360
- while (true) {
361
- const { done, value } = await reader.read();
362
- if (done) break;
363
- state.chunkCount++;
364
- state.bytesRead += value.byteLength;
365
- state.maxChunkBytes = Math.max(state.maxChunkBytes, value.byteLength);
366
- buffer += decoder.decode(value, { stream: true });
367
- let next;
368
- while ((next = nextSseBlock(buffer)) !== null) {
369
- const block = next.block;
370
- buffer = next.rest;
371
- if (next.delimiter.includes("\r\n")) state.crlfBoundaryCount++;
372
- else state.lfBoundaryCount++;
373
- const eventData = extractSseData(block);
374
- if (eventData === "[DONE]") {
375
- state.sawDoneSentinel = true;
376
- continue;
377
- }
378
- if (!eventData) continue;
379
- let data: SseData;
380
- try { data = JSON.parse(eventData); } catch { state.parseSkipCount++; continue; }
381
- state.eventCount++;
382
- const eventType = safeDiagnosticLabel(data.type, "_unknown") || "_unknown";
383
- state.eventTypes[eventType] = (state.eventTypes[eventType] || 0) + 1;
384
- const delta = extractTextDelta(data);
385
- if (delta) state.textOutput += delta;
386
- const finalText = extractFinalText(data);
387
- if (finalText) state.finalTextOutput = finalText;
388
- if (finalText) state.messageOutputSeen = true;
389
- const partial = extractPartialImage(data);
390
- if (partial && typeof onPartialImage === "function") onPartialImage(partial);
391
- if (data.type === "response.output_item.done") recordOutputItem(state, data.type, data.item);
392
- if (data.type === "response.output_item.done" && data.item?.type === "image_generation_call") {
393
- await appendFinalImageFromItem(state, data.item, maxImages, requestId, onFinalImage);
394
- }
395
- if (data.type === "response.output_item.done" && data.item?.type === "web_search_call") state.webSearchCalls++;
396
- if (data.type === "response.completed") {
397
- state.sawResponseCompleted = true;
398
- state.usage = data.response?.usage || null;
399
- for (const item of data.response?.output || []) {
400
- recordOutputItem(state, data.type, item);
401
- await appendFinalImageFromItem(state, item, maxImages, requestId, onFinalImage);
402
- }
403
- const wsNum = data.response?.tool_usage?.web_search?.num_requests;
404
- if (typeof wsNum === "number" && wsNum > state.webSearchCalls) state.webSearchCalls = wsNum;
405
- }
406
- if (data.type === "error") {
407
- throw makeStreamError(
408
- "Responses stream returned an error",
409
- safeDiagnosticLabel(data.error?.code, "RESPONSES_STREAM_ERROR") || "RESPONSES_STREAM_ERROR",
410
- state.eventCount,
411
- eventType,
412
- );
413
- }
414
- }
415
- }
416
- state.finalBufferChars = buffer.length;
417
- logEvent(scope, "stream_end", {
418
- requestId,
419
- events: state.eventCount,
420
- imageCount: state.images.length,
421
- webSearchCalls: state.webSearchCalls,
422
- imageCallSeen: state.imageCallSeen,
423
- messageOutputSeen: state.messageOutputSeen,
424
- bytesRead: state.bytesRead,
425
- parseSkipCount: state.parseSkipCount,
426
- });
427
- return resultFromState(state);
428
- }
429
-
430
- export async function parseJson(res: Response, maxImages: number): Promise<ParsedResponsesResult> {
431
- const json = await res.json() as {
432
- output?: SseItem[];
433
- usage?: Record<string, number>;
434
- };
435
- const state = createState();
436
- state.usage = json.usage || null;
437
- for (const item of json.output || []) {
438
- state.eventCount++;
439
- state.eventTypes["json.output"] = (state.eventTypes["json.output"] || 0) + 1;
440
- recordOutputItem(state, "json.output", item);
441
- if (item.type === "image_generation_call" && item.result && state.images.length < maxImages) {
442
- state.images.push({
443
- b64: item.result,
444
- revisedPrompt: typeof item.revised_prompt === "string" ? item.revised_prompt : null,
445
- });
446
- }
447
- if (item.type === "web_search_call") state.webSearchCalls++;
448
- const itemText = extractJsonItemText(item);
449
- if (itemText) state.textOutput += `${state.textOutput ? "\n\n" : ""}${itemText}`;
450
- }
451
- return resultFromState(state);
452
- }
@@ -1,28 +0,0 @@
1
- export interface ImageGenOptions {
2
- quality?: string;
3
- size?: string;
4
- moderation?: string;
5
- partial_images?: number;
6
- }
7
-
8
- export type ResponseTool = { type: string; quality?: string; size?: string; moderation?: string; partial_images?: number };
9
- export type ImageToolChoice = "required" | { type: "image_generation" };
10
-
11
- export function tools(webSearchEnabled: boolean, imageOptions: ImageGenOptions): ResponseTool[] {
12
- return [
13
- ...(webSearchEnabled ? [{ type: "web_search" }] : []),
14
- { type: "image_generation", ...imageOptions },
15
- ];
16
- }
17
-
18
- export function toolTypes(requestTools: ResponseTool[]): string[] {
19
- return requestTools.map((tool) => tool.type);
20
- }
21
-
22
- export function imageToolChoice(forceImageTool: boolean): ImageToolChoice {
23
- return forceImageTool ? { type: "image_generation" } : "required";
24
- }
25
-
26
- export function imageToolChoiceKind(choice: ImageToolChoice): "required" | "image_generation" {
27
- return choice === "required" ? "required" : "image_generation";
28
- }
@@ -1,146 +0,0 @@
1
- import type OpenAI from "openai";
2
- import { config as runtimeConfigDefault } from "../config.js";
3
-
4
- export type AppConfig = typeof runtimeConfigDefault;
5
- export type ApiKeySource = "env" | "oauth" | "config" | "none" | undefined;
6
- export type OAuthReadyState = "starting" | "ready" | "failed" | "disabled" | undefined;
7
-
8
- export interface RuntimeContext {
9
- apiKey: string | undefined;
10
- apiKeySource: ApiKeySource;
11
- config: AppConfig;
12
- grokActualPort: number | undefined;
13
- grokPort: number;
14
- grokUrl: string;
15
- hasApiKey: boolean;
16
- oauthActualPort: number | undefined;
17
- oauthPort: number;
18
- oauthReadyPromise: Promise<void> | null;
19
- oauthReadyState: OAuthReadyState;
20
- oauthUrl: string;
21
- openai: OpenAI | null;
22
- packageVersion: string;
23
- rootDir: string;
24
- serverActualPort: number | undefined;
25
- serverConfiguredPort: number;
26
- serverUrl: string;
27
- startedAt: number;
28
- }
29
-
30
- /** A partial used during boot when only some fields are known, or by callers
31
- * threading ctx through layered APIs (oauth/responses adapters). */
32
- export type RuntimeContextOverrides = Partial<RuntimeContext>;
33
-
34
- /** Looser ctx shape for route registration helpers and tests, where callers
35
- * often pass minimal nested config fixtures. Behaviour-preserving under the
36
- * current non-strict-null tsconfig. */
37
- export type RouteRuntimeContext =
38
- & Omit<Partial<RuntimeContext>, "config">
39
- & { config?: { [K in keyof AppConfig]?: Partial<AppConfig[K]> } };
40
-
41
- /** Normalize a possibly-Partial RouteRuntimeContext into a strict RuntimeContext.
42
- *
43
- * IMPORTANT: This MUTATES `ctx` in place and returns the same object (object
44
- * identity preserved). The runtime mutates fields on the original ctx after
45
- * route registration — e.g. `markOAuthReady()` flips `oauthReadyState`,
46
- * `startServer()` sets `serverActualPort` once the listener binds. Snapshotting
47
- * here would break those updates because deep code (like `waitForOAuthReady`)
48
- * re-reads `ctx.oauthReadyState`.
49
- *
50
- * - Live fields (oauthReadyState, oauthReadyPromise, serverActualPort, openai,
51
- * apiKey, ...) keep their original references via in-place fill.
52
- * - Missing config nests are merged from `runtimeConfigDefault` per top-level
53
- * key, so partial fixtures (e.g. `{ config: { storage: {...} } }`) still see
54
- * real `oauth`/`ids`/`limits` defaults at deep call sites.
55
- *
56
- * Use this at the top of any function that crosses from `RouteRuntimeContext`
57
- * into deep typed code. Per GPT Pro's P05 review: RouteRuntimeContext stays
58
- * boundary-only; deep lib code should operate on strict RuntimeContext. */
59
- export function requireRuntimeContext(ctx: RouteRuntimeContext | undefined): RuntimeContext {
60
- const target = (ctx ?? {}) as RouteRuntimeContext & Record<string, unknown>;
61
- target.config = mergeRuntimeConfig(target.config);
62
- if (target.apiKey === undefined && Object.prototype.hasOwnProperty.call(target, "apiKey") === false) {
63
- target.apiKey = undefined;
64
- }
65
- if (target.hasApiKey === undefined) target.hasApiKey = false;
66
- if (target.grokPort === undefined) {
67
- target.grokPort = (target.config as AppConfig).grokProvider?.proxyPort ?? 18645;
68
- }
69
- if (target.grokUrl === undefined) {
70
- const grokCfg = (target.config as AppConfig).grokProvider;
71
- const host = grokCfg?.proxyHost ?? "127.0.0.1";
72
- const port = target.grokActualPort ?? target.grokPort ?? grokCfg?.proxyPort ?? 18645;
73
- target.grokUrl = `http://${host}:${port}/v1`;
74
- }
75
- if (target.oauthPort === undefined) {
76
- target.oauthPort = (target.config as AppConfig).oauth?.proxyPort ?? 11782;
77
- }
78
- if (target.oauthReadyPromise === undefined) target.oauthReadyPromise = null;
79
- if (target.oauthUrl === undefined) {
80
- target.oauthUrl = `http://127.0.0.1:${(target.config as AppConfig).oauth?.proxyPort ?? target.oauthPort ?? 11782}`;
81
- }
82
- if (target.openai === undefined) target.openai = null;
83
- if (target.packageVersion === undefined) target.packageVersion = "0.0.0";
84
- if (target.rootDir === undefined) target.rootDir = process.cwd();
85
- if (target.serverConfiguredPort === undefined) {
86
- target.serverConfiguredPort = (target.config as AppConfig).server?.port ?? 11783;
87
- }
88
- if (target.serverUrl === undefined) {
89
- const port = target.serverActualPort ?? target.serverConfiguredPort ?? 11783;
90
- target.serverUrl = `http://localhost:${port}`;
91
- }
92
- if (target.startedAt === undefined) target.startedAt = Date.now();
93
- return target as unknown as RuntimeContext;
94
- }
95
-
96
- /** Per-top-level-key merge: caller's nested config keys win, missing nests
97
- * fall back to `runtimeConfigDefault`. Avoids deep-clone snapshotting so
98
- * callers can still observe live-mutated config values if they exist. */
99
- function mergeRuntimeConfig(
100
- partial: RouteRuntimeContext["config"] | undefined,
101
- ): AppConfig {
102
- if (!partial) return runtimeConfigDefault;
103
- const merged: Record<string, unknown> = {};
104
- for (const k of Object.keys(runtimeConfigDefault) as Array<keyof AppConfig>) {
105
- const fromPartial = (partial as Record<string, unknown>)[k as string];
106
- if (fromPartial && typeof fromPartial === "object") {
107
- merged[k as string] = {
108
- ...(runtimeConfigDefault[k] as object),
109
- ...(fromPartial as object),
110
- };
111
- } else {
112
- merged[k as string] = runtimeConfigDefault[k];
113
- }
114
- }
115
- for (const k of Object.keys(partial)) {
116
- if (!(k in merged)) merged[k] = (partial as Record<string, unknown>)[k];
117
- }
118
- return merged as AppConfig;
119
- }
120
-
121
- /** Stub-friendly default for tests. Do NOT use in production boot paths. */
122
- export function createTestRuntimeContext(over: RuntimeContextOverrides = {}): RuntimeContext {
123
- const now = Date.now();
124
- const base: RuntimeContext = {
125
- apiKey: undefined,
126
- apiKeySource: undefined,
127
- config: {} as AppConfig,
128
- grokActualPort: undefined,
129
- grokPort: 18645,
130
- grokUrl: "http://127.0.0.1:18645/v1",
131
- hasApiKey: false,
132
- oauthActualPort: undefined,
133
- oauthPort: 11782,
134
- oauthReadyPromise: null,
135
- oauthReadyState: undefined,
136
- oauthUrl: "http://127.0.0.1:11782",
137
- openai: null,
138
- packageVersion: "0.0.0-test",
139
- rootDir: process.cwd(),
140
- serverActualPort: undefined,
141
- serverConfiguredPort: 11783,
142
- serverUrl: "http://127.0.0.1:11783",
143
- startedAt: now,
144
- };
145
- return { ...base, ...over };
146
- }