@gobi-ai/cli 0.9.0 → 0.9.2
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.
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
"name": "gobi-ai"
|
|
5
5
|
},
|
|
6
6
|
"description": "Claude Code plugin for the Gobi collaborative knowledge platform CLI",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.9.1",
|
|
8
8
|
"plugins": [
|
|
9
9
|
{
|
|
10
10
|
"name": "gobi",
|
|
11
11
|
"description": "Manage the Gobi collaborative knowledge platform from the command line. Search and ask brains, publish brain documents, create threads, manage sessions, generate images and videos.",
|
|
12
|
-
"version": "0.
|
|
12
|
+
"version": "0.9.1",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "gobi-ai"
|
|
15
15
|
},
|
package/dist/commands/media.js
CHANGED
|
@@ -3,7 +3,7 @@ import { BASE_URL, POLL_MAX_DURATION_MS } from "../constants.js";
|
|
|
3
3
|
import { getValidToken } from "../auth/manager.js";
|
|
4
4
|
import { ApiError } from "../errors.js";
|
|
5
5
|
import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
|
|
6
|
-
// ──
|
|
6
|
+
// ── Helpers ──
|
|
7
7
|
async function pollStatus(path, terminalStates, intervalMs = 3000) {
|
|
8
8
|
const start = Date.now();
|
|
9
9
|
while (Date.now() - start < POLL_MAX_DURATION_MS) {
|
|
@@ -12,10 +12,16 @@ async function pollStatus(path, terminalStates, intervalMs = 3000) {
|
|
|
12
12
|
const status = data.status || "";
|
|
13
13
|
if (terminalStates.includes(status))
|
|
14
14
|
return data;
|
|
15
|
+
// If no status field but downloadUrl exists, treat as completed
|
|
16
|
+
if (!status && extractImageUrl(data))
|
|
17
|
+
return data;
|
|
15
18
|
await new Promise((r) => setTimeout(r, intervalMs));
|
|
16
19
|
}
|
|
17
20
|
throw new Error(`Polling timed out after ${POLL_MAX_DURATION_MS / 1000}s`);
|
|
18
21
|
}
|
|
22
|
+
function extractImageUrl(data) {
|
|
23
|
+
return (data.downloadUrl || data.download_url || data.url);
|
|
24
|
+
}
|
|
19
25
|
export function registerMediaCommand(program) {
|
|
20
26
|
const media = program
|
|
21
27
|
.command("media")
|
|
@@ -240,7 +246,7 @@ export function registerMediaCommand(program) {
|
|
|
240
246
|
.command("image-generate")
|
|
241
247
|
.description("Generate an image from a text prompt. Types: image (default), thumbnail (YouTube-optimized), asset (logo/product). Aspect ratios: 1:1, 16:9, 9:16, 4:3, 3:4")
|
|
242
248
|
.requiredOption("--prompt <prompt>", "Text prompt for image generation")
|
|
243
|
-
.
|
|
249
|
+
.option("--name <name>", "Name for the generated image (auto-generated from prompt if omitted)")
|
|
244
250
|
.option("--type <type>", "Generation type: image (default), thumbnail (YouTube-optimized), asset (logo/product)")
|
|
245
251
|
.option("--aspect-ratio <aspectRatio>", "Aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4)")
|
|
246
252
|
.option("--negative-prompt <negativePrompt>", "Negative prompt")
|
|
@@ -248,9 +254,10 @@ export function registerMediaCommand(program) {
|
|
|
248
254
|
.option("--reference-media-id <referenceMediaId>", "Reference image media ID")
|
|
249
255
|
.option("--wait", "Poll until generation completes")
|
|
250
256
|
.action(async (opts) => {
|
|
257
|
+
const name = opts.name || opts.prompt.slice(0, 50).replace(/[^a-zA-Z0-9-_ ]/g, "").trim().replace(/\s+/g, "-");
|
|
251
258
|
const body = {
|
|
252
259
|
prompt: opts.prompt,
|
|
253
|
-
name
|
|
260
|
+
name,
|
|
254
261
|
};
|
|
255
262
|
if (opts.type)
|
|
256
263
|
body.type = opts.type;
|
|
@@ -278,10 +285,17 @@ export function registerMediaCommand(program) {
|
|
|
278
285
|
jsonOut(data);
|
|
279
286
|
return;
|
|
280
287
|
}
|
|
288
|
+
const imgUrl = extractImageUrl(data);
|
|
289
|
+
const status = data.status || "queued";
|
|
281
290
|
console.log(`Image generation started!\n` +
|
|
282
291
|
` Job ID: ${jobId}\n` +
|
|
283
|
-
` Status: ${
|
|
284
|
-
|
|
292
|
+
` Status: ${status}`);
|
|
293
|
+
if (imgUrl) {
|
|
294
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
295
|
+
}
|
|
296
|
+
else if (status === "queued" || status === "inference_started" || status === "inference_working") {
|
|
297
|
+
console.log(` Check: gobi media image-status ${jobId}`);
|
|
298
|
+
}
|
|
285
299
|
});
|
|
286
300
|
media
|
|
287
301
|
.command("image-edit")
|
|
@@ -311,9 +325,13 @@ export function registerMediaCommand(program) {
|
|
|
311
325
|
jsonOut(data);
|
|
312
326
|
return;
|
|
313
327
|
}
|
|
328
|
+
const imgUrl = extractImageUrl(data);
|
|
314
329
|
console.log(`Image edit started!\n` +
|
|
315
330
|
` Job ID: ${jobId}\n` +
|
|
316
331
|
` Status: ${data.status || "queued"}`);
|
|
332
|
+
if (imgUrl) {
|
|
333
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
334
|
+
}
|
|
317
335
|
});
|
|
318
336
|
media
|
|
319
337
|
.command("image-inpaint")
|
|
@@ -345,35 +363,82 @@ export function registerMediaCommand(program) {
|
|
|
345
363
|
jsonOut(data);
|
|
346
364
|
return;
|
|
347
365
|
}
|
|
366
|
+
const imgUrl = extractImageUrl(data);
|
|
348
367
|
console.log(`Inpainting started!\n` +
|
|
349
368
|
` Job ID: ${jobId}\n` +
|
|
350
369
|
` Status: ${data.status || "queued"}`);
|
|
370
|
+
if (imgUrl) {
|
|
371
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
372
|
+
}
|
|
351
373
|
});
|
|
352
374
|
media
|
|
353
375
|
.command("image-status <jobId>")
|
|
354
376
|
.description("Check image generation job status.")
|
|
355
377
|
.option("--wait", "Poll until a terminal state is reached")
|
|
356
378
|
.action(async (jobId, opts) => {
|
|
379
|
+
let data;
|
|
357
380
|
if (opts.wait) {
|
|
358
|
-
|
|
381
|
+
data = await pollStatus(`/media-gen/images/${jobId}`, [
|
|
359
382
|
"completed",
|
|
360
383
|
"failed",
|
|
361
384
|
"inference_complete",
|
|
362
385
|
"inference_failed",
|
|
363
386
|
]);
|
|
364
|
-
if (isJsonMode(media)) {
|
|
365
|
-
jsonOut(data);
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
console.log(`Image job ${jobId} — status: ${data.status}`);
|
|
369
|
-
return;
|
|
370
387
|
}
|
|
371
|
-
|
|
372
|
-
|
|
388
|
+
else {
|
|
389
|
+
const resp = (await apiGet(`/media-gen/images/${jobId}`));
|
|
390
|
+
data = unwrapResp(resp);
|
|
391
|
+
}
|
|
373
392
|
if (isJsonMode(media)) {
|
|
374
393
|
jsonOut(data);
|
|
375
394
|
return;
|
|
376
395
|
}
|
|
396
|
+
const imgUrl = extractImageUrl(data);
|
|
377
397
|
console.log(`Image job ${jobId} — status: ${data.status || "unknown"}`);
|
|
398
|
+
if (imgUrl) {
|
|
399
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
media
|
|
403
|
+
.command("image-download <jobId>")
|
|
404
|
+
.description("Download a generated image.")
|
|
405
|
+
.option("--wait", "Poll until generation completes before downloading")
|
|
406
|
+
.option("--type <type>", "Image type (image, thumbnail, asset)")
|
|
407
|
+
.action(async (jobId, opts) => {
|
|
408
|
+
if (opts.wait) {
|
|
409
|
+
console.log(`Waiting for image job ${jobId} to complete…`);
|
|
410
|
+
await pollStatus(`/media-gen/images/${jobId}`, [
|
|
411
|
+
"completed",
|
|
412
|
+
"failed",
|
|
413
|
+
"inference_complete",
|
|
414
|
+
"inference_failed",
|
|
415
|
+
]);
|
|
416
|
+
}
|
|
417
|
+
const token = await getValidToken();
|
|
418
|
+
const query = opts.type ? `?type=${opts.type}` : "";
|
|
419
|
+
const url = `${BASE_URL}/media-gen/images/${jobId}/download${query}`;
|
|
420
|
+
const res = await fetch(url, {
|
|
421
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
422
|
+
});
|
|
423
|
+
if (!res.ok) {
|
|
424
|
+
const text = (await res.text()) || "(no body)";
|
|
425
|
+
throw new ApiError(res.status, `/media-gen/images/${jobId}/download`, text);
|
|
426
|
+
}
|
|
427
|
+
const contentType = res.headers.get("content-type") || "image/png";
|
|
428
|
+
const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg"
|
|
429
|
+
: contentType.includes("webp") ? "webp"
|
|
430
|
+
: "png";
|
|
431
|
+
const filename = `${jobId}.${ext}`;
|
|
432
|
+
if (isJsonMode(media)) {
|
|
433
|
+
// In JSON mode, return base64-encoded image
|
|
434
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
435
|
+
jsonOut({ filename, contentType, size: buffer.length, base64: buffer.toString("base64") });
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// Write to file
|
|
439
|
+
const { writeFile } = await import("fs/promises");
|
|
440
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
441
|
+
await writeFile(filename, buffer);
|
|
442
|
+
console.log(`Image saved to ${filename} (${buffer.length} bytes)`);
|
|
378
443
|
});
|
|
379
444
|
}
|
package/package.json
CHANGED
|
@@ -25,6 +25,18 @@ For programmatic/agent usage, always pass `--json` as a **global** option (befor
|
|
|
25
25
|
gobi --json media image-generate --prompt "a sunset over mountains"
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## Typical Workflow (Image Generation)
|
|
29
|
+
|
|
30
|
+
Always use `--wait` to poll until completion in a single command:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gobi --json media image-generate --prompt "a sunset over mountains" --wait
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- `--name` is **optional** — auto-derived from prompt if omitted.
|
|
37
|
+
- `--wait` avoids needing a separate `image-status` call.
|
|
38
|
+
- `image-status` takes a **positional** jobId (NOT `--job-id`): `gobi media image-status <jobId>`
|
|
39
|
+
|
|
28
40
|
## Available Commands
|
|
29
41
|
|
|
30
42
|
### Upload
|
|
@@ -6,23 +6,24 @@ Usage: gobi media [options] [command]
|
|
|
6
6
|
Media generation commands (videos, images).
|
|
7
7
|
|
|
8
8
|
Options:
|
|
9
|
-
-h, --help
|
|
9
|
+
-h, --help display help for command
|
|
10
10
|
|
|
11
11
|
Commands:
|
|
12
|
-
upload-init [options]
|
|
13
|
-
upload-finalize [options]
|
|
14
|
-
avatars
|
|
15
|
-
voices
|
|
16
|
-
video-create [options]
|
|
17
|
-
video-list
|
|
18
|
-
video-get <id>
|
|
19
|
-
video-status [options] <id>
|
|
20
|
-
video-download <id>
|
|
21
|
-
image-generate [options]
|
|
22
|
-
image-edit [options]
|
|
23
|
-
image-inpaint [options]
|
|
24
|
-
image-status [options] <jobId>
|
|
25
|
-
|
|
12
|
+
upload-init [options] Get a presigned upload URL for a media file.
|
|
13
|
+
upload-finalize [options] Confirm that a media upload is complete.
|
|
14
|
+
avatars List available avatars.
|
|
15
|
+
voices List available voices.
|
|
16
|
+
video-create [options] Create an avatar video generation job.
|
|
17
|
+
video-list List all videos.
|
|
18
|
+
video-get <id> Get video metadata.
|
|
19
|
+
video-status [options] <id> Poll video generation status.
|
|
20
|
+
video-download <id> Get the download URL for a completed video.
|
|
21
|
+
image-generate [options] Generate an image from a text prompt. Types: image (default), thumbnail (YouTube-optimized), asset (logo/product). Aspect ratios: 1:1, 16:9, 9:16, 4:3, 3:4
|
|
22
|
+
image-edit [options] Edit an existing image with a prompt (image-to-image).
|
|
23
|
+
image-inpaint [options] Inpaint an image region using a mask.
|
|
24
|
+
image-status [options] <jobId> Check image generation job status.
|
|
25
|
+
image-download [options] <jobId> Download a generated image.
|
|
26
|
+
help [command] display help for command
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
## upload-init
|
|
@@ -144,7 +145,7 @@ Generate an image from a text prompt. Types: image (default), thumbnail (YouTube
|
|
|
144
145
|
|
|
145
146
|
Options:
|
|
146
147
|
--prompt <prompt> Text prompt for image generation
|
|
147
|
-
--name <name> Name for the generated image
|
|
148
|
+
--name <name> Name for the generated image (auto-generated from prompt if omitted)
|
|
148
149
|
--type <type> Generation type: image (default), thumbnail (YouTube-optimized), asset (logo/product)
|
|
149
150
|
--aspect-ratio <aspectRatio> Aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4)
|
|
150
151
|
--negative-prompt <negativePrompt> Negative prompt
|
|
@@ -196,3 +197,16 @@ Options:
|
|
|
196
197
|
--wait Poll until a terminal state is reached
|
|
197
198
|
-h, --help display help for command
|
|
198
199
|
```
|
|
200
|
+
|
|
201
|
+
## image-download
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Usage: gobi media image-download [options] <jobId>
|
|
205
|
+
|
|
206
|
+
Download a generated image.
|
|
207
|
+
|
|
208
|
+
Options:
|
|
209
|
+
--wait Poll until generation completes before downloading
|
|
210
|
+
--type <type> Image type (image, thumbnail, asset)
|
|
211
|
+
-h, --help display help for command
|
|
212
|
+
```
|