@gobi-ai/cli 0.9.0 → 0.9.1
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.0",
|
|
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.0",
|
|
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")
|
|
@@ -278,10 +284,17 @@ export function registerMediaCommand(program) {
|
|
|
278
284
|
jsonOut(data);
|
|
279
285
|
return;
|
|
280
286
|
}
|
|
287
|
+
const imgUrl = extractImageUrl(data);
|
|
288
|
+
const status = data.status || "queued";
|
|
281
289
|
console.log(`Image generation started!\n` +
|
|
282
290
|
` Job ID: ${jobId}\n` +
|
|
283
|
-
` Status: ${
|
|
284
|
-
|
|
291
|
+
` Status: ${status}`);
|
|
292
|
+
if (imgUrl) {
|
|
293
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
294
|
+
}
|
|
295
|
+
else if (status === "queued" || status === "inference_started" || status === "inference_working") {
|
|
296
|
+
console.log(` Check: gobi media image-status ${jobId}`);
|
|
297
|
+
}
|
|
285
298
|
});
|
|
286
299
|
media
|
|
287
300
|
.command("image-edit")
|
|
@@ -311,9 +324,13 @@ export function registerMediaCommand(program) {
|
|
|
311
324
|
jsonOut(data);
|
|
312
325
|
return;
|
|
313
326
|
}
|
|
327
|
+
const imgUrl = extractImageUrl(data);
|
|
314
328
|
console.log(`Image edit started!\n` +
|
|
315
329
|
` Job ID: ${jobId}\n` +
|
|
316
330
|
` Status: ${data.status || "queued"}`);
|
|
331
|
+
if (imgUrl) {
|
|
332
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
333
|
+
}
|
|
317
334
|
});
|
|
318
335
|
media
|
|
319
336
|
.command("image-inpaint")
|
|
@@ -345,35 +362,82 @@ export function registerMediaCommand(program) {
|
|
|
345
362
|
jsonOut(data);
|
|
346
363
|
return;
|
|
347
364
|
}
|
|
365
|
+
const imgUrl = extractImageUrl(data);
|
|
348
366
|
console.log(`Inpainting started!\n` +
|
|
349
367
|
` Job ID: ${jobId}\n` +
|
|
350
368
|
` Status: ${data.status || "queued"}`);
|
|
369
|
+
if (imgUrl) {
|
|
370
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
371
|
+
}
|
|
351
372
|
});
|
|
352
373
|
media
|
|
353
374
|
.command("image-status <jobId>")
|
|
354
375
|
.description("Check image generation job status.")
|
|
355
376
|
.option("--wait", "Poll until a terminal state is reached")
|
|
356
377
|
.action(async (jobId, opts) => {
|
|
378
|
+
let data;
|
|
357
379
|
if (opts.wait) {
|
|
358
|
-
|
|
380
|
+
data = await pollStatus(`/media-gen/images/${jobId}`, [
|
|
359
381
|
"completed",
|
|
360
382
|
"failed",
|
|
361
383
|
"inference_complete",
|
|
362
384
|
"inference_failed",
|
|
363
385
|
]);
|
|
364
|
-
if (isJsonMode(media)) {
|
|
365
|
-
jsonOut(data);
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
console.log(`Image job ${jobId} — status: ${data.status}`);
|
|
369
|
-
return;
|
|
370
386
|
}
|
|
371
|
-
|
|
372
|
-
|
|
387
|
+
else {
|
|
388
|
+
const resp = (await apiGet(`/media-gen/images/${jobId}`));
|
|
389
|
+
data = unwrapResp(resp);
|
|
390
|
+
}
|
|
373
391
|
if (isJsonMode(media)) {
|
|
374
392
|
jsonOut(data);
|
|
375
393
|
return;
|
|
376
394
|
}
|
|
395
|
+
const imgUrl = extractImageUrl(data);
|
|
377
396
|
console.log(`Image job ${jobId} — status: ${data.status || "unknown"}`);
|
|
397
|
+
if (imgUrl) {
|
|
398
|
+
console.log(` Download URL: ${imgUrl}`);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
media
|
|
402
|
+
.command("image-download <jobId>")
|
|
403
|
+
.description("Download a generated image.")
|
|
404
|
+
.option("--wait", "Poll until generation completes before downloading")
|
|
405
|
+
.option("--type <type>", "Image type (image, thumbnail, asset)")
|
|
406
|
+
.action(async (jobId, opts) => {
|
|
407
|
+
if (opts.wait) {
|
|
408
|
+
console.log(`Waiting for image job ${jobId} to complete…`);
|
|
409
|
+
await pollStatus(`/media-gen/images/${jobId}`, [
|
|
410
|
+
"completed",
|
|
411
|
+
"failed",
|
|
412
|
+
"inference_complete",
|
|
413
|
+
"inference_failed",
|
|
414
|
+
]);
|
|
415
|
+
}
|
|
416
|
+
const token = await getValidToken();
|
|
417
|
+
const query = opts.type ? `?type=${opts.type}` : "";
|
|
418
|
+
const url = `${BASE_URL}/media-gen/images/${jobId}/download${query}`;
|
|
419
|
+
const res = await fetch(url, {
|
|
420
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
421
|
+
});
|
|
422
|
+
if (!res.ok) {
|
|
423
|
+
const text = (await res.text()) || "(no body)";
|
|
424
|
+
throw new ApiError(res.status, `/media-gen/images/${jobId}/download`, text);
|
|
425
|
+
}
|
|
426
|
+
const contentType = res.headers.get("content-type") || "image/png";
|
|
427
|
+
const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg"
|
|
428
|
+
: contentType.includes("webp") ? "webp"
|
|
429
|
+
: "png";
|
|
430
|
+
const filename = `${jobId}.${ext}`;
|
|
431
|
+
if (isJsonMode(media)) {
|
|
432
|
+
// In JSON mode, return base64-encoded image
|
|
433
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
434
|
+
jsonOut({ filename, contentType, size: buffer.length, base64: buffer.toString("base64") });
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
// Write to file
|
|
438
|
+
const { writeFile } = await import("fs/promises");
|
|
439
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
440
|
+
await writeFile(filename, buffer);
|
|
441
|
+
console.log(`Image saved to ${filename} (${buffer.length} bytes)`);
|
|
378
442
|
});
|
|
379
443
|
}
|