ima2-gen 1.1.17 → 1.1.18

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 (33) hide show
  1. package/README.md +24 -2
  2. package/bin/commands/capabilities.js +6 -0
  3. package/bin/commands/capabilities.ts +6 -0
  4. package/bin/commands/video.js +211 -0
  5. package/bin/commands/video.ts +202 -0
  6. package/bin/ima2.js +61 -6
  7. package/bin/ima2.ts +54 -6
  8. package/docs/API.md +73 -4
  9. package/docs/CLI.md +38 -0
  10. package/lib/capabilities.js +9 -0
  11. package/lib/capabilities.ts +9 -0
  12. package/lib/grokVideoAdapter.js +17 -1
  13. package/lib/grokVideoAdapter.ts +15 -1
  14. package/lib/imageModels.js +1 -1
  15. package/lib/imageModels.ts +2 -2
  16. package/lib/oauthLauncher.js +5 -2
  17. package/lib/oauthLauncher.ts +5 -3
  18. package/package.json +1 -1
  19. package/ui/dist/.vite/manifest.json +12 -12
  20. package/ui/dist/assets/{AgentWorkspace-CLHwx6u4.js → AgentWorkspace-BTuPjlDH.js} +1 -1
  21. package/ui/dist/assets/{CardNewsWorkspace-6y_HNp3I.js → CardNewsWorkspace-DmqCMnIx.js} +1 -1
  22. package/ui/dist/assets/{NodeCanvas-DR2N5Dib.js → NodeCanvas-jr9WXfNm.js} +1 -1
  23. package/ui/dist/assets/{PromptBuilderPanel-BQlPtGGm.js → PromptBuilderPanel-CoWjqQZS.js} +1 -1
  24. package/ui/dist/assets/{PromptImportDialog-aNk40wLt.js → PromptImportDialog-C2zGZkyK.js} +2 -2
  25. package/ui/dist/assets/{PromptImportDiscoverySection-B6NKkVBz.js → PromptImportDiscoverySection-N0ZxHLYs.js} +1 -1
  26. package/ui/dist/assets/{PromptImportFolderSection-9-xbe-FM.js → PromptImportFolderSection-BC3dCASZ.js} +1 -1
  27. package/ui/dist/assets/{PromptLibraryPanel-CbEY0AM6.js → PromptLibraryPanel-CcVliYnF.js} +2 -2
  28. package/ui/dist/assets/{SettingsWorkspace-ao9ymIWt.js → SettingsWorkspace-CiB4ux7E.js} +1 -1
  29. package/ui/dist/assets/{index-DP88bEQf.js → index-C93CfR9P.js} +1 -1
  30. package/ui/dist/assets/index-CIhB_ia7.css +1 -0
  31. package/ui/dist/assets/{index-B0re600T.js → index-uBEJn5jz.js} +12 -12
  32. package/ui/dist/index.html +2 -2
  33. package/ui/dist/assets/index-CXJEgTOQ.css +0 -1
