@j-o-r/hello-dave 0.1.1 → 0.1.4

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 (173) hide show
  1. package/CHANGELOG.md +42 -25
  2. package/README.md +81 -221
  3. package/TODO.md +173 -35
  4. package/agents/agent_creator.js +105 -0
  5. package/agents/agent_creator.prompt.md +371 -0
  6. package/agents/ask_agent.js +64 -127
  7. package/agents/claude_agent.js +68 -0
  8. package/agents/code_agent.js +55 -135
  9. package/agents/code_agent.prompt.md +50 -0
  10. package/agents/echo_agent.js +76 -0
  11. package/agents/financial_expert.js +75 -0
  12. package/agents/gpt_agent.js +52 -103
  13. package/agents/gpt_code.js +81 -0
  14. package/agents/grok_agent.js +58 -114
  15. package/agents/minimax_agent.js +92 -0
  16. package/agents/mureka_agent.js +77 -0
  17. package/agents/planner_agent.js +172 -0
  18. package/agents/stability_agent.js +87 -0
  19. package/agents/test_agent.js +75 -157
  20. package/agents/weather_agent.js +73 -0
  21. package/agents/workflow_agent.js +189 -0
  22. package/bin/dave.js +436 -184
  23. package/docs/bin-dave.md +85 -35
  24. package/docs/cdn-ssh.md +100 -0
  25. package/docs/creating-agents.md +301 -0
  26. package/docs/creating-toolsets.md +336 -0
  27. package/docs/docs-organization.md +48 -0
  28. package/docs/project-overview.md +86 -51
  29. package/lib/API/elevenlabs.io/music.compose.md +441 -0
  30. package/lib/API/elevenlabs.io/music.create-composition-plan.md +370 -0
  31. package/lib/API/elevenlabs.io/music.stream.md +425 -0
  32. package/lib/API/lalal.ai/lalal.js +445 -0
  33. package/lib/API/lalal.ai/openapi.json +2614 -0
  34. package/lib/API/minimax/ImageToolset.js +82 -37
  35. package/lib/API/minimax/MusicToolset.js +125 -79
  36. package/lib/API/minimax/VideoToolset.js +170 -167
  37. package/lib/API/minimax/image.js +5 -1
  38. package/lib/API/minimax/music.js +210 -23
  39. package/lib/API/minimax/video.js +242 -53
  40. package/lib/API/mureka/MusicToolset.js +646 -0
  41. package/lib/API/mureka/README.md +41 -0
  42. package/lib/API/mureka/index.js +7 -0
  43. package/lib/API/mureka/music.js +658 -0
  44. package/lib/API/openai.com/index.js +7 -0
  45. package/lib/API/openai.com/{reponses/text.js → responses.js} +64 -18
  46. package/lib/API/openai.com/video.create.character.md +40 -0
  47. package/lib/API/openai.com/video.create.md +219 -0
  48. package/lib/API/openai.com/video.delete.md +44 -0
  49. package/lib/API/openai.com/video.download.md +31 -0
  50. package/lib/API/openai.com/video.edit.md +155 -0
  51. package/lib/API/openai.com/video.extend.md +166 -0
  52. package/lib/API/openai.com/video.fetch.character.md +43 -0
  53. package/lib/API/openai.com/video.js +784 -0
  54. package/lib/API/openai.com/video.list.md +201 -0
  55. package/lib/API/openai.com/video.remix.md +175 -0
  56. package/lib/API/openai.com/video.retrieve.md +139 -0
  57. package/lib/API/openai.com/videoToolset.js +616 -0
  58. package/lib/API/stability.ai/ImageToolset.js +131 -40
  59. package/lib/API/stability.ai/MusicToolset.js +79 -47
  60. package/lib/API/stability.ai/audio.js +63 -131
  61. package/lib/API/x.ai/chat.responses.md +1040 -0
  62. package/lib/API/x.ai/image.js +229 -59
  63. package/lib/API/x.ai/imageToolset.js +376 -0
  64. package/lib/API/x.ai/index.js +1 -1
  65. package/lib/API/x.ai/responses.js +9 -18
  66. package/lib/Agent.js +271 -0
  67. package/lib/Agent.js.old +284 -0
  68. package/lib/AgentLauncher.js +562 -0
  69. package/lib/Cli.js +87 -13
  70. package/lib/Prompt.js +23 -1
  71. package/lib/Session.js +5 -4
  72. package/lib/ToolSet.js +102 -6
  73. package/lib/agentLoader.js +369 -0
  74. package/lib/cdn.js +67 -231
  75. package/lib/{CdnToolset.js → cdnToolset.js} +47 -64
  76. package/lib/defaultToolsets.js +43 -0
  77. package/lib/fafs.js +1 -1
  78. package/lib/genericToolset.js +442 -119
  79. package/lib/handOffToolset.js +179 -0
  80. package/lib/index.js +34 -27
  81. package/lib/toolsetLoader.js +248 -0
  82. package/package.json +11 -5
  83. package/types/API/lalal.ai/lalal.d.ts +116 -0
  84. package/types/API/minimax/image.d.ts +2 -1
  85. package/types/API/minimax/music.d.ts +189 -26
  86. package/types/API/minimax/video.d.ts +100 -31
  87. package/types/API/mureka/index.d.ts +7 -0
  88. package/types/API/mureka/music.d.ts +472 -0
  89. package/types/API/openai.com/index.d.ts +7 -0
  90. package/types/API/openai.com/{reponses/text.d.ts → responses.d.ts} +11 -11
  91. package/types/API/openai.com/video.d.ts +409 -0
  92. package/types/API/openai.com/videoToolset.d.ts +24 -0
  93. package/types/API/stability.ai/audio.d.ts +14 -103
  94. package/types/API/stability.ai/image.d.ts +2 -2
  95. package/types/API/x.ai/image.d.ts +138 -26
  96. package/types/API/x.ai/imageToolset.d.ts +3 -0
  97. package/types/API/x.ai/index.d.ts +1 -1
  98. package/types/API/x.ai/responses.d.ts +4 -4
  99. package/types/Agent.d.ts +123 -0
  100. package/types/AgentLauncher.d.ts +222 -0
  101. package/types/Cli.d.ts +28 -8
  102. package/types/Prompt.d.ts +23 -5
  103. package/types/Session.d.ts +1 -1
  104. package/types/ToolSet.d.ts +10 -0
  105. package/types/agentLoader.d.ts +78 -0
  106. package/types/cdn.d.ts +15 -90
  107. package/types/defaultToolsets.d.ts +9 -0
  108. package/types/fafs.d.ts +1 -1
  109. package/types/genericToolset.d.ts +1 -1
  110. package/types/handOffToolset.d.ts +28 -0
  111. package/types/index.d.ts +19 -17
  112. package/types/toolsetLoader.d.ts +114 -0
  113. package/utils/format_log.js +101 -23
  114. package/utils/launch_agent.js +18 -0
  115. package/utils/list_sessions.sh +13 -5
  116. package/utils/search_sessions.sh +65 -29
  117. package/utils/toolsets.js +33 -0
  118. package/README.md.bak.1779452127 +0 -240
  119. package/agents/codeserver.sh +0 -47
  120. package/agents/daisy_agent.js +0 -173
  121. package/agents/docs_agent.js +0 -148
  122. package/agents/memory_agent.js +0 -263
  123. package/agents/minimax.js +0 -173
  124. package/agents/npm_agent.js +0 -202
  125. package/agents/prompt_agent.js +0 -133
  126. package/agents/readme_agent.js +0 -148
  127. package/agents/spawn_agent.js +0 -160
  128. package/agents/stability.js +0 -173
  129. package/agents/todo_agent.js +0 -175
  130. package/bin/codeDave +0 -58
  131. package/docs/agent-dave-websocket-protocol.md +0 -180
  132. package/docs/agent-manager.md +0 -244
  133. package/docs/codeserver-pattern.md +0 -191
  134. package/docs/generic-toolset.md +0 -326
  135. package/docs/howtos/agent-networking.md +0 -253
  136. package/docs/howtos/spawn-agents.md.bak +0 -200
  137. package/docs/howtos/spawn-agents.md.bak_new +0 -200
  138. package/docs/multi-agent-clusters.md +0 -265
  139. package/docs/music-toolsets.md +0 -137
  140. package/docs/path-resolution-best-practices.md +0 -104
  141. package/docs/plans/minimax-music-generation.md +0 -80
  142. package/docs/plans/unified-agent-architecture.md +0 -146
  143. package/docs/plans/websocket-streaming-plan.md.bak +0 -317
  144. package/docs/prompt/spawn_agent.md +0 -175
  145. package/docs/prompt/spawn_agent.md.bak +0 -201
  146. package/docs/prompt/task_clarification_and_documentation.md +0 -35
  147. package/docs/prompt-class.md +0 -141
  148. package/docs/todo-archive-infra-2026-04-21.md +0 -15
  149. package/docs/todo-archive-v0.0.8.md +0 -1
  150. package/docs/todo-archive-v0.1.0.md +0 -32
  151. package/docs/todo-archive.md +0 -44
  152. package/docs/tools-syntax-validation.md +0 -121
  153. package/docs/toolset.md +0 -164
  154. package/docs/xai-responses.md +0 -111
  155. package/docs/xai_collections.md +0 -106
  156. package/lib/API/x.ai/ImageToolset.js +0 -165
  157. package/lib/API/x.ai/text.js +0 -415
  158. package/lib/AgentClient.js +0 -248
  159. package/lib/AgentManager.js +0 -245
  160. package/lib/AgentServer.js +0 -404
  161. package/lib/wsCli.js +0 -287
  162. package/lib/wsIO.js +0 -90
  163. package/types/API/x.ai/text.d.ts +0 -286
  164. package/types/AgentClient.d.ts +0 -109
  165. package/types/AgentManager.d.ts +0 -100
  166. package/types/AgentServer.d.ts +0 -89
  167. package/types/wsCli.d.ts +0 -17
  168. package/types/wsIO.d.ts +0 -30
  169. package/utils/test.sh +0 -46
  170. /package/docs/{suggestions.md → _notes/token-counts.md} +0 -0
  171. /package/lib/API/openai.com/{reponses/MESSAGES.md → MESSAGES.md} +0 -0
  172. /package/types/API/{x.ai/ImageToolset.d.ts → mureka/MusicToolset.d.ts} +0 -0
  173. /package/types/{CdnToolset.d.ts → cdnToolset.d.ts} +0 -0
