@gobi-ai/cli 0.7.3 → 0.8.0

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.
@@ -16,8 +16,8 @@
16
16
  "homepage": "https://github.com/gobi-ai/gobi-cli",
17
17
  "source": "./",
18
18
  "skills": [
19
- "./skills/gobi",
20
- "./skills/gobi-dev-homepage"
19
+ "./skills/gobi-cli",
20
+ "./skills/gobi-homepage"
21
21
  ],
22
22
  "commands": "./commands"
23
23
  }
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "homepage": "https://github.com/gobi-ai/gobi-cli",
9
9
  "skills": [
10
- "./skills/gobi",
11
- "./skills/gobi-dev-homepage"
10
+ "./skills/gobi-cli",
11
+ "./skills/gobi-homepage"
12
12
  ],
13
13
  "commands": "./commands"
14
14
  }
@@ -0,0 +1,379 @@
1
+ import { apiGet, apiPost } from "../client.js";
2
+ import { BASE_URL, POLL_MAX_DURATION_MS } from "../constants.js";
3
+ import { getValidToken } from "../auth/manager.js";
4
+ import { ApiError } from "../errors.js";
5
+ import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
6
+ // ── Polling helper ──
7
+ async function pollStatus(path, terminalStates, intervalMs = 3000) {
8
+ const start = Date.now();
9
+ while (Date.now() - start < POLL_MAX_DURATION_MS) {
10
+ const resp = (await apiGet(path));
11
+ const data = unwrapResp(resp);
12
+ const status = data.status || "";
13
+ if (terminalStates.includes(status))
14
+ return data;
15
+ await new Promise((r) => setTimeout(r, intervalMs));
16
+ }
17
+ throw new Error(`Polling timed out after ${POLL_MAX_DURATION_MS / 1000}s`);
18
+ }
19
+ export function registerMediaCommand(program) {
20
+ const media = program
21
+ .command("media")
22
+ .description("Media generation commands (videos, images).");
23
+ // ════════════════════════════════════════════════════════════════════
24
+ // Upload
25
+ // ════════════════════════════════════════════════════════════════════
26
+ media
27
+ .command("upload-init")
28
+ .description("Get a presigned upload URL for a media file.")
29
+ .requiredOption("--file-name <fileName>", "Name of the file to upload")
30
+ .requiredOption("--content-type <contentType>", "MIME type (e.g. image/png, video/mp4)")
31
+ .option("--file-size <fileSize>", "File size in bytes")
32
+ .action(async (opts) => {
33
+ const body = {
34
+ fileName: opts.fileName,
35
+ contentType: opts.contentType,
36
+ };
37
+ if (opts.fileSize)
38
+ body.fileSize = parseInt(opts.fileSize, 10);
39
+ const resp = (await apiPost("/media-gen/media/initialize", body));
40
+ const data = unwrapResp(resp);
41
+ if (isJsonMode(media)) {
42
+ jsonOut(data);
43
+ return;
44
+ }
45
+ console.log(`Upload initialized!\n` +
46
+ ` Media ID: ${data.mediaId}\n` +
47
+ ` Upload URL: ${data.uploadUrl}\n\n` +
48
+ `PUT your file to the upload URL, then run:\n` +
49
+ ` gobi media upload-finalize --media-id ${data.mediaId}`);
50
+ });
51
+ media
52
+ .command("upload-finalize")
53
+ .description("Confirm that a media upload is complete.")
54
+ .requiredOption("--media-id <mediaId>", "Media ID from upload-init")
55
+ .action(async (opts) => {
56
+ const resp = (await apiPost("/media-gen/media/finalize", {
57
+ mediaId: opts.mediaId,
58
+ }));
59
+ const data = unwrapResp(resp);
60
+ if (isJsonMode(media)) {
61
+ jsonOut(data);
62
+ return;
63
+ }
64
+ console.log(`Upload finalized for media ${opts.mediaId}.`);
65
+ });
66
+ // ════════════════════════════════════════════════════════════════════
67
+ // Avatars & Voices
68
+ // ════════════════════════════════════════════════════════════════════
69
+ media
70
+ .command("avatars")
71
+ .description("List available avatars.")
72
+ .action(async () => {
73
+ const resp = (await apiGet("/media-gen/avatars"));
74
+ const data = unwrapResp(resp);
75
+ if (isJsonMode(media)) {
76
+ jsonOut(data);
77
+ return;
78
+ }
79
+ if (!Array.isArray(data) || data.length === 0) {
80
+ console.log("No avatars available.");
81
+ return;
82
+ }
83
+ console.log("Available avatars:");
84
+ for (const a of data) {
85
+ console.log(` - ${a.id || a.avatarId}: ${a.name || "(unnamed)"}`);
86
+ }
87
+ });
88
+ media
89
+ .command("voices")
90
+ .description("List available voices.")
91
+ .action(async () => {
92
+ const resp = (await apiGet("/media-gen/voices"));
93
+ const data = unwrapResp(resp);
94
+ if (isJsonMode(media)) {
95
+ jsonOut(data);
96
+ return;
97
+ }
98
+ if (!Array.isArray(data) || data.length === 0) {
99
+ console.log("No voices available.");
100
+ return;
101
+ }
102
+ console.log("Available voices:");
103
+ for (const v of data) {
104
+ console.log(` - ${v.id || v.voiceId}: ${v.name || "(unnamed)"}`);
105
+ }
106
+ });
107
+ // ════════════════════════════════════════════════════════════════════
108
+ // Videos
109
+ // ════════════════════════════════════════════════════════════════════
110
+ media
111
+ .command("video-create")
112
+ .description("Create an avatar video generation job.")
113
+ .requiredOption("--name <name>", "Name for the video")
114
+ .requiredOption("--avatar-id <avatarId>", "Avatar to use")
115
+ .requiredOption("--voice-id <voiceId>", "Voice to use")
116
+ .requiredOption("--script <script>", "Script for the avatar to read")
117
+ .option("--background-media-id <backgroundMediaId>", "Background media ID (from upload)")
118
+ .option("--wait", "Poll until generation completes")
119
+ .action(async (opts) => {
120
+ const body = {
121
+ name: opts.name,
122
+ avatarId: opts.avatarId,
123
+ voiceId: opts.voiceId,
124
+ script: opts.script,
125
+ };
126
+ if (opts.backgroundMediaId)
127
+ body.backgroundMediaId = opts.backgroundMediaId;
128
+ const resp = (await apiPost("/media-gen/videos", body));
129
+ let data = unwrapResp(resp);
130
+ const videoId = data.id || data.videoId;
131
+ if (opts.wait && videoId) {
132
+ console.log(`Video ${videoId} queued — polling for completion…`);
133
+ data = await pollStatus(`/media-gen/videos/${videoId}/status`, ["inference_complete", "inference_failed"]);
134
+ }
135
+ if (isJsonMode(media)) {
136
+ jsonOut(data);
137
+ return;
138
+ }
139
+ const status = data.status || "queued";
140
+ console.log(`Video created!\n` +
141
+ ` ID: ${videoId}\n` +
142
+ ` Status: ${status}`);
143
+ if (status === "inference_complete") {
144
+ console.log(` Download: gobi media video-download ${videoId}`);
145
+ }
146
+ });
147
+ media
148
+ .command("video-list")
149
+ .description("List all videos.")
150
+ .action(async () => {
151
+ const resp = (await apiGet("/media-gen/videos"));
152
+ const data = unwrapResp(resp);
153
+ if (isJsonMode(media)) {
154
+ jsonOut(data);
155
+ return;
156
+ }
157
+ if (!Array.isArray(data) || data.length === 0) {
158
+ console.log("No videos found.");
159
+ return;
160
+ }
161
+ console.log("Videos:");
162
+ for (const v of data) {
163
+ console.log(` - [${v.id}] status: ${v.status || "unknown"}, created: ${v.createdAt || "?"}`);
164
+ }
165
+ });
166
+ media
167
+ .command("video-get <id>")
168
+ .description("Get video metadata.")
169
+ .action(async (id) => {
170
+ const resp = (await apiGet(`/media-gen/videos/${id}`));
171
+ const data = unwrapResp(resp);
172
+ if (isJsonMode(media)) {
173
+ jsonOut(data);
174
+ return;
175
+ }
176
+ console.log(`Video ${id}:`);
177
+ for (const [k, v] of Object.entries(data)) {
178
+ console.log(` ${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`);
179
+ }
180
+ });
181
+ media
182
+ .command("video-status <id>")
183
+ .description("Poll video generation status.")
184
+ .option("--wait", "Poll until a terminal state is reached")
185
+ .action(async (id, opts) => {
186
+ if (opts.wait) {
187
+ const data = await pollStatus(`/media-gen/videos/${id}/status`, ["inference_complete", "inference_failed"]);
188
+ if (isJsonMode(media)) {
189
+ jsonOut(data);
190
+ return;
191
+ }
192
+ console.log(`Video ${id} — status: ${data.status}`);
193
+ return;
194
+ }
195
+ const resp = (await apiGet(`/media-gen/videos/${id}/status`));
196
+ const data = unwrapResp(resp);
197
+ if (isJsonMode(media)) {
198
+ jsonOut(data);
199
+ return;
200
+ }
201
+ console.log(`Video ${id} — status: ${data.status || "unknown"}`);
202
+ });
203
+ media
204
+ .command("video-download <id>")
205
+ .description("Get the download URL for a completed video.")
206
+ .action(async (id) => {
207
+ const token = await getValidToken();
208
+ const url = `${BASE_URL}/media-gen/videos/${id}/download`;
209
+ const res = await fetch(url, {
210
+ headers: { Authorization: `Bearer ${token}` },
211
+ redirect: "manual",
212
+ });
213
+ // If the server redirects, extract the Location header
214
+ if (res.status >= 300 && res.status < 400) {
215
+ const location = res.headers.get("location") || "";
216
+ if (isJsonMode(media)) {
217
+ jsonOut({ downloadUrl: location });
218
+ return;
219
+ }
220
+ console.log(`Download URL for video ${id}:\n ${location}`);
221
+ return;
222
+ }
223
+ if (!res.ok) {
224
+ const text = (await res.text()) || "(no body)";
225
+ throw new ApiError(res.status, `/media-gen/videos/${id}/download`, text);
226
+ }
227
+ // If it returns JSON instead of a redirect
228
+ const resp = (await res.json());
229
+ const data = unwrapResp(resp);
230
+ if (isJsonMode(media)) {
231
+ jsonOut(data);
232
+ return;
233
+ }
234
+ console.log(`Download URL for video ${id}:\n ${data.url || data.downloadUrl || JSON.stringify(data)}`);
235
+ });
236
+ // ════════════════════════════════════════════════════════════════════
237
+ // Images
238
+ // ════════════════════════════════════════════════════════════════════
239
+ media
240
+ .command("image-generate")
241
+ .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
+ .requiredOption("--prompt <prompt>", "Text prompt for image generation")
243
+ .requiredOption("--name <name>", "Name for the generated image")
244
+ .option("--type <type>", "Generation type: image (default), thumbnail (YouTube-optimized), asset (logo/product)")
245
+ .option("--aspect-ratio <aspectRatio>", "Aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4)")
246
+ .option("--negative-prompt <negativePrompt>", "Negative prompt")
247
+ .option("--seed <seed>", "Random seed for reproducibility")
248
+ .option("--reference-media-id <referenceMediaId>", "Reference image media ID")
249
+ .option("--wait", "Poll until generation completes")
250
+ .action(async (opts) => {
251
+ const body = {
252
+ prompt: opts.prompt,
253
+ name: opts.name,
254
+ };
255
+ if (opts.type)
256
+ body.type = opts.type;
257
+ if (opts.aspectRatio)
258
+ body.aspectRatio = opts.aspectRatio;
259
+ if (opts.negativePrompt)
260
+ body.negativePrompt = opts.negativePrompt;
261
+ if (opts.seed)
262
+ body.seed = parseInt(opts.seed, 10);
263
+ if (opts.referenceMediaId)
264
+ body.referenceMediaId = opts.referenceMediaId;
265
+ const resp = (await apiPost("/media-gen/images/generate", body));
266
+ let data = unwrapResp(resp);
267
+ const jobId = data.jobId || data.id;
268
+ if (opts.wait && jobId) {
269
+ console.log(`Image job ${jobId} queued — polling for completion…`);
270
+ data = await pollStatus(`/media-gen/images/${jobId}`, [
271
+ "completed",
272
+ "failed",
273
+ "inference_complete",
274
+ "inference_failed",
275
+ ]);
276
+ }
277
+ if (isJsonMode(media)) {
278
+ jsonOut(data);
279
+ return;
280
+ }
281
+ console.log(`Image generation started!\n` +
282
+ ` Job ID: ${jobId}\n` +
283
+ ` Status: ${data.status || "queued"}\n` +
284
+ ` Check: gobi media image-status ${jobId}`);
285
+ });
286
+ media
287
+ .command("image-edit")
288
+ .description("Edit an existing image with a prompt (image-to-image).")
289
+ .requiredOption("--media-id <mediaId>", "Source image media ID")
290
+ .requiredOption("--prompt <prompt>", "Edit instruction")
291
+ .requiredOption("--name <name>", "Name for the edited image")
292
+ .option("--wait", "Poll until generation completes")
293
+ .action(async (opts) => {
294
+ const resp = (await apiPost("/media-gen/images/edit", {
295
+ mediaId: opts.mediaId,
296
+ prompt: opts.prompt,
297
+ name: opts.name,
298
+ }));
299
+ let data = unwrapResp(resp);
300
+ const jobId = data.jobId || data.id;
301
+ if (opts.wait && jobId) {
302
+ console.log(`Image edit job ${jobId} — polling for completion…`);
303
+ data = await pollStatus(`/media-gen/images/${jobId}`, [
304
+ "completed",
305
+ "failed",
306
+ "inference_complete",
307
+ "inference_failed",
308
+ ]);
309
+ }
310
+ if (isJsonMode(media)) {
311
+ jsonOut(data);
312
+ return;
313
+ }
314
+ console.log(`Image edit started!\n` +
315
+ ` Job ID: ${jobId}\n` +
316
+ ` Status: ${data.status || "queued"}`);
317
+ });
318
+ media
319
+ .command("image-inpaint")
320
+ .description("Inpaint an image region using a mask.")
321
+ .requiredOption("--media-id <mediaId>", "Source image media ID")
322
+ .requiredOption("--mask-media-id <maskMediaId>", "Mask image media ID")
323
+ .requiredOption("--prompt <prompt>", "Inpainting prompt")
324
+ .requiredOption("--name <name>", "Name for the inpainted image")
325
+ .option("--wait", "Poll until generation completes")
326
+ .action(async (opts) => {
327
+ const resp = (await apiPost("/media-gen/images/inpaint", {
328
+ mediaId: opts.mediaId,
329
+ maskMediaId: opts.maskMediaId,
330
+ prompt: opts.prompt,
331
+ name: opts.name,
332
+ }));
333
+ let data = unwrapResp(resp);
334
+ const jobId = data.jobId || data.id;
335
+ if (opts.wait && jobId) {
336
+ console.log(`Inpaint job ${jobId} — polling for completion…`);
337
+ data = await pollStatus(`/media-gen/images/${jobId}`, [
338
+ "completed",
339
+ "failed",
340
+ "inference_complete",
341
+ "inference_failed",
342
+ ]);
343
+ }
344
+ if (isJsonMode(media)) {
345
+ jsonOut(data);
346
+ return;
347
+ }
348
+ console.log(`Inpainting started!\n` +
349
+ ` Job ID: ${jobId}\n` +
350
+ ` Status: ${data.status || "queued"}`);
351
+ });
352
+ media
353
+ .command("image-status <jobId>")
354
+ .description("Check image generation job status.")
355
+ .option("--wait", "Poll until a terminal state is reached")
356
+ .action(async (jobId, opts) => {
357
+ if (opts.wait) {
358
+ const data = await pollStatus(`/media-gen/images/${jobId}`, [
359
+ "completed",
360
+ "failed",
361
+ "inference_complete",
362
+ "inference_failed",
363
+ ]);
364
+ if (isJsonMode(media)) {
365
+ jsonOut(data);
366
+ return;
367
+ }
368
+ console.log(`Image job ${jobId} — status: ${data.status}`);
369
+ return;
370
+ }
371
+ const resp = (await apiGet(`/media-gen/images/${jobId}`));
372
+ const data = unwrapResp(resp);
373
+ if (isJsonMode(media)) {
374
+ jsonOut(data);
375
+ return;
376
+ }
377
+ console.log(`Image job ${jobId} — status: ${data.status || "unknown"}`);
378
+ });
379
+ }
package/dist/main.js CHANGED
@@ -10,6 +10,7 @@ import { registerSessionsCommand } from "./commands/sessions.js";
10
10
  import { registerSenseCommand } from "./commands/sense.js";