package/bin/ima2.ts CHANGED
@@ -64,13 +64,15 @@ async function setup() {
64
64
 
65
65
  console.log("\n ima2-gen — GPT Image 2 Generator\n");
66
66
  console.log(" Choose authentication method:\n");
67
- console.log(" 1) API Key paste your OpenAI API key (paid)");
68
- console.log(" 2) OAuth — login with ChatGPT account (free)\n");
67
+ console.log(" 1) GPT OAuth login with ChatGPT account (free, images only)");
68
+ console.log(" 2) Grok OAuth — login with xAI/Grok account (images + video)");
69
+ console.log(" 3) Both — GPT OAuth + Grok OAuth");
70
+ console.log(" 4) API Key — paste your OpenAI API key (paid)\n");
69
71
 
70
- const choice = await rl.question(" Enter 1 or 2: ");
72
+ const choice = await rl.question(" Enter 1-4: ");
71
73
  const config = loadConfig();
72
74
 
73
- if (choice.trim() === "1") {
75
+ if (choice.trim() === "4") {
74
76
  const key = await rl.question(" OpenAI API Key: ");
75
77
  if (!key.startsWith("sk-")) {
76
78
  console.log(" Invalid API key format. Expected sk-...");
@@ -81,13 +83,56 @@ async function setup() {
81
83
  config.apiKey = key.trim();
82
84
  saveConfig(config);
83
85
  console.log("\n API key saved. Starting server...\n");
86
+ } else if (choice.trim() === "2") {
87
+ config.provider = "grok";
88
+ config.oauth = config.oauth || {};
89
+ config.oauth.disableAutoStart = true;
90
+ delete config.apiKey;
91
+ saveConfig(config);
92
+ console.log("\n Starting Grok OAuth login...\n");
93
+ try {
94
+ execSync(`node ${JSON.stringify(join(ROOT, "bin", "ima2.js"))} grok login`, { stdio: "inherit" });
95
+ } catch {
96
+ console.log("\n Grok login failed or cancelled. You can retry with 'ima2 grok login'.\n");
97
+ rl.close();
98
+ process.exit(1);
99
+ }
100
+ console.log(" Grok configured. Run 'ima2 serve' to start.\n");
101
+ } else if (choice.trim() === "3") {
102
+ config.provider = "oauth";
103
+ delete config.apiKey;
104
+ if (config.oauth) delete config.oauth.disableAutoStart;
105
+ saveConfig(config);
106
+ console.log("\n Setting up both GPT OAuth + Grok OAuth...\n");
107
+ // GPT OAuth
108
+ const auth = detectCodexAuth();
109
+ if (!auth.authed) {
110
+ console.log(" Running GPT OAuth login...\n");
111
+ try {
112
+ execSync(`${resolveBin("npx")} @openai/codex login`, { stdio: "inherit" });
113
+ } catch {
114
+ console.log("\n GPT login failed. Continuing with Grok...\n");
115
+ }
116
+ } else {
117
+ console.log(` GPT OAuth session found.\n`);
118
+ }
119
+ // Grok OAuth
120
+ console.log(" Running Grok OAuth login...\n");
121
+ try {
122
+ execSync(`node ${JSON.stringify(join(ROOT, "bin", "ima2.js"))} grok login`, { stdio: "inherit" });
123
+ } catch {
124
+ console.log("\n Grok login failed. You can retry with 'ima2 grok login'.\n");
125
+ }
126
+ console.log(" Both providers configured.\n");
84
127
  } else {
128
+ // Default: GPT OAuth (choice 1 or anything else)
85
129
  config.provider = "oauth";
130
+ config.oauth = config.oauth || {};
131
+ config.oauth.disableAutoStart = false;
86
132
  delete config.apiKey;
87
133
  saveConfig(config);
88
134
  console.log("\n Starting OAuth login...\n");
89
135
 
90
- // Check if codex auth exists (file OR keyring via `codex login status`)
91
136
  const auth = detectCodexAuth();
92
137
  const hasAuth = auth.authed;
93
138
 
@@ -232,6 +277,7 @@ function showHelp() {
232
277
 
233
278
  Client commands (require a running 'ima2 serve'):
234
279
  gen <prompt> Generate image(s) from prompt (ima2 gen --help)
280
+ video <prompt> Generate video via Grok (ima2 video --help)
235
281
  edit <file> Edit an existing image (ima2 edit --help)
236
282
  ls List recent history (ima2 ls --help)
237
283
  show <name> Show one history item (ima2 show --help)
@@ -277,6 +323,7 @@ function showHelp() {
277
323
  ima2 serve --dev Start with verbose server diagnostics
278
324
  ima2 gen "a shiba in space" Generate from CLI
279
325
  ima2 gen "merge" --ref a.png --ref b.png -q high -o out.png
326
+ ima2 video "a cat playing piano" --duration 10
280
327
  ima2 ls -n 10 Last 10 generations
281
328
  ima2 skill Print agent usage skill
282
329
  ima2 capabilities --json Inspect supported models/options
@@ -295,7 +342,7 @@ if (args.includes("-v") || args.includes("--version")) {
295
342
  }
296
343
 
297
344
  if ((!command || args.includes("-h") || args.includes("--help"))
298
- && !["doctor", "gen", "edit", "ls", "show", "ps", "cancel", "session", "history", "prompt", "multimode", "node", "annotate", "canvas-versions", "metadata", "comfy", "cardnews", "inflight", "storage", "billing", "providers", "oauth", "grok", "config", "defaults", "capabilities", "skill", "ping"].includes(command)) {
345
+ && !["doctor", "gen", "video", "edit", "ls", "show", "ps", "cancel", "session", "history", "prompt", "multimode", "node", "annotate", "canvas-versions", "metadata", "comfy", "cardnews", "inflight", "storage", "billing", "providers", "oauth", "grok", "config", "defaults", "capabilities", "skill", "ping"].includes(command)) {
299
346
  showHelp();
300
347
  process.exit(command ? 0 : 1);
301
348
  }
@@ -337,6 +384,7 @@ switch (command) {
337
384
  }
338
385
  break;
339
386
  case "gen":
387
+ case "video":
340
388
  case "edit":
341
389
  case "ls":
342
390
  case "show":
package/docs/API.md CHANGED
@@ -20,10 +20,8 @@ Image generation supports OAuth, API-key, and Grok providers.
20
20
  - Grok generation maps `size` to xAI `aspect_ratio` and `resolution`; it does not send an OpenAI-style `size` field upstream. Grok edit uses xAI `/v1/images/edits`; Grok mask edit remains unsupported and returns `GROK_MASK_UNSUPPORTED`.
21
21
  - Mask edits are mask/selection guided edits, not pixel-perfect inpaint guarantees.
22
22
 
23
- Grok video generation (T2V/I2V) is not part of the `1.1.15` runtime API. The
24
- `docs/grok-video-i2v-plan.md` and `docs/grok-video-i2v-research.md` files are
25
- planning and research notes only; no `/api/video` route or Grok video endpoint
26
- wrapper is shipped in this release.
23
+ Grok video generation uses `POST /api/video/generate` (SSE). See the Video
24
+ Generation section below for the full endpoint specification.
27
25
 
28
26
  ## Health And Status
29
27
 
@@ -219,6 +217,76 @@ Server-side validation may return these reference codes:
219
217
  | `GROK_REF_TOO_MANY` | Grok classic generation received more than three reference images |
220
218
  | `GROK_MASK_UNSUPPORTED` | Grok edit was requested with a mask; xAI mask edit is not wired in this release |
221
219
 
220
+ ## Video Generation
221
+
222
+ ### `POST /api/video/generate` (SSE)
223
+
224
+ Generate a video via the Grok video provider. Returns Server-Sent Events.
225
+
226
+ ```json
227
+ {
228
+ "prompt": "a cat playing piano",
229
+ "provider": "grok",
230
+ "model": "grok-imagine-video",
231
+ "duration": 5,
232
+ "resolution": "480p",
233
+ "aspectRatio": "auto",
234
+ "sourceImage": "<base64>",
235
+ "referenceImages": ["<base64>", "<base64>"],
236
+ "referenceFilenames": ["existing-file.png"],
237
+ "sessionId": "optional",
238
+ "requestId": "optional-client-id"
239
+ }
240
+ ```
241
+
242
+ **Models**: `grok-imagine-video` (default), `grok-imagine-video-1.5-preview`.
243
+
244
+ **Mode** is auto-detected from reference inputs:
245
+
246
+ | Inputs | Mode | Duration cap |
247
+ |---|---|---|
248
+ | No images | text-to-video | 1–15s |
249
+ | 1 image (`sourceImage` or `sourceFilename`) | image-to-video | 1–15s |
250
+ | 2–7 images (`referenceImages` / `referenceFilenames`) | reference-to-video | 1–10s |
251
+
252
+ **Parameters**:
253
+
254
+ | Field | Type | Default | Notes |
255
+ |---|---|---|---|
256
+ | `prompt` | string | — | Required |
257
+ | `provider` | string | `"grok"` | Must be `"grok"` |
258
+ | `model` | string | `grok-imagine-video` | Video model |
259
+ | `duration` | integer | `5` | 1–15 seconds (clamped to 10 for reference-to-video) |
260
+ | `resolution` | string | `"480p"` | `480p` or `720p` |
261
+ | `aspectRatio` | string | `"auto"` | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, auto |
262
+ | `sourceImage` | string | — | Base64 image for image-to-video |
263
+ | `sourceFilename` | string | — | Existing generated file for image-to-video |
264
+ | `referenceImages` | string[] | — | Base64 images for reference-to-video |
265
+ | `referenceFilenames` | string[] | — | Existing generated files for reference-to-video |
266
+
267
+ **SSE events**:
268
+
269
+ | Event | Data | Description |
270
+ |---|---|---|
271
+ | `planning` | `{ requestId }` | Preparing video generation |
272
+ | `submitted` | `{ requestId, xaiVideoRequestId }` | Submitted to xAI |
273
+ | `progress` | `{ requestId, progress, stalled }` | Progress 0.0–1.0 |
274
+ | `done` | `{ requestId, filename, url, mediaType, revisedPrompt, elapsed, usage, video }` | Video ready |
275
+ | `error` | `{ error, code, status, requestId }` | Generation failed |
276
+
277
+ **Video error codes**:
278
+
279
+ | Code | Meaning |
280
+ |---|---|
281
+ | `VIDEO_PROVIDER_UNSUPPORTED` | Provider is not `"grok"` |
282
+ | `PROMPT_REQUIRED` | Empty or missing prompt |
283
+ | `INVALID_GROK_VIDEO_MODEL` | Model not in valid set |
284
+ | `INVALID_VIDEO_RESOLUTION` | Resolution not 480p or 720p |
285
+ | `INVALID_VIDEO_ASPECT_RATIO` | Aspect ratio not in valid set |
286
+ | `INVALID_VIDEO_DURATION` | Duration not 1–15 integer |
287
+ | `GROK_VIDEO_REF_TOO_MANY` | More than 7 reference images |
288
+ | `GROK_VIDEO_FAILED` | Upstream xAI video generation failed |
289
+
222
290
  ## History
223
291
 
224
292
  | Method | Path | Notes |
@@ -299,6 +367,7 @@ Most server routes under `/api/*` have a CLI wrapper. The exception is **Agent M
299
367
  | `POST /api/generate` | `ima2 gen` |
300
368
  | `POST /api/edit` | `ima2 edit` |
301
369
  | `POST /api/generate/multimode` (SSE) | `ima2 multimode` |
370
+ | `POST /api/video/generate` (SSE) | `ima2 video` |
302
371
  | `POST /api/node/generate` (SSE) / `GET /api/node/:id` | `ima2 node generate` / `ima2 node show` |
303
372
  | `GET /api/history` | `ima2 ls` |
304
373
  | `DELETE /api/history/:name` / `…/permanent` | `ima2 history rm [--permanent]` |
package/docs/CLI.md CHANGED
@@ -49,6 +49,7 @@ Agents should start from the packaged skill and capability commands instead of g
49
49
  | `ima2 gen <prompt>` | Generate from the CLI |
50
50
  | `ima2 edit <file> --prompt <text>` | Edit an existing image |
51
51
  | `ima2 multimode <prompt>` | Multi-image SSE generation (streams `phase` / `partial` / `image` events) |
52
+ | `ima2 video <prompt>` | Video generation via Grok (SSE streaming with progress) |
52
53
  | `ima2 node generate` | Node-mode generate (SSE; supports `--no-stream`) |
53
54
  | `ima2 node show <nodeId>` | Read node metadata |
54
55
 
@@ -105,6 +106,43 @@ small text, and pixel-perfect typography can still need iteration or post-editin
105
106
 
106
107
  Multimode-specific flags include `--max-images <1..8>`, `--ref <file>` (repeatable, max 5), `--mode <auto|direct>`, `--provider <auto|oauth|api|grok>`, and `--show-partial`. `ima2 edit --mask` remains intentionally deferred to #31 because current mask plumbing is guided edit rather than guaranteed true masked/inpaint semantics.
107
108
 
109
+ ## Video
110
+
111
+ | Command | Description |
112
+ |---|---|
113
+ | `ima2 video <prompt>` | Generate a video via Grok (SSE streaming with progress) |
114
+
115
+ Video flags:
116
+
117
+ | Flag | Meaning |
118
+ |---|---|
119
+ | `--duration <1..15>` | Duration in seconds (default: 5) |
120
+ | `--resolution <480p\|720p>` | Video resolution (default: 480p) |
121
+ | `--aspect-ratio <ratio\|auto>` | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, auto (default: auto) |
122
+ | `--model <name>` | `grok-imagine-video` or `grok-imagine-video-1.5-preview` |
123
+ | `--ref <file>` | Attach source/reference image (repeatable, max 7) |
124
+ | `-o, --out <file>` | Output file path |
125
+ | `-d, --out-dir <dir>` | Output directory |
126
+ | `--timeout <sec>` | Timeout in seconds (default: 600) |
127
+ | `--session <id>` | Session ID |
128
+
129
+ Video mode is auto-detected from `--ref` count:
130
+
131
+ | Refs | Mode |
132
+ |---|---|
133
+ | 0 | text-to-video |
134
+ | 1 | image-to-video |
135
+ | 2–7 | reference-to-video (max 10s duration) |
136
+
137
+ SSE events: `planning` → `submitted` → `progress` (0–100%) → `done` or `error`.
138
+
139
+ ```bash
140
+ ima2 video "a cat playing piano"
141
+ ima2 video "animate this" --ref photo.png --duration 10
142
+ ima2 video "cinematic" --resolution 720p --aspect-ratio 16:9 -o out.mp4
143
+ ima2 video "style transfer" --ref a.png --ref b.png --ref c.png --model grok-imagine-video-1.5-preview
144
+ ```
145
+
108
146
  ## Diagnostics
109
147
 
110
148
  `ima2 doctor image-probe` runs live Responses probes that help classify image
@@ -9,6 +9,7 @@ const AGENT_COMMANDS = [
9
9
  "capabilities",
10
10
  "defaults",
11
11
  "gen",
12
+ "video",
12
13
  "edit",
13
14
  "multimode",
14
15
  "node generate",
@@ -55,6 +56,13 @@ export function buildIma2Capabilities({ appConfig = runtimeConfigDefault, packag
55
56
  unsupported: toArray(appConfig.imageModels.unsupported),
56
57
  grokSupported: ["grok-imagine-image", "grok-imagine-image-quality"],
57
58
  },
59
+ videoModels: {
60
+ supported: ["grok-imagine-video", "grok-imagine-video-1.5-preview"],
61
+ resolutions: ["480p", "720p"],
62
+ aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "auto"],
63
+ durationRange: [1, 15],
64
+ maxReferences: 7,
65
+ },
58
66
  reasoningEfforts: toArray(appConfig.imageModels.validReasoningEfforts),
59
67
  quality: toArray(VALID_IMAGE_QUALITIES),
60
68
  moderation: toArray(appConfig.oauth.validModeration),
@@ -98,6 +106,7 @@ export function buildIma2Capabilities({ appConfig = runtimeConfigDefault, packag
98
106
  i2i: "Use --ref for reference generation, or ima2 edit <file> --prompt \"<text>\" for image edits.",
99
107
  defaults: "Use ima2 defaults set model/reasoning for persistent defaults; request flags remain per-call overrides.",
100
108
  promptBuilder: "Use ima2 prompt build --message \"...\" to refine prompt intent. Use ima2 gen / ima2 multimode to generate images. Workspace profile settings are UI-only.",
109
+ video: "Use ima2 video \"<prompt>\" to generate video. Supports --ref for image-to-video and reference-to-video modes.",
101
110
  },
102
111
  };
103
112
  }
@@ -13,6 +13,7 @@ const AGENT_COMMANDS = [
13
13
  "capabilities",
14
14
  "defaults",
15
15
  "gen",
16
+ "video",
16
17
  "edit",
17
18
  "multimode",
18
19
  "node generate",
@@ -69,6 +70,13 @@ export function buildIma2Capabilities({
69
70
  unsupported: toArray(appConfig.imageModels.unsupported),
70
71
  grokSupported: ["grok-imagine-image", "grok-imagine-image-quality"],
71
72
  },
73
+ videoModels: {
74
+ supported: ["grok-imagine-video", "grok-imagine-video-1.5-preview"],
75
+ resolutions: ["480p", "720p"],
76
+ aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "auto"],
77
+ durationRange: [1, 15],
78
+ maxReferences: 7,
79
+ },
72
80
  reasoningEfforts: toArray(appConfig.imageModels.validReasoningEfforts),
73
81
  quality: toArray(VALID_IMAGE_QUALITIES),
74
82
  moderation: toArray(appConfig.oauth.validModeration),
@@ -112,6 +120,7 @@ export function buildIma2Capabilities({
112
120
  i2i: "Use --ref for reference generation, or ima2 edit <file> --prompt \"<text>\" for image edits.",
113
121
  defaults: "Use ima2 defaults set model/reasoning for persistent defaults; request flags remain per-call overrides.",
114
122
  promptBuilder: "Use ima2 prompt build --message \"...\" to refine prompt intent. Use ima2 gen / ima2 multimode to generate images. Workspace profile settings are UI-only.",
123
+ video: "Use ima2 video \"<prompt>\" to generate video. Supports --ref for image-to-video and reference-to-video modes.",
115
124
  },
116
125
  };
117
126
  }
@@ -381,7 +381,23 @@ export async function generateVideoViaGrok(prompt, ctx, options = {}) {
381
381
  }
382
382
  : await planGrokVideo(prompt, ctx, options);
383
383
  const payload = buildVideoGenerationPayload(plan, { model, sourceImageUrl: srcUrl, referenceImageUrls: refUrls });
384
- const xaiVideoRequestId = await startVideoRequest(ctx, payload, options);
384
+ let xaiVideoRequestId;
385
+ let effectiveModel = model;
386
+ try {
387
+ xaiVideoRequestId = await startVideoRequest(ctx, payload, options);
388
+ }
389
+ catch (e) {
390
+ // Fallback: if 1.5-preview fails, retry with base model
391
+ if (model !== "grok-imagine-video" && e?.status === 400) {
392
+ effectiveModel = "grok-imagine-video";
393
+ const fallbackPayload = buildVideoGenerationPayload(plan, { model: effectiveModel, sourceImageUrl: srcUrl, referenceImageUrls: refUrls });
394
+ xaiVideoRequestId = await startVideoRequest(ctx, fallbackPayload, options);
395
+ logEvent("grok", "video:fallback", { requestId: options.requestId, from: model, to: effectiveModel });
396
+ }
397
+ else {
398
+ throw e;
399
+ }
400
+ }
385
401
  options.onEvent?.({ phase: "submitted", xaiVideoRequestId });
386
402
  logEvent("grok", "video:submitted", { requestId: options.requestId, xaiVideoRequestId, mode: plan.mode });
387
403
  const poll = await pollVideoUntilDone(ctx, xaiVideoRequestId, options);
@@ -447,7 +447,21 @@ export async function generateVideoViaGrok(prompt: string, ctx: RouteRuntimeCont
447
447
  }
448
448
  : await planGrokVideo(prompt, ctx, options);
449
449
  const payload = buildVideoGenerationPayload(plan, { model, sourceImageUrl: srcUrl, referenceImageUrls: refUrls });
450
- const xaiVideoRequestId = await startVideoRequest(ctx, payload, options);
450
+ let xaiVideoRequestId: string;
451
+ let effectiveModel = model;
452
+ try {
453
+ xaiVideoRequestId = await startVideoRequest(ctx, payload, options);
454
+ } catch (e: any) {
455
+ // Fallback: if 1.5-preview fails, retry with base model
456
+ if (model !== "grok-imagine-video" && e?.status === 400) {
457
+ effectiveModel = "grok-imagine-video";
458
+ const fallbackPayload = buildVideoGenerationPayload(plan, { model: effectiveModel, sourceImageUrl: srcUrl, referenceImageUrls: refUrls });
459
+ xaiVideoRequestId = await startVideoRequest(ctx, fallbackPayload, options);
460
+ logEvent("grok", "video:fallback", { requestId: options.requestId, from: model, to: effectiveModel });
461
+ } else {
462
+ throw e;
463
+ }
464
+ }
451
465
  options.onEvent?.({ phase: "submitted", xaiVideoRequestId });
452
466
  logEvent("grok", "video:submitted", { requestId: options.requestId, xaiVideoRequestId, mode: plan.mode });
453
467
  const poll = await pollVideoUntilDone(ctx, xaiVideoRequestId, options);
@@ -62,7 +62,7 @@ export function normalizeGrokImageModel(rawModel) {
62
62
  // Video is a separate generation kind, not an image model. Keep it out of the
63
63
  // image model unions/helpers above so `grok-` image classification is unaffected.
64
64
  const GROK_FALLBACK_VIDEO_MODEL = "grok-imagine-video";
65
- export const VALID_GROK_VIDEO_MODELS = new Set(["grok-imagine-video"]);
65
+ export const VALID_GROK_VIDEO_MODELS = new Set(["grok-imagine-video", "grok-imagine-video-1.5-preview"]);
66
66
  export const VALID_VIDEO_RESOLUTIONS = new Set(["480p", "720p"]);
67
67
  export const VALID_VIDEO_ASPECT_RATIOS = new Set([
68
68
  "1:1",
@@ -74,7 +74,7 @@ export function normalizeGrokImageModel(rawModel: unknown) {
74
74
  // Video is a separate generation kind, not an image model. Keep it out of the
75
75
  // image model unions/helpers above so `grok-` image classification is unaffected.
76
76
  const GROK_FALLBACK_VIDEO_MODEL = "grok-imagine-video";
77
- export const VALID_GROK_VIDEO_MODELS = new Set(["grok-imagine-video"]);
77
+ export const VALID_GROK_VIDEO_MODELS = new Set(["grok-imagine-video", "grok-imagine-video-1.5-preview"]);
78
78
  export const VALID_VIDEO_RESOLUTIONS = new Set(["480p", "720p"]);
79
79
  export const VALID_VIDEO_ASPECT_RATIOS = new Set([
80
80
  "1:1",
@@ -92,7 +92,7 @@ export const MAX_VIDEO_DURATION = 15;
92
92
  export const MAX_REF2V_REFERENCES = 7;
93
93
  export const MAX_REF2V_DURATION = 10;
94
94
 
95
- export type GrokVideoModel = "grok-imagine-video";
95
+ export type GrokVideoModel = "grok-imagine-video" | "grok-imagine-video-1.5-preview";
96
96
  export type VideoResolution = "480p" | "720p";
97
97
  export type VideoAspectRatio = "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3" | "auto";
98
98
  export type VideoMode = "text-to-video" | "image-to-video" | "reference-to-video";
@@ -1,6 +1,7 @@
1
- import { spawnBin } from "../bin/lib/platform.js";
1
+ import { isWin } from "../bin/lib/platform.js";
2
2
  import { config } from "../config.js";
3
3
  import { parseLocalhostPortFromUrl, parseOAuthReadyUrl } from "./runtimePorts.js";
4
+ import { spawn } from "node:child_process";
4
5
  export function startOAuthProxy(options = {}) {
5
6
  const oauthPort = options.oauthPort ?? config.oauth.proxyPort;
6
7
  const restartDelayMs = options.restartDelayMs ?? config.oauth.restartDelayMs;
@@ -11,8 +12,10 @@ export function startOAuthProxy(options = {}) {
11
12
  const spawnProxy = () => {
12
13
  console.log(`Starting openai-oauth on port ${oauthPort}...`);
13
14
  const spawnedAt = Date.now();
14
- const child = spawnBin("npx", ["openai-oauth", "--port", String(oauthPort)], {
15
+ const child = spawn("npx", ["openai-oauth", "--port", String(oauthPort)], {
15
16
  stdio: ["ignore", "pipe", "pipe"],
17
+ shell: isWin,
18
+ windowsHide: true,
16
19
  env: { ...process.env },
17
20
  });
18
21
  currentChild = child;
@@ -1,7 +1,7 @@
1
- import { spawnBin } from "../bin/lib/platform.js";
1
+ import { isWin } from "../bin/lib/platform.js";
2
2
  import { config } from "../config.js";
3
3
  import { parseLocalhostPortFromUrl, parseOAuthReadyUrl } from "./runtimePorts.js";
4
- import type { ChildProcess } from "node:child_process";
4
+ import { type ChildProcess, spawn } from "node:child_process";
5
5
 
6
6
  export function startOAuthProxy(options: any = {}) {
7
7
  const oauthPort = options.oauthPort ?? config.oauth.proxyPort;
@@ -14,8 +14,10 @@ export function startOAuthProxy(options: any = {}) {
14
14
  const spawnProxy = () => {
15
15
  console.log(`Starting openai-oauth on port ${oauthPort}...`);
16
16
  const spawnedAt = Date.now();
17
- const child = spawnBin("npx", ["openai-oauth", "--port", String(oauthPort)], {
17
+ const child = spawn("npx", ["openai-oauth", "--port", String(oauthPort)], {
18
18
  stdio: ["ignore", "pipe", "pipe"],
19
+ shell: isWin,
20
+ windowsHide: true,
19
21
  env: { ...process.env },
20
22
  });
21
23
  currentChild = child;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ima2-gen",
3
- "version": "1.1.17",
3
+ "version": "1.1.18",
4
4
  "description": "Local OAuth image generation studio with classic and node workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "index.html": {
3
- "file": "assets/index-B0re600T.js",
3
+ "file": "assets/index-uBEJn5jz.js",
4
4
  "name": "index",
5
5
  "src": "index.html",
6
6
  "isEntry": true,
@@ -16,11 +16,11 @@
16
16
  "src/components/PromptLibraryPanel.tsx"
17
17
  ],
18
18
  "css": [
19
- "assets/index-CXJEgTOQ.css"
19
+ "assets/index-CIhB_ia7.css"
20
20
  ]
21
21
  },
22
22
  "src/components/NodeCanvas.tsx": {
23
- "file": "assets/NodeCanvas-DR2N5Dib.js",
23
+ "file": "assets/NodeCanvas-jr9WXfNm.js",
24
24
  "name": "NodeCanvas",
25
25
  "src": "src/components/NodeCanvas.tsx",
26
26
  "isDynamicEntry": true,
@@ -32,7 +32,7 @@
32
32
  ]
33
33
  },
34
34
  "src/components/PromptImportDialog.tsx": {
35
- "file": "assets/PromptImportDialog-aNk40wLt.js",
35
+ "file": "assets/PromptImportDialog-C2zGZkyK.js",
36
36
  "name": "PromptImportDialog",
37
37
  "src": "src/components/PromptImportDialog.tsx",
38
38
  "isDynamicEntry": true,
@@ -45,7 +45,7 @@
45
45
  ]
46
46
  },
47
47
  "src/components/PromptImportDiscoverySection.tsx": {
48
- "file": "assets/PromptImportDiscoverySection-B6NKkVBz.js",
48
+ "file": "assets/PromptImportDiscoverySection-N0ZxHLYs.js",
49
49
  "name": "PromptImportDiscoverySection",
50
50
  "src": "src/components/PromptImportDiscoverySection.tsx",
51
51
  "isDynamicEntry": true,
@@ -54,7 +54,7 @@
54
54
  ]
55
55
  },
56
56
  "src/components/PromptImportFolderSection.tsx": {
57
- "file": "assets/PromptImportFolderSection-9-xbe-FM.js",
57
+ "file": "assets/PromptImportFolderSection-BC3dCASZ.js",
58
58
  "name": "PromptImportFolderSection",
59
59
  "src": "src/components/PromptImportFolderSection.tsx",
60
60
  "isDynamicEntry": true,
@@ -63,7 +63,7 @@
63
63
  ]
64
64
  },
65
65
  "src/components/PromptLibraryPanel.tsx": {
66
- "file": "assets/PromptLibraryPanel-CbEY0AM6.js",
66
+ "file": "assets/PromptLibraryPanel-CcVliYnF.js",
67
67
  "name": "PromptLibraryPanel",
68
68
  "src": "src/components/PromptLibraryPanel.tsx",
69
69
  "isDynamicEntry": true,
@@ -75,7 +75,7 @@
75
75
  ]
76
76
  },
77
77
  "src/components/SettingsWorkspace.tsx": {
78
- "file": "assets/SettingsWorkspace-ao9ymIWt.js",
78
+ "file": "assets/SettingsWorkspace-CiB4ux7E.js",
79
79
  "name": "SettingsWorkspace",
80
80
  "src": "src/components/SettingsWorkspace.tsx",
81
81
  "isDynamicEntry": true,
@@ -84,7 +84,7 @@
84
84
  ]
85
85
  },
86
86
  "src/components/agent/AgentWorkspace.tsx": {
87
- "file": "assets/AgentWorkspace-CLHwx6u4.js",
87
+ "file": "assets/AgentWorkspace-BTuPjlDH.js",
88
88
  "name": "AgentWorkspace",
89
89
  "src": "src/components/agent/AgentWorkspace.tsx",
90
90
  "isDynamicEntry": true,
@@ -93,7 +93,7 @@
93
93
  ]
94
94
  },
95
95
  "src/components/canvas-mode/index.ts": {
96
- "file": "assets/index-DP88bEQf.js",
96
+ "file": "assets/index-C93CfR9P.js",
97
97
  "name": "index",
98
98
  "src": "src/components/canvas-mode/index.ts",
99
99
  "isDynamicEntry": true,
@@ -102,7 +102,7 @@
102
102
  ]
103
103
  },
104
104
  "src/components/card-news/CardNewsWorkspace.tsx": {
105
- "file": "assets/CardNewsWorkspace-6y_HNp3I.js",
105
+ "file": "assets/CardNewsWorkspace-DmqCMnIx.js",
106
106
  "name": "CardNewsWorkspace",
107
107
  "src": "src/components/card-news/CardNewsWorkspace.tsx",
108
108
  "isDynamicEntry": true,
@@ -111,7 +111,7 @@
111
111
  ]
112
112
  },
113
113
  "src/components/prompt-builder/PromptBuilderPanel.tsx": {
114
- "file": "assets/PromptBuilderPanel-BQlPtGGm.js",
114
+ "file": "assets/PromptBuilderPanel-CoWjqQZS.js",
115
115
  "name": "PromptBuilderPanel",
116
116
  "src": "src/components/prompt-builder/PromptBuilderPanel.tsx",
117
117
  "isDynamicEntry": true,