@@ -1,40 +1,68 @@
1
+ import { request as doRequest } from '@j-o-r/apiserver';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+
1
5
  /**
2
6
  * @file lib/API/x.ai/image.js
3
7
  * @module x.ai/image
4
- * @description Pure HTTP wrapper for the xAI Grok Imagine API.
8
+ * @description Pure HTTP wrapper for the xAI Grok Imagine API (v1).
9
+ *
10
+ * Provides a clean, minimal, and robust interface for:
11
+ * - Text-to-image generation via `generateImage()`
12
+ * - Natural language image editing (with up to 3 reference images) via `editImage()`
13
+ * - Image-to-video generation via `generateVideo()` — fully automatic with internal polling
14
+ *
15
+ * All public functions handle:
16
+ * - Authentication via the `XAIKEY` environment variable (Bearer token)
17
+ * - Request formatting and validation
18
+ * - Automatic local file saving to `.cache/xai/` (with timestamped filenames)
19
+ * - Robust error handling and propagation
20
+ *
21
+ * Video generation is asynchronous on the xAI server side. The `generateVideo()` function
22
+ * submits the request and internally polls `/videos/{request_id}` until the result is ready
23
+ * (or times out). The helper `pollVideoResult()` is exported for advanced control.
5
24
  *
6
- * Provides a clean, minimal interface for:
7
- * - Text-to-image generation
8
- * - Natural language image editing (supports up to 3 reference images)
9
- * - Image-to-video generation (fully automatic with internal polling)
25
+ * **Supported Models** (current as of June 2026):
26
+ * - Image generation & editing: `grok-imagine-image-quality`, `grok-imagine-image`
27
+ * - Video generation: `grok-imagine-video`
10
28
  *
11
- * All functions handle authentication, request formatting, local file saving,
12
- * and error handling. Video generation waits internally until the result is ready.
29
+ * **Source Documentation**: Derived from the official xAI Imagine API documentation in the
30
+ * accompanying Markdown files:
31
+ * - `image.md` (overview, pricing, capabilities, supported models)
32
+ * - `image.to.generation.md` (text-to-image details, aspect ratios, resolution, n, response_format)
33
+ * - `image.editing.md` (image editing, multi-image support, compositing)
34
+ * - `image.to.video.md` (image-to-video specifics, duration, motion prompts)
13
35
  *
14
- * @see ./image.md for the full API specification and examples
36
+ * @see ./image.md
37
+ * @see ./image.to.generation.md
38
+ * @see ./image.editing.md
39
+ * @see ./image.to.video.md
15
40
  */