11
11
  import { registerSyncCommand } from "./commands/sync.js";
12
12
  import { registerUpdateCommand } from "./commands/update.js";
13
+ import { registerMediaCommand } from "./commands/media.js";
13
14
  const require = createRequire(import.meta.url);
14
15
  const { version } = require("../package.json");
15
16
  const SKIP_BANNER_COMMANDS = new Set(["auth", "init", "update"]);
@@ -36,6 +37,7 @@ export async function cli() {
36
37
  registerSenseCommand(program);
37
38
  registerSyncCommand(program);
38
39
  registerUpdateCommand(program);
40
+ registerMediaCommand(program);
39
41
  // Propagate helpWidth to all subcommands
40
42
  const helpWidth = process.stdout.columns || 200;
41
43
  for (const cmd of program.commands) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -6,7 +6,7 @@ description: >-
6
6
  the outside world — checking what's happening, reading and writing threads,
7
7
  and collaborating with others.
8
8
  Use when the user wants to interact with Gobi spaces, vaults, brains, threads,
9
- sessions, or brain updates.
9
+ sessions, brain updates, or media generation (images, videos, thumbnails).
10
10
  allowed-tools: Bash(gobi:*)
11
11
  metadata:
12
12
  author: gobi-ai
@@ -100,6 +100,14 @@ gobi auth status
100
100
 
101
101
  `gobi brain` commands manage your vault's brain: search across all spaces, ask brains questions, and publish/unpublish your BRAIN.md. Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
102
102
 
103
+ ## Gobi Media — Image & Video Generation
104
+
105
+ `gobi media` commands generate images, thumbnails, assets, and avatar videos using AI. All generation is async: create a job, poll for status (or use `--wait`), then retrieve the result.
106
+
107
+ - **Images**: Generate from text prompts, edit existing images, or inpaint with masks. Supports types: `image` (default), `thumbnail` (YouTube-optimized), `asset` (logo/product).
108
+ - **Videos**: Create avatar videos with script narration. Choose an avatar and voice, provide a script, and generate a talking-head video.
109
+ - **Upload**: Upload custom media files (backgrounds, references, masks) via presigned S3 URLs for use in generation.
110
+
103
111
  ## Gobi Session — Conversations
104
112
 
105
113
  `gobi session` commands manage your conversations: list, read, and reply to sessions.
@@ -169,6 +177,20 @@ Note: `--space-slug` is not available on other `brain` subcommands or on `sessio
169
177
  - `gobi session get` — Get a session and its messages (paginated).
170
178
  - `gobi session list` — List all sessions you are part of, sorted by most recent activity.
171
179
  - `gobi session reply` — Send a human reply to a session you are a member of.
180
+ - `gobi media` — Media generation commands (images, videos).
181
+ - `gobi media upload-init` — Get a presigned upload URL for a media file. Requires `--file-name`, `--content-type`, `--file-size`.
182
+ - `gobi media upload-finalize` — Confirm that a media upload is complete.
183
+ - `gobi media avatars` — List available avatars.
184
+ - `gobi media voices` — List available voices.
185
+ - `gobi media video-create` — Create an avatar video generation job. Requires `--name`, `--avatar-id`, `--voice-id`, `--script`.
186
+ - `gobi media video-list` — List all videos.
187
+ - `gobi media video-get <id>` — Get video metadata.
188
+ - `gobi media video-status <id>` — Poll video generation status. Use `--wait` to block until complete.
189
+ - `gobi media video-download <id>` — Get the download URL for a completed video.
190
+ - `gobi media image-generate` — Generate an image from a text prompt. Requires `--prompt`, `--name`. Optional: `--type` (image|thumbnail|asset), `--aspect-ratio`, `--negative-prompt`, `--seed`, `--reference-media-id`. Use `--wait` to block until complete.
191
+ - `gobi media image-edit` — Edit an existing image with a prompt. Requires `--media-id`, `--prompt`, `--name`.
192
+ - `gobi media image-inpaint` — Inpaint an image region using a mask. Requires `--media-id`, `--mask-media-id`, `--prompt`, `--name`.
193
+ - `gobi media image-status <jobId>` — Check image generation job status. Use `--wait` to block until complete.
172
194
  - `gobi sense` — Sense commands (activities, transcriptions).
173
195
  - `gobi sense activities` — Fetch activity records within a time range.
174
196
  - `gobi sense transcriptions` — Fetch transcription records within a time range.
@@ -182,6 +204,7 @@ Note: `--space-slug` is not available on other `brain` subcommands or on `sessio
182
204
  - [gobi space](references/space.md)
183
205
  - [gobi brain](references/brain.md)
184
206
  - [gobi session](references/session.md)
207
+ - [gobi media](references/media.md)
185
208
  - [gobi sense](references/sense.md)
186
209
  - [gobi sync](references/sync.md)
187
210
  - [gobi update](references/update.md)
@@ -196,6 +219,7 @@ gobi auth --help
196
219
  gobi space --help
197
220
  gobi brain --help
198
221
  gobi session --help
222
+ gobi media --help
199
223
  gobi sense --help
200
224
  gobi sync --help
201
225
  ```
@@ -0,0 +1,215 @@
1
+ # gobi media
2
+
3
+ ```
4
+ Usage: gobi media [options] [command]
5
+
6
+ Media generation commands (videos, images).
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
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
+ help [command] display help for command
26
+ ```
27
+
28
+ ## Async Workflow
29
+
30
+ Video and image generation are async:
31
+ 1. Call the create/generate endpoint — receive a job ID
32
+ 2. Poll with `--wait` or manually check status until terminal state
33
+ 3. Retrieve or download the result
34
+
35
+ ## Media Upload
36
+
37
+ Upload media files (images, videos) for use as backgrounds, references, or masks:
38
+
39
+ 1. `gobi media upload-init` — get a presigned S3 upload URL
40
+ 2. PUT your file to the returned `uploadUrl`
41
+ 3. `gobi media upload-finalize` — confirm the upload
42
+
43
+ The returned `mediaId` can then be used with `--background-media-id`, `--reference-media-id`, `--media-id`, or `--mask-media-id`.
44
+
45
+ ## upload-init
46
+
47
+ ```
48
+ Usage: gobi media upload-init [options]
49
+
50
+ Get a presigned upload URL for a media file.
51
+
52
+ Options:
53
+ --file-name <fileName> Name of the file to upload
54
+ --content-type <contentType> MIME type (e.g. image/png, video/mp4)
55
+ --file-size <fileSize> File size in bytes
56
+ -h, --help display help for command
57
+ ```
58
+
59
+ ## upload-finalize
60
+
61
+ ```
62
+ Usage: gobi media upload-finalize [options]
63
+
64
+ Confirm that a media upload is complete.
65
+
66
+ Options:
67
+ --media-id <mediaId> Media ID from upload-init
68
+ -h, --help display help for command
69
+ ```
70
+
71
+ ## avatars
72
+
73
+ ```
74
+ Usage: gobi media avatars [options]
75
+
76
+ List available avatars.
77
+
78
+ Options:
79
+ -h, --help display help for command
80
+ ```
81
+
82
+ ## voices
83
+
84
+ ```
85
+ Usage: gobi media voices [options]
86
+
87
+ List available voices.
88
+
89
+ Options:
90
+ -h, --help display help for command
91
+ ```
92
+
93
+ ## video-create
94
+
95
+ ```
96
+ Usage: gobi media video-create [options]
97
+
98
+ Create an avatar video generation job.
99
+
100
+ Options:
101
+ --name <name> Name for the video
102
+ --avatar-id <avatarId> Avatar to use
103
+ --voice-id <voiceId> Voice to use
104
+ --script <script> Script for the avatar to read
105
+ --background-media-id <backgroundMediaId> Background media ID (from upload)
106
+ --wait Poll until generation completes
107
+ -h, --help display help for command
108
+ ```
109
+
110
+ ## video-list
111
+
112
+ ```
113
+ Usage: gobi media video-list [options]
114
+
115
+ List all videos.
116
+
117
+ Options:
118
+ -h, --help display help for command
119
+ ```
120
+
121
+ ## video-get
122
+
123
+ ```
124
+ Usage: gobi media video-get [options] <id>
125
+
126
+ Get video metadata.
127
+
128
+ Options:
129
+ -h, --help display help for command
130
+ ```
131
+
132
+ ## video-status
133
+
134
+ ```
135
+ Usage: gobi media video-status [options] <id>
136
+
137
+ Poll video generation status.
138
+
139
+ Options:
140
+ --wait Poll until a terminal state is reached
141
+ -h, --help display help for command
142
+ ```
143
+
144
+ ## video-download
145
+
146
+ ```
147
+ Usage: gobi media video-download [options] <id>
148
+
149
+ Get the download URL for a completed video.
150
+
151
+ Options:
152
+ -h, --help display help for command
153
+ ```
154
+
155
+ ## image-generate
156
+
157
+ ```
158
+ Usage: gobi media image-generate [options]
159
+
160
+ 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
161
+
162
+ Options:
163
+ --prompt <prompt> Text prompt for image generation
164
+ --name <name> Name for the generated image
165
+ --type <type> Generation type: image (default), thumbnail (YouTube-optimized), asset (logo/product)
166
+ --aspect-ratio <aspectRatio> Aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4)
167
+ --negative-prompt <negativePrompt> Negative prompt
168
+ --seed <seed> Random seed for reproducibility
169
+ --reference-media-id <referenceMediaId> Reference image media ID
170
+ --wait Poll until generation completes
171
+ -h, --help display help for command
172
+ ```
173
+
174
+ ## image-edit
175
+
176
+ ```
177
+ Usage: gobi media image-edit [options]
178
+
179
+ Edit an existing image with a prompt (image-to-image).
180
+
181
+ Options:
182
+ --media-id <mediaId> Source image media ID
183
+ --prompt <prompt> Edit instruction
184
+ --name <name> Name for the edited image
185
+ --wait Poll until generation completes
186
+ -h, --help display help for command
187
+ ```
188
+
189
+ ## image-inpaint
190
+
191
+ ```
192
+ Usage: gobi media image-inpaint [options]
193
+
194
+ Inpaint an image region using a mask.
195
+
196
+ Options:
197
+ --media-id <mediaId> Source image media ID
198
+ --mask-media-id <maskMediaId> Mask image media ID
199
+ --prompt <prompt> Inpainting prompt
200
+ --name <name> Name for the inpainted image
201
+ --wait Poll until generation completes
202
+ -h, --help display help for command
203
+ ```
204
+
205
+ ## image-status
206
+
207
+ ```
208
+ Usage: gobi media image-status [options] <jobId>
209
+
210
+ Check image generation job status.
211
+
212
+ Options:
213
+ --wait Poll until a terminal state is reached
214
+ -h, --help display help for command
215
+ ```
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  name: gobi-homepage
3
3
  description: >-
4
- Developer reference for building Gobi Applets — custom HTML pages hosted on
4
+ Developer reference for building Gobi Homepages — custom HTML pages hosted on
5
5
  webdrive and served as a vault's public homepage at gobispace.com/@{vaultSlug}.
6
- Use when a developer wants to build or modify a vault homepage applet.
6
+ Use when a developer wants to build or modify a vault homepage.
7
7
  ---
8
8
 
9
- # Gobi Applet Developer Guide
9
+ # Gobi Homepage Developer Guide
10
10
 
11
- A **Gobi Applet** is a custom HTML page hosted on a vault's webdrive and served as its public homepage at `https://gobispace.com/@{vaultSlug}`. Gobi injects a `window.gobi` bridge before any scripts run, giving the applet access to vault data, files, brain updates, and chat.
11
+ A **Gobi Homepage** is a custom HTML page hosted on a vault's webdrive and served as its public homepage at `https://gobispace.com/@{vaultSlug}`. Gobi injects a `window.gobi` bridge before any scripts run, giving the homepage access to vault data, files, brain updates, and chat.
12
12
 
13
- > **Sandbox:** The applet runs in a sandboxed iframe with `origin: null`. Direct `fetch()` / `XMLHttpRequest` calls are blocked by CORS. All data access must go through `window.gobi.*`.
13
+ > **Sandbox:** The homepage runs in a sandboxed iframe with `origin: null`. Direct `fetch()` / `XMLHttpRequest` calls are blocked by CORS. All data access must go through `window.gobi.*`.
14
14
 
15
15
  ---
16
16
 
@@ -20,9 +20,9 @@ A **Gobi Applet** is a custom HTML page hosted on a vault's webdrive and served
20
20
  ```bash
21
21
  gobi sync
22
22
  ```
23
- 2. Set `homepagePath` in vault settings:
24
- - `app/home.html` — Gobi sidebars visible alongside the applet
25
- - `app/home.html?nav=false` — full-screen, no Gobi chrome
23
+ 2. Set `homepage` in BRAIN.md (homepage property):
24
+ - `homepage: "[[app/home.html]]"` — Gobi sidebars visible alongside the homepage
25
+ - `homepage: "[[app/home.html?nav=false]]"` — full-screen, no Gobi chrome
26
26
 
27
27
  ---
28
28
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes