@gobi-ai/cli 0.7.3 → 0.9.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.
- package/.claude-plugin/marketplace.json +9 -5
- package/.claude-plugin/plugin.json +7 -3
- package/dist/commands/media.js +379 -0
- package/dist/main.js +2 -0
- package/package.json +2 -2
- package/skills/gobi-brain/SKILL.md +56 -0
- package/skills/{gobi/SKILL.template.md → gobi-core/SKILL.md} +27 -63
- package/skills/gobi-core/references/space.md +48 -0
- package/skills/{gobi-dev-homepage → gobi-homepage}/SKILL.md +8 -8
- package/skills/gobi-media/SKILL.md +57 -0
- package/skills/gobi-media/references/media.md +198 -0
- package/skills/gobi-sense/SKILL.md +34 -0
- package/skills/gobi-space/SKILL.md +58 -0
- package/skills/{gobi → gobi-space}/references/space.md +0 -22
- package/skills/gobi/SKILL.md +0 -216
- package/skills/gobi/scripts/generate-docs.ts +0 -199
- /package/skills/{gobi → gobi-brain}/references/brain.md +0 -0
- /package/skills/{gobi → gobi-core}/references/auth.md +0 -0
- /package/skills/{gobi → gobi-core}/references/init.md +0 -0
- /package/skills/{gobi → gobi-core}/references/session.md +0 -0
- /package/skills/{gobi → gobi-core}/references/sync.md +0 -0
- /package/skills/{gobi → gobi-core}/references/update.md +0 -0
- /package/skills/{gobi → gobi-sense}/references/sense.md +0 -0
|
@@ -4,20 +4,24 @@
|
|
|
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.8.0",
|
|
8
8
|
"plugins": [
|
|
9
9
|
{
|
|
10
10
|
"name": "gobi",
|
|
11
|
-
"description": "Manage the Gobi collaborative knowledge platform from the command line. Search and ask brains, publish brain documents, create threads, manage sessions.",
|
|
12
|
-
"version": "0.
|
|
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",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "gobi-ai"
|
|
15
15
|
},
|
|
16
16
|
"homepage": "https://github.com/gobi-ai/gobi-cli",
|
|
17
17
|
"source": "./",
|
|
18
18
|
"skills": [
|
|
19
|
-
"./skills/gobi",
|
|
20
|
-
"./skills/gobi-
|
|
19
|
+
"./skills/gobi-core",
|
|
20
|
+
"./skills/gobi-space",
|
|
21
|
+
"./skills/gobi-brain",
|
|
22
|
+
"./skills/gobi-media",
|
|
23
|
+
"./skills/gobi-sense",
|
|
24
|
+
"./skills/gobi-homepage"
|
|
21
25
|
],
|
|
22
26
|
"commands": "./commands"
|
|
23
27
|
}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gobi",
|
|
3
3
|
"description": "Manage the Gobi collaborative knowledge platform from the command line",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "gobi-ai"
|
|
7
7
|
},
|
|
8
8
|
"homepage": "https://github.com/gobi-ai/gobi-cli",
|
|
9
9
|
"skills": [
|
|
10
|
-
"./skills/gobi",
|
|
11
|
-
"./skills/gobi-
|
|
10
|
+
"./skills/gobi-core",
|
|
11
|
+
"./skills/gobi-space",
|
|
12
|
+
"./skills/gobi-brain",
|
|
13
|
+
"./skills/gobi-media",
|
|
14
|
+
"./skills/gobi-sense",
|
|
15
|
+
"./skills/gobi-homepage"
|
|
12
16
|
],
|
|
13
17
|
"commands": "./commands"
|
|
14
18
|
}
|
|
@@ -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.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "CLI client for the Gobi collaborative knowledge platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"dev": "tsx src/index.ts",
|
|
41
41
|
"start": "node dist/index.js",
|
|
42
42
|
"test": "node --test dist/*.test.js dist/**/*.test.js",
|
|
43
|
-
"generate-skill-docs": "npm run build && npx tsx
|
|
43
|
+
"generate-skill-docs": "npm run build && npx tsx scripts/generate-skill-docs.ts",
|
|
44
44
|
"prepare": "npm run build",
|
|
45
45
|
"prepublishOnly": "npm run build"
|
|
46
46
|
},
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gobi-brain
|
|
3
|
+
description: >-
|
|
4
|
+
Gobi brain commands for knowledge management: search public brains by text
|
|
5
|
+
and semantic similarity, ask brains questions, publish/unpublish BRAIN.md,
|
|
6
|
+
and manage brain updates (list/post/edit/delete). Use when the user wants
|
|
7
|
+
to search knowledge, ask a brain, publish their brain document, or manage
|
|
8
|
+
brain updates.
|
|
9
|
+
allowed-tools: Bash(gobi:*)
|
|
10
|
+
metadata:
|
|
11
|
+
author: gobi-ai
|
|
12
|
+
version: "0.8.0"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# gobi-brain
|
|
16
|
+
|
|
17
|
+
Gobi brain commands for knowledge management (v0.8.0).
|
|
18
|
+
|
|
19
|
+
Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
|
|
20
|
+
|
|
21
|
+
## Gobi Brain — Knowledge Management
|
|
22
|
+
|
|
23
|
+
`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}`.
|
|
24
|
+
|
|
25
|
+
## Space Slug Override
|
|
26
|
+
|
|
27
|
+
For `gobi brain list-updates`, you can filter by space with a subcommand option:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
gobi brain list-updates --space-slug <slug>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Note: `--space-slug` is not available on other `brain` subcommands.
|
|
34
|
+
|
|
35
|
+
## Important: JSON Mode
|
|
36
|
+
|
|
37
|
+
For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
gobi --json brain search --query "machine learning"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Available Commands
|
|
44
|
+
|
|
45
|
+
- `gobi brain search` — Search public brains by text and semantic similarity.
|
|
46
|
+
- `gobi brain ask` — Ask a brain a question. Creates a targeted session (1:1 conversation).
|
|
47
|
+
- `gobi brain publish` — Upload BRAIN.md to the vault root on webdrive. Triggers post-processing (brain sync, metadata update, Discord notification).
|
|
48
|
+
- `gobi brain unpublish` — Delete BRAIN.md from the vault on webdrive.
|
|
49
|
+
- `gobi brain list-updates` — List recent brain updates. Without --space-slug, lists all updates for you. With --space-slug, lists updates for that space. Use --mine to show only updates by you.
|
|
50
|
+
- `gobi brain post-update` — Post a brain update for a vault.
|
|
51
|
+
- `gobi brain edit-update` — Edit a published brain update. You must be the author.
|
|
52
|
+
- `gobi brain delete-update` — Delete a published brain update. You must be the author.
|
|
53
|
+
|
|
54
|
+
## Reference Documentation
|
|
55
|
+
|
|
56
|
+
- [gobi brain](references/brain.md)
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: gobi-
|
|
2
|
+
name: gobi-core
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
sessions, or brain updates.
|
|
4
|
+
Core Gobi CLI: authentication (login/logout/status), vault initialization
|
|
5
|
+
(gobi init), space selection (gobi space warp/list), file sync (gobi sync),
|
|
6
|
+
CLI updates (gobi update), and session management (list/get/reply to
|
|
7
|
+
conversations). Use when the user needs to set up Gobi, authenticate,
|
|
8
|
+
sync files, manage sessions, or update the CLI.
|
|
10
9
|
allowed-tools: Bash(gobi:*)
|
|
11
10
|
metadata:
|
|
12
11
|
author: gobi-ai
|
|
13
|
-
version: "
|
|
12
|
+
version: "0.8.0"
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
# gobi-
|
|
15
|
+
# gobi-core
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
Core CLI commands for the Gobi collaborative knowledge platform (v0.8.0).
|
|
19
18
|
|
|
20
19
|
## Prerequisites
|
|
21
20
|
|
|
@@ -89,74 +88,39 @@ gobi auth status
|
|
|
89
88
|
|
|
90
89
|
**Important for agents**: Before running any `space` command, check if `.gobi/settings.yaml` exists in the current directory with both `vaultSlug` and `selectedSpaceSlug`. If the vault is missing, guide the user through `gobi init`. If only the space is missing, guide the user through `gobi space warp`. These commands require user input (interactive prompts), so the agent cannot run them silently.
|
|
91
90
|
|
|
92
|
-
## Gobi Space — Community Channel
|
|
93
|
-
|
|
94
|
-
`gobi space` is the main interface for interacting with the user's Gobi community. When the user asks about what's happening, what others are discussing, or wants to engage with their community — use `gobi space` commands. Think of it as the user's community feed and communication hub.
|
|
95
|
-
|
|
96
|
-
- When the user wants to explore or catch up on what's happening in their space, invoke `/gobi:space-explore`.
|
|
97
|
-
- When the user wants to share or post learnings from the current session, invoke `/gobi:space-share`.
|
|
98
|
-
|
|
99
|
-
## Gobi Brain — Knowledge Management
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
## Gobi Session — Conversations
|
|
104
|
-
|
|
105
|
-
`gobi session` commands manage your conversations: list, read, and reply to sessions.
|
|
106
|
-
|
|
107
91
|
## Important: JSON Mode
|
|
108
92
|
|
|
109
93
|
For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand) to get structured JSON output:
|
|
110
94
|
|
|
111
|
-
```bash
|
|
112
|
-
gobi --json space list-threads
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
or
|
|
116
|
-
|
|
117
95
|
```bash
|
|
118
96
|
gobi --json session list
|
|
119
97
|
```
|
|
120
98
|
|
|
121
99
|
JSON responses have the shape `{ "success": true, "data": ... }` on success or `{ "success": false, "error": "..." }` on failure.
|
|
122
100
|
|
|
123
|
-
## Space Slug Override
|
|
124
|
-
|
|
125
|
-
`gobi space` commands use the space from `.gobi/settings.yaml`. Override it with a parent-level flag:
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
gobi space --space-slug <slug> list-threads
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
For `gobi brain list-updates`, you can filter by space with a subcommand option:
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
gobi brain list-updates --space-slug <slug>
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Note: `--space-slug` is not available on other `brain` subcommands or on `session` commands.
|
|
138
|
-
|
|
139
101
|
## Available Commands
|
|
140
102
|
|
|
141
|
-
|
|
103
|
+
- `gobi auth` — Authentication commands.
|
|
104
|
+
- `gobi auth login` — Log in to Gobi. Opens a browser URL for Google OAuth, then polls until authentication is complete.
|
|
105
|
+
- `gobi auth status` — Check whether you are currently authenticated with Gobi.
|
|
106
|
+
- `gobi auth logout` — Log out of Gobi and remove stored credentials.
|
|
107
|
+
- `gobi init` — Log in (if needed) and select or create the vault for the current directory.
|
|
108
|
+
- `gobi space list` — List spaces you are a member of.
|
|
109
|
+
- `gobi space warp` — Select the active space. Pass a slug to warp directly, or omit for interactive selection.
|
|
110
|
+
- `gobi session` — Session commands (get, list, reply).
|
|
111
|
+
- `gobi session get` — Get a session and its messages (paginated).
|
|
112
|
+
- `gobi session list` — List all sessions you are part of, sorted by most recent activity.
|
|
113
|
+
- `gobi session reply` — Send a human reply to a session you are a member of.
|
|
114
|
+
- `gobi sync` — Sync local vault files with Gobi Webdrive.
|
|
115
|
+
- `gobi update` — Update gobi-cli to the latest version.
|
|
142
116
|
|
|
143
117
|
## Reference Documentation
|
|
144
118
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
```bash
|
|
152
|
-
gobi --help
|
|
153
|
-
gobi auth --help
|
|
154
|
-
gobi space --help
|
|
155
|
-
gobi brain --help
|
|
156
|
-
gobi session --help
|
|
157
|
-
gobi sense --help
|
|
158
|
-
gobi sync --help
|
|
159
|
-
```
|
|
119
|
+
- [gobi auth](references/auth.md)
|
|
120
|
+
- [gobi init](references/init.md)
|
|
121
|
+
- [gobi session](references/session.md)
|
|
122
|
+
- [gobi sync](references/sync.md)
|
|
123
|
+
- [gobi update](references/update.md)
|
|
160
124
|
|
|
161
125
|
## Configuration Files
|
|
162
126
|
|