16
41
 
17
42
  /**
18
43
  * @constant {string} BASE_URL
19
- * @description Base URL for the xAI API.
44
+ * @description Base URL for the xAI API (version 1). All endpoints are relative to this.
45
+ * @type {string}
20
46
  */
21
47
  const BASE_URL = 'https://api.x.ai/v1';
22
48
 
23
49
  /**
24
50
  * @constant {string} TMP_DIR
25
- * @description Local directory where generated images and videos are saved.
51
+ * @description Absolute path to the local directory where generated images and videos are saved.
52
+ * The directory is created automatically (recursively) if it does not exist.
53
+ * Location: `<process.cwd()>/.cache/xai/`
54
+ * @type {string}
26
55
  */
27
56
  const TMP_DIR = path.join(process.cwd(), '.cache', 'xai');
28
57
 
29
- import { request as doRequest } from '@j-o-r/apiserver';
30
- import fs from 'fs/promises';
31
- import path from 'path';
32
-
33
58
  /**
34
59
  * Builds authenticated headers for xAI requests.
35
60
  *
36
- * @returns {Object} Headers containing Authorization Bearer token.
37
- * @throws {Error} If `XAI_API_KEY` environment variable is not set.
61
+ * Requires the `XAIKEY` environment variable to be set.
62
+ * Throws a descriptive error if the key is missing.
63
+ *
64
+ * @returns {Object} Headers object containing `Authorization: Bearer <key>` and `Content-Type: application/json`.
65
+ * @throws {Error} If `process.env.XAIKEY` is not set.
38
66
  */
