@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.8.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.8.0",
12
+ "version": "0.9.0",
13
13
  "author": {
14
14
  "name": "gobi-ai"
15
15
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gobi",
3
3
  "description": "Manage the Gobi collaborative knowledge platform from the command line",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "author": {
6
6
  "name": "gobi-ai"
7
7
  },
@@ -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
- // ── Polling helper ──
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: ${data.status || "queued"}\n` +
284
- ` Check: gobi media image-status ${jobId}`);
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
- const data = await pollStatus(`/media-gen/images/${jobId}`, [
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
- const resp = (await apiGet(`/media-gen/images/${jobId}`));
372
- const data = unwrapResp(resp);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",