39
67
  const getHeaders = () => {
40
68
  if (!process.env.XAIKEY) {
@@ -47,23 +75,35 @@ const getHeaders = () => {
47
75
  };
48
76
 
49
77
  /**
50
- * Ensures the temporary directory for media files exists.
78
+ * Ensures the temporary directory (`TMP_DIR`) for media files exists.
79
+ *
80
+ * Creates the directory recursively if needed. Safe to call multiple times.
51
81
  *
52
82
  * @async
53
- * @returns {Promise<void>}
83
+ * @returns {Promise<void>} Resolves when the directory is guaranteed to exist.
54
84
  */
55
85
  async function ensureTmpDir() {
56
86
  await fs.mkdir(TMP_DIR, { recursive: true });
57
87
  }
58
88
 
59
89
  /**
60
- * Saves image or video data (URL, base64, Buffer, etc.) to a local file.
90
+ * Saves image or video data to a local file in `TMP_DIR`.
91
+ *
92
+ * Supports multiple input formats:
93
+ * - Remote HTTP(S) URL (auto-downloads)
94
+ * - Base64 string (plain or data URI)
95
+ * - Node.js `Buffer`
96
+ * - `Blob` or `ArrayBuffer`
97
+ *
98
+ * Automatically creates the target directory if necessary.
99
+ * Filenames are timestamped to avoid collisions.
61
100
  *
62
101
  * @async
63
102
  * @param {string|Buffer|Blob|ArrayBuffer} data - Media content to save.
64
- * @param {string} [filenamePrefix='xai-media'] - Prefix for the saved file.
65
- * @param {string} [ext='png'] - File extension.
103
+ * @param {string} [filenamePrefix='xai-media'] - Prefix for the generated filename.
104
+ * @param {string} [ext='png'] - File extension (e.g. 'png', 'jpg', 'mp4').
66
105
  * @returns {Promise<string>} Absolute path to the saved file.
106
+ * @throws {Error} On unsupported data type, download failure, or write error.
67
107
  */
68
108
  async function saveMediaToLocal(data, filenamePrefix = 'xai-media', ext = 'png') {
69
109
  await ensureTmpDir();
@@ -95,12 +135,20 @@ async function saveMediaToLocal(data, filenamePrefix = 'xai-media', ext = 'png')
95
135
  }
96
136
 
97
137
  /**
98
- * Prepares an image input into the format expected by xAI.
99
- * Accepts URLs, base64 data URIs, local file paths, Buffers, or Blobs.
138
+ * Prepares an image input into the format expected by the xAI API:
139
+ * `{ url: string, type: 'image_url' }`
140
+ *
141
+ * Accepts a wide variety of inputs and normalizes them:
142
+ * - Public HTTP(S) URL → passed through
143
+ * - Base64 data URI (`data:image/...`) → passed through
144
+ * - Local filesystem path → read and converted to base64 data URI
145
+ * - `Buffer` → converted to PNG base64 data URI
146
+ * - `Blob` → converted to PNG base64 data URI
100
147
  *
101
148
  * @async
102
- * @param {string|Buffer|Blob} imageInput - Image source.
103
- * @returns {Promise<Object>} Object with `url` and `type`.
149
+ * @param {string|Buffer|Blob} imageInput - Image source (URL, path, base64, Buffer, or Blob).
150
+ * @returns {Promise<Object>} Object with `url` (string) and `type: 'image_url'`.
151
+ * @throws {Error} If input is missing or of an unsupported type.
104
152
  */
105
153
  async function prepareImageInput(imageInput) {
106
154
  if (!imageInput) throw new Error('Image input is required');
@@ -145,21 +193,56 @@ async function prepareImageInput(imageInput) {
145
193
  ============================================================ */
146
194
 
147
195
  /**
148
- * Generates one or more images from a text prompt using Grok Imagine.
196
+ * Generates one or more images from a text prompt using Grok Imagine models.
197
+ *
198
+ * **Supported models** (current):
199
+ * - `grok-imagine-image-quality` — Recommended primary model.
200
+ * - `grok-imagine-image` — Alternative image model.
201
+ *
202
+ * Default: `grok-imagine-image-quality`.
203
+ *
204
+ * Supported features:
205
+ * - Up to 10 images per request (`n`)
206
+ * - Custom aspect ratios (see `image.to.generation.md` for the full table)
207
+ * - Resolutions: `1k` or `2k`
208
+ * - Response formats: `url` (temporary public URL) or `b64_json` (base64)
209
+ *
210
+ * All generated images are automatically saved to the local `.cache/xai/` directory.
211
+ * The function returns both the local path and the original API response data.
149
212
  *
150
213
  * @async
151
214
  * @function generateImage
152
- * @param {string} prompt - Detailed text prompt describing the desired image.
215
+ * @param {string} prompt - Detailed text prompt describing the desired image(s). Required.
153
216
  * @param {Object} [options={}] - Optional generation parameters.
154
- * @param {string} [options.model='grok-imagine-image-quality'] - Model to use.
155
- * @param {number} [options.n=1] - Number of images to generate (1–10).
156
- * @param {string} [options.response_format='url'] - `'url'` or `'b64_json'`.
157
- * @returns {Promise<Object>} Result containing `local_path`, `url` (or `base64`), and metadata.
158
- * @throws {Error} On missing prompt or API errors.
217
+ * @param {string} [options.model='grok-imagine-image-quality'] - Model identifier. Supported values: `grok-imagine-image-quality` (recommended) or `grok-imagine-image`.
218
+ * @param {number} [options.n=1] - Number of images to generate. Range: 1–10. Default: 1.
219
+ * @param {string} [options.response_format='url'] - `'url'` (default) or `'b64_json'`.
220
+ * @param {string} [options.aspect_ratio] - Desired aspect ratio (e.g. `'16:9'`, `'1:1'`, `'auto'`).
221
+ * @param {string} [options.resolution] - Output resolution (`'1k'` or `'2k'`).
222
+ * @returns {Promise<Object>} Result object containing:
223
+ * - `local_path` (string) – absolute path to the saved file
224
+ * - `url` (string, if response_format=url) – temporary public URL
225
+ * - `base64` (string, if response_format=b64_json)
226
+ * - `revised_prompt` (string) – model-revised version of the prompt (if provided)
227
+ * - `raw` (Object) – full original API response
228
+ * @throws {Error} If `prompt` is missing/invalid, or on any API/network error.
159
229
  *
160
230
  * @example
231
+ * // Basic usage (uses recommended model)
161
232
  * const result = await generateImage("A futuristic city at night");
162
233
  * console.log(result.local_path);
234
+ *
235
+ * @example
236
+ * // Multiple images with custom aspect ratio and resolution
237
+ * const results = await generateImage("Mountain landscape at sunrise", {
238
+ * n: 4,
239
+ * aspect_ratio: "16:9",
240
+ * resolution: "2k"
241
+ * });
242
+ *
243
+ * @example
244
+ * // Using the alternative model
245
+ * const result = await generateImage("...", { model: "grok-imagine-image" });
163
246
  */
164
247
  async function generateImage(prompt, options = {}) {
165
248
  if (!prompt || typeof prompt !== 'string') {
@@ -174,6 +257,10 @@ async function generateImage(prompt, options = {}) {
174
257
  // Note: "size" is not supported by the xAI Grok Imagine API
175
258
  };
176
259
 
260
+ // Add optional params if provided
261
+ if (options.aspect_ratio) body.aspect_ratio = options.aspect_ratio;
262
+ if (options.resolution) body.resolution = options.resolution;
263
+
177
264
  const headers = getHeaders();
178
265
  const res = await doRequest(`${BASE_URL}/images/generations`, 'POST', headers, body);
179
266
 
@@ -213,27 +300,49 @@ async function generateImage(prompt, options = {}) {
213
300
 
214
301
  /**
215
302
  * Edits one or more images using a natural language prompt.
216
- * Supports up to 3 reference images for compositing, style transfer, or multi-subject scenes.
303
+ *
304
+ * Supports up to 3 reference images for advanced compositing, style transfer,
305
+ * multi-subject scenes, or object insertion/removal.
306
+ *
307
+ * The model analyzes the provided image(s) and applies the edits described in the prompt.
308
+ *
309
+ * **Official API payload (per current xAI Inference API spec)**:
310
+ * - Single image: `"image": { "url": "...", "type": "image_url" }`
311
+ * - Multiple images (up to 3): `"images": [ { "url": "...", "type": "image_url" }, ... ]`
312
+ * (array of objects — **not** `image_urls`)
313
+ *
314
+ * When using multiple images, refer to them in the prompt as <IMAGE_0>, <IMAGE_1>, <IMAGE_2>, etc.
315
+ *
316
+ * **Supported models** (same as generation):
317
+ * - `grok-imagine-image-quality` (recommended)
318
+ * - `grok-imagine-image`
319
+ *
320
+ * All output images are automatically saved locally.
217
321
  *
218
322
  * @async
219
323
  * @function editImage
220
- * @param {string} prompt - Description of the desired edit.
221
- * @param {string|Buffer|Blob|Array<string|Buffer|Blob>} imageInputs - One or more images (URL, path, base64, Buffer, or Blob).
324
+ * @param {string} prompt - Natural language description of the desired edit(s). Required.
325
+ * @param {string|Buffer|Blob|Array<string|Buffer|Blob>} imageInputs - One or more reference images.
326
+ * Accepts URLs, local paths, base64, Buffers, or Blobs. **Maximum: 3 images**.
222
327
  * @param {Object} [options={}] - Optional parameters.
223
- * @param {string} [options.model='grok-imagine-image-quality']
224
- * @param {number} [options.n=1]
225
- * @param {string} [options.response_format='url']
226
- * @returns {Promise<Object>} Result with edited image.
227
- * @throws {Error} If more than 3 images are provided or on API error.
328
+ * @param {string} [options.model='grok-imagine-image-quality'] - Model identifier. Supported values: `grok-imagine-image-quality` (recommended) or `grok-imagine-image`.
329
+ * @param {number} [options.n=1] - Number of output images. Range: 1–10. Default: 1.
330
+ * @param {string} [options.response_format='url'] - `'url'` or `'b64_json'`.
331
+ * @param {string} [options.aspect_ratio] - Desired aspect ratio for output.
332
+ * @param {string} [options.resolution] - Output resolution (`'1k'` or `'2k'`).
333
+ * @returns {Promise<Object>} Result object (same shape as `generateImage`):
334
+ * - `local_path`, `url`/`base64`, `revised_prompt`, `raw`
335
+ * @throws {Error} If prompt or imageInputs are missing, more than 3 images are supplied,
336
+ * or on API/network error.
228
337
  *
229
338
  * @example
230
- * // Single image edit
339
+ * // Single-image edit (recommended model)
231
340
  * const result = await editImage("Make this a pencil sketch", "./photo.png");
232
341
  *
233
342
  * @example
234
343
  * // Multi-image editing (up to 3)
235
344
  * const result = await editImage(
236
- * "Combine these two people into one scene",
345
+ * "Combine <IMAGE_0> and <IMAGE_1> into one scene with a city background",
237
346
  * ["./person1.png", "./person2.png"]
238
347
  * );
239
348
  */
@@ -252,11 +361,22 @@ async function editImage(prompt, imageInputs, options = {}) {
252
361
  const body = {
253
362
  model: options.model || 'grok-imagine-image-quality',
254
363
  prompt,
255
- image: preparedImages.length === 1 ? preparedImages[0] : preparedImages,
256
364
  n: options.n || 1,
257
365
  response_format: options.response_format || 'url'
258
366
  };
259
367
 
368
+ if (preparedImages.length === 1) {
369
+ // Single image: object form (official docs example)
370
+ body.image = preparedImages[0];
371
+ } else {
372
+ // Multi-image (2–3): array of objects under the official `images` field
373
+ // (per current Inference API spec — not `image_urls`)
374
+ body.images = preparedImages;
375
+ }
376
+
377
+ if (options.aspect_ratio) body.aspect_ratio = options.aspect_ratio;
378
+ if (options.resolution) body.resolution = options.resolution;
379
+
260
380
  const headers = getHeaders();
261
381
  const res = await doRequest(`${BASE_URL}/images/edits`, 'POST', headers, body);
262
382
 
@@ -294,7 +414,32 @@ async function editImage(prompt, imageInputs, options = {}) {
294
414
  INTERNAL: Poll video result
295
415
  ============================================================ */
296
416
 
297
- async function _pollVideoResult(requestId, maxAttempts = 60, intervalMs = 5000) {
417
+ /**
418
+ * Polls the xAI video status endpoint until generation completes or fails.
419
+ *
420
+ * This is an internal helper used by `generateVideo()`, but it is exported for
421
+ * advanced use cases where you want manual control over polling parameters.
422
+ *
423
+ * The xAI video endpoint returns `status: 'done'`, `'failed'`, or `'expired'`.
424
+ * On success it also provides `video.url`.
425
+ *
426
+ * **Timeout note**: Video generation can be slow. The default `intervalMs` was
427
+ * increased (and the function exported) to give callers more flexibility.
428
+ * Recommended values: `intervalMs` 15000–30000, `maxAttempts` 60+ for longer videos.
429
+ *
430
+ * @async
431
+ * @function pollVideoResult
432
+ * @param {string} requestId - The `request_id` returned from a `/videos/generations` call. Required.
433
+ * @param {number} [maxAttempts=60] - Maximum number of polling attempts. Default: 60.
434
+ * @param {number} [intervalMs=15000] - Milliseconds to wait between polls. Default: 15000.
435
+ * @returns {Promise<Object>} Result containing:
436
+ * - `local_path` (string) – path to the saved MP4
437
+ * - `url` (string) – public video URL
438
+ * - `status: 'done'`
439
+ * - `raw` (Object) – full response
440
+ * @throws {Error} On failure/expiration status, missing request_id, or polling timeout.
441
+ */
442
+ async function pollVideoResult(requestId, maxAttempts = 60, intervalMs = 15000) {
298
443
  if (!requestId) throw new Error('Video request_id is required');
299
444
 
300
445
  const headers = getHeaders();
@@ -316,14 +461,14 @@ async function _pollVideoResult(requestId, maxAttempts = 60, intervalMs = 5000)
316
461
  }
317
462
 
318
463
  if (data.status === 'failed' || data.status === 'expired') {
319
- throw new Error(`Video generation ${data.status}: ${JSON.stringify(data)}`);
464
+ throw new Error(`Video generation ${data.status}: ${JSON.stringify(data)} request: ${requestId}`);
320
465
  }
321
466
  }
322
467
 
323
468
  await new Promise(resolve => setTimeout(resolve, intervalMs));
324
469
  }
325
470
 
326
- throw new Error(`Timeout polling video request ${requestId}`);
471
+ throw new Error(`Timeout polling video request: ${requestId}`);
327
472
  }
328
473
 
329
474
  /* ============================================================
@@ -331,28 +476,52 @@ async function _pollVideoResult(requestId, maxAttempts = 60, intervalMs = 5000)
331
476
  ============================================================ */
332
477
 
333
478
  /**
334
- * Generates a video from a still image and text prompt.
335
- * This function is fully automatic — it submits the request and polls internally
336
- * until the video is ready before returning.
479
+ * Generates a video from a still image + text motion prompt.
480
+ *
481
+ * This function is **fully automatic**: it posts to `/videos/generations` and then
482
+ * internally calls `pollVideoResult()` until the video is ready.
483
+ *
484
+ * **Supported model**: Only `grok-imagine-video` (the sole model for image-to-video generation).
485
+ *
486
+ * The provided image becomes the first frame; the prompt describes the desired motion/animation.
487
+ * Supports public URLs or base64 data URIs for the source image.
488
+ *
489
+ * Video parameters (from official docs):
490
+ * - `duration`: 1–15 seconds (affects pricing)
491
+ * - `aspect_ratio`, `resolution`
492
+ *
493
+ * **Important**: Video generation can take a long time and is prone to polling timeouts.
494
+ * Use the exported `pollVideoResult()` directly with higher `intervalMs`/`maxAttempts`
495
+ * if you encounter frequent timeouts.
337
496
  *
338
497
  * @async
339
498
  * @function generateVideo
340
- * @param {string} prompt - Description of the desired motion or animation.
341
- * @param {string|Buffer|Blob} imageInput - Source image (URL, path, base64, Buffer, or Blob).
499
+ * @param {string} prompt - Description of the desired motion or animation. Required.
500
+ * @param {string|Buffer|Blob} imageInput - Source image (URL, local path, base64, Buffer, or Blob). Required.
342
501
  * @param {Object} [options={}] - Optional video parameters.
343
- * @param {number} [options.duration=8] - Video duration in seconds.
344
- * @param {string} [options.aspect_ratio='16:9'] - Aspect ratio.
502
+ * @param {string} [options.model='grok-imagine-video'] - Model identifier (only `grok-imagine-video` is supported).
503
+ * @param {number} [options.duration=8] - Video length in seconds (1–15). Default: 8.
504
+ * @param {string} [options.aspect_ratio='16:9'] - Aspect ratio for the video.
345
505
  * @param {string} [options.resolution='720p'] - Output resolution.
346
- * @returns {Promise<Object>} Result containing `local_path` and `url` of the generated video.
347
- * @throws {Error} On missing inputs or generation failure.
506
+ * @returns {Promise<Object>} Result containing `local_path`, `url`, `status: 'done'`, and `raw`.
507
+ * @throws {Error} If prompt or imageInput is missing, or on generation failure/timeout.
348
508
  *
349
509
  * @example
510
+ * // Basic usage with public image URL
350
511
  * const result = await generateVideo(
351
512
  * "Make the water crash down and slowly pan out",
352
513
  * "https://example.com/waterfall.png",
353
514
  * { duration: 12 }
354
515
  * );
355
516
  * console.log(result.local_path);
517
+ *
518
+ * @example
519
+ * // Using a local file with custom aspect ratio
520
+ * const result = await generateVideo(
521
+ * "Animate the character walking forward",
522
+ * "./character.png",
523
+ * { duration: 10, aspect_ratio: "9:16" }
524
+ * );
356
525
  */
357
526
  async function generateVideo(prompt, imageInput, options = {}) {
358
527
  if (!prompt) throw new Error('generateVideo() requires a prompt');
@@ -382,12 +551,13 @@ async function generateVideo(prompt, imageInput, options = {}) {
382
551
  throw new Error('No request_id returned from video generation');
383
552
  }
384
553
 
385
- // Automatically poll until ready
386
- return await _pollVideoResult(request_id);
554
+ // Automatically poll until ready (uses updated defaults in pollVideoResult)
555
+ return await pollVideoResult(request_id);
387
556
  }
388
557
 
389
558
  export {
390
559
  generateImage,
391
560
  editImage,
392
- generateVideo
393
- };
561
+ generateVideo,
562
+ pollVideoResult
563
+ };