@gobi-ai/cli 2.0.24 → 2.0.26
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 +3 -3
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +30 -35
- package/dist/commands/artifact.js +472 -0
- package/dist/commands/global.js +21 -104
- package/dist/commands/personal.js +21 -102
- package/dist/commands/space.js +20 -103
- package/dist/commands/utils.js +0 -15
- package/dist/commands/vault.js +1 -1
- package/dist/main.js +2 -2
- package/package.json +1 -1
- package/skills/gobi-artifact/SKILL.md +133 -0
- package/skills/gobi-artifact/references/artifact.md +142 -0
- package/skills/gobi-core/SKILL.md +6 -7
- package/skills/gobi-homepage/SKILL.md +1 -1
- package/skills/gobi-media/SKILL.md +2 -2
- package/skills/gobi-sense/SKILL.md +2 -2
- package/skills/gobi-space/SKILL.md +12 -18
- package/skills/gobi-space/references/global.md +17 -28
- package/skills/gobi-space/references/personal.md +17 -26
- package/skills/gobi-space/references/space.md +1 -10
- package/skills/gobi-vault/SKILL.md +3 -3
- package/skills/gobi-vault/references/vault.md +2 -2
- package/dist/commands/draft.js +0 -230
- package/skills/gobi-draft/SKILL.md +0 -113
- package/skills/gobi-draft/references/draft.md +0 -114
package/dist/commands/global.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { WEB_BASE_URL } from "../constants.js";
|
|
2
2
|
import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { getValidToken } from "../auth/manager.js";
|
|
3
|
+
import { isJsonMode, jsonOut, readStdin, unwrapResp, } from "./utils.js";
|
|
4
|
+
import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
|
|
6
5
|
function readContent(value) {
|
|
7
6
|
if (value === "-")
|
|
8
7
|
return readStdin();
|
|
@@ -188,48 +187,25 @@ export function registerGlobalCommand(program) {
|
|
|
188
187
|
// ── Create post ──
|
|
189
188
|
global
|
|
190
189
|
.command("create-post")
|
|
191
|
-
.description("Create a post in the global feed.
|
|
190
|
+
.description("Create a post in the global feed.")
|
|
192
191
|
.option("--title <title>", "Title of the post")
|
|
193
192
|
.option("--content <content>", "Post content (markdown supported, use \"-\" for stdin)")
|
|
194
193
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
195
|
-
.option("--
|
|
196
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting (also sets authorVaultSlug to that vault)")
|
|
197
|
-
.option("--draft-id <draftId>", "Use this draft as the source of title and content (mutually exclusive with --title/--content/--rich-text). On success, links the post back by recording postId on draft.metadata so the client can render an 'Open post' button. The draft's vaultSlug seeds --vault-slug when not given explicitly.")
|
|
194
|
+
.option("--artifact <artifactId>", "Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
198
195
|
.option("--attach <file>", "Local media file to attach. Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video.", (value, prev = []) => [...prev, value], [])
|
|
199
196
|
.option("--repost-post-id <postId>", "Wrap an existing top-level post as the embedded card on this new post. Composes with --content / --rich-text / --attach (the wrapping author's text + media render above the embedded card). Reposts-of-reposts are collapsed to the transitive root server-side. The referenced post must exist, not be deleted, and not itself be a reply.")
|
|
200
197
|
.action(async (opts) => {
|
|
201
|
-
if (opts.
|
|
202
|
-
|
|
203
|
-
throw new Error("--draft-id sources title and content from the draft; --title, --content, and --rich-text are not allowed alongside it.");
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
if (!opts.content && !opts.richText) {
|
|
208
|
-
throw new Error("Provide either --content or --rich-text (or --draft-id).");
|
|
209
|
-
}
|
|
210
|
-
if (opts.content && opts.richText) {
|
|
211
|
-
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
212
|
-
}
|
|
198
|
+
if (!opts.content && !opts.richText) {
|
|
199
|
+
throw new Error("Provide either --content or --rich-text.");
|
|
213
200
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const effectiveContent = draft ? draft.content : opts.content;
|
|
217
|
-
const effectiveVaultSlugOpt = opts.vaultSlug ?? draft?.vaultSlug ?? undefined;
|
|
218
|
-
let authorVaultSlug;
|
|
219
|
-
if (effectiveVaultSlugOpt || opts.autoAttachments) {
|
|
220
|
-
authorVaultSlug = resolveVaultSlug({ vaultSlug: effectiveVaultSlugOpt });
|
|
201
|
+
if (opts.content && opts.richText) {
|
|
202
|
+
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
221
203
|
}
|
|
222
204
|
const body = {};
|
|
223
|
-
if (
|
|
224
|
-
body.title =
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
228
|
-
const token = await getValidToken();
|
|
229
|
-
const links = extractWikiLinks(content);
|
|
230
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
231
|
-
}
|
|
232
|
-
body.content = content;
|
|
205
|
+
if (opts.title != null)
|
|
206
|
+
body.title = opts.title;
|
|
207
|
+
if (opts.content != null) {
|
|
208
|
+
body.content = readContent(opts.content);
|
|
233
209
|
}
|
|
234
210
|
if (opts.richText != null) {
|
|
235
211
|
let parsed;
|
|
@@ -241,8 +217,8 @@ export function registerGlobalCommand(program) {
|
|
|
241
217
|
}
|
|
242
218
|
body.richText = parsed;
|
|
243
219
|
}
|
|
244
|
-
if (
|
|
245
|
-
body.
|
|
220
|
+
if (opts.artifact && opts.artifact.length > 0)
|
|
221
|
+
body.artifactIds = opts.artifact;
|
|
246
222
|
if (opts.attach && opts.attach.length > 0) {
|
|
247
223
|
assertPostAttachmentMix(opts.attach);
|
|
248
224
|
body.attachments = await uploadPostAttachments(opts.attach);
|
|
@@ -256,19 +232,6 @@ export function registerGlobalCommand(program) {
|
|
|
256
232
|
}
|
|
257
233
|
const resp = (await apiPost(`/posts`, body));
|
|
258
234
|
const post = unwrapResp(resp);
|
|
259
|
-
if (opts.draftId && post.id != null) {
|
|
260
|
-
try {
|
|
261
|
-
await apiPatch(`/app/drafts/${opts.draftId}/metadata`, {
|
|
262
|
-
postId: typeof post.id === "number" ? post.id : Number(post.id),
|
|
263
|
-
spaceSlug: null,
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
catch (e) {
|
|
267
|
-
// Don't fail the create if linking fails — the post is live; just
|
|
268
|
-
// surface a warning so the agent can mention it.
|
|
269
|
-
console.error(`Warning: failed to link post to draft ${opts.draftId}: ${e.message}`);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
235
|
const shareUrl = buildPersonalPostUrl(post);
|
|
273
236
|
if (isJsonMode(global)) {
|
|
274
237
|
jsonOut({ ...post, shareUrl });
|
|
@@ -278,8 +241,7 @@ export function registerGlobalCommand(program) {
|
|
|
278
241
|
` ID: ${post.id}\n` +
|
|
279
242
|
(post.title ? ` Title: ${post.title}\n` : "") +
|
|
280
243
|
` Created: ${post.createdAt}\n` +
|
|
281
|
-
` URL: ${shareUrl}`
|
|
282
|
-
(opts.draftId ? `\n Linked to draft: ${opts.draftId}` : ""));
|
|
244
|
+
` URL: ${shareUrl}`);
|
|
283
245
|
});
|
|
284
246
|
// ── Edit post ──
|
|
285
247
|
global
|
|
@@ -288,37 +250,23 @@ export function registerGlobalCommand(program) {
|
|
|
288
250
|
.option("--title <title>", "New title")
|
|
289
251
|
.option("--content <content>", "New content (markdown supported, use \"-\" for stdin)")
|
|
290
252
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
291
|
-
.option("--vault-slug <vaultSlug>", "Attribute the post to this vault (sets authorVaultSlug).")
|
|
292
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before editing (uses --vault-slug or .gobi vault)")
|
|
293
253
|
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged.", (value, prev = []) => [...prev, value], [])
|
|
294
254
|
.action(async (postId, opts) => {
|
|
295
|
-
const wantsVaultChange = !!(opts.vaultSlug || opts.autoAttachments);
|
|
296
255
|
const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
|
|
297
256
|
if (opts.title == null &&
|
|
298
257
|
opts.content == null &&
|
|
299
258
|
opts.richText == null &&
|
|
300
|
-
!wantsVaultChange &&
|
|
301
259
|
!wantsAttachChange) {
|
|
302
|
-
throw new Error("Provide at least --title, --content, --rich-text,
|
|
260
|
+
throw new Error("Provide at least --title, --content, --rich-text, or --attach to update.");
|
|
303
261
|
}
|
|
304
262
|
if (opts.content && opts.richText) {
|
|
305
263
|
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
306
264
|
}
|
|
307
|
-
let authorVaultSlug;
|
|
308
|
-
if (opts.vaultSlug || opts.autoAttachments) {
|
|
309
|
-
authorVaultSlug = resolveVaultSlug(opts);
|
|
310
|
-
}
|
|
311
265
|
const body = {};
|
|
312
266
|
if (opts.title != null)
|
|
313
267
|
body.title = opts.title;
|
|
314
268
|
if (opts.content != null) {
|
|
315
|
-
|
|
316
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
317
|
-
const token = await getValidToken();
|
|
318
|
-
const links = extractWikiLinks(content);
|
|
319
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
320
|
-
}
|
|
321
|
-
body.content = content;
|
|
269
|
+
body.content = readContent(opts.content);
|
|
322
270
|
}
|
|
323
271
|
if (opts.richText != null) {
|
|
324
272
|
let parsed;
|
|
@@ -330,8 +278,6 @@ export function registerGlobalCommand(program) {
|
|
|
330
278
|
}
|
|
331
279
|
body.richText = parsed;
|
|
332
280
|
}
|
|
333
|
-
if (authorVaultSlug !== undefined)
|
|
334
|
-
body.authorVaultSlug = authorVaultSlug;
|
|
335
281
|
if (opts.attach && opts.attach.length > 0) {
|
|
336
282
|
assertPostAttachmentMix(opts.attach);
|
|
337
283
|
body.attachments = await uploadPostAttachments(opts.attach);
|
|
@@ -362,8 +308,6 @@ export function registerGlobalCommand(program) {
|
|
|
362
308
|
.description("Create a reply to a post in the global feed.")
|
|
363
309
|
.option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
|
|
364
310
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
365
|
-
.option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
|
|
366
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting (also attributes the reply to that vault)")
|
|
367
311
|
.option("--attach <file>", "Local media file to attach to this reply. Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video.", (value, prev = []) => [...prev, value], [])
|
|
368
312
|
.action(async (postId, opts) => {
|
|
369
313
|
if (!opts.content && !opts.richText) {
|
|
@@ -372,19 +316,9 @@ export function registerGlobalCommand(program) {
|
|
|
372
316
|
if (opts.content && opts.richText) {
|
|
373
317
|
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
374
318
|
}
|
|
375
|
-
let authorVaultSlug;
|
|
376
|
-
if (opts.vaultSlug || opts.autoAttachments) {
|
|
377
|
-
authorVaultSlug = resolveVaultSlug(opts);
|
|
378
|
-
}
|
|
379
319
|
const body = {};
|
|
380
320
|
if (opts.content != null) {
|
|
381
|
-
|
|
382
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
383
|
-
const token = await getValidToken();
|
|
384
|
-
const links = extractWikiLinks(content);
|
|
385
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
386
|
-
}
|
|
387
|
-
body.content = content;
|
|
321
|
+
body.content = readContent(opts.content);
|
|
388
322
|
}
|
|
389
323
|
if (opts.richText != null) {
|
|
390
324
|
let parsed;
|
|
@@ -396,8 +330,6 @@ export function registerGlobalCommand(program) {
|
|
|
396
330
|
}
|
|
397
331
|
body.richText = parsed;
|
|
398
332
|
}
|
|
399
|
-
if (authorVaultSlug)
|
|
400
|
-
body.authorVaultSlug = authorVaultSlug;
|
|
401
333
|
if (opts.attach && opts.attach.length > 0) {
|
|
402
334
|
assertPostAttachmentMix(opts.attach);
|
|
403
335
|
body.attachments = await uploadPostAttachments(opts.attach);
|
|
@@ -415,29 +347,16 @@ export function registerGlobalCommand(program) {
|
|
|
415
347
|
.description("Edit a reply you authored in the global feed.")
|
|
416
348
|
.option("--content <content>", "New reply content (markdown supported, use \"-\" for stdin)")
|
|
417
349
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
418
|
-
.option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
|
|
419
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before editing (also attributes the reply to that vault)")
|
|
420
350
|
.action(async (replyId, opts) => {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
throw new Error("Provide at least --content, --rich-text, or --vault-slug to update.");
|
|
351
|
+
if (opts.content == null && opts.richText == null) {
|
|
352
|
+
throw new Error("Provide at least --content or --rich-text to update.");
|
|
424
353
|
}
|
|
425
354
|
if (opts.content && opts.richText) {
|
|
426
355
|
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
427
356
|
}
|
|
428
|
-
let authorVaultSlug;
|
|
429
|
-
if (opts.vaultSlug || opts.autoAttachments) {
|
|
430
|
-
authorVaultSlug = resolveVaultSlug(opts);
|
|
431
|
-
}
|
|
432
357
|
const body = {};
|
|
433
358
|
if (opts.content != null) {
|
|
434
|
-
|
|
435
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
436
|
-
const token = await getValidToken();
|
|
437
|
-
const links = extractWikiLinks(content);
|
|
438
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
439
|
-
}
|
|
440
|
-
body.content = content;
|
|
359
|
+
body.content = readContent(opts.content);
|
|
441
360
|
}
|
|
442
361
|
if (opts.richText != null) {
|
|
443
362
|
let parsed;
|
|
@@ -449,8 +368,6 @@ export function registerGlobalCommand(program) {
|
|
|
449
368
|
}
|
|
450
369
|
body.richText = parsed;
|
|
451
370
|
}
|
|
452
|
-
if (authorVaultSlug !== undefined)
|
|
453
|
-
body.authorVaultSlug = authorVaultSlug;
|
|
454
371
|
const resp = (await apiPatch(`/posts/replies/${replyId}`, body));
|
|
455
372
|
const reply = unwrapResp(resp);
|
|
456
373
|
if (isJsonMode(global)) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { getValidToken } from "../auth/manager.js";
|
|
2
|
+
import { isJsonMode, jsonOut, readStdin, unwrapResp, } from "./utils.js";
|
|
3
|
+
import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
|
|
5
4
|
function readContent(value) {
|
|
6
5
|
if (value === "-")
|
|
7
6
|
return readStdin();
|
|
@@ -186,48 +185,25 @@ export function registerPersonalCommand(program) {
|
|
|
186
185
|
// private posts have no audience.
|
|
187
186
|
personal
|
|
188
187
|
.command("create-post")
|
|
189
|
-
.description("Create a private post in your personal space.
|
|
188
|
+
.description("Create a private post in your personal space. Visible only to you.")
|
|
190
189
|
.option("--title <title>", "Title of the post")
|
|
191
190
|
.option("--content <content>", "Post content (markdown supported, use \"-\" for stdin)")
|
|
192
191
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
193
|
-
.option("--
|
|
194
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting (also sets authorVaultSlug to that vault)")
|
|
195
|
-
.option("--draft-id <draftId>", "Use this draft as the source of title and content (mutually exclusive with --title/--content/--rich-text). On success, links the post back via draft.metadata. The draft's vaultSlug seeds --vault-slug when not given explicitly.")
|
|
192
|
+
.option("--artifact <artifactId>", "Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
196
193
|
.option("--attach <file>", "Local media file to attach. Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video.", (value, prev = []) => [...prev, value], [])
|
|
197
194
|
.option("--repost-post-id <postId>", "Wrap an existing top-level post as the embedded card on this new private post. The referenced post must be visible to you (your own personal-space post, a global-feed post, or a post in a space you're a member of). Reposting someone else's personal-space post returns 404.")
|
|
198
195
|
.action(async (opts) => {
|
|
199
|
-
if (opts.
|
|
200
|
-
|
|
201
|
-
throw new Error("--draft-id sources title and content from the draft; --title, --content, and --rich-text are not allowed alongside it.");
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
if (!opts.content && !opts.richText) {
|
|
206
|
-
throw new Error("Provide either --content or --rich-text (or --draft-id).");
|
|
207
|
-
}
|
|
208
|
-
if (opts.content && opts.richText) {
|
|
209
|
-
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
210
|
-
}
|
|
196
|
+
if (!opts.content && !opts.richText) {
|
|
197
|
+
throw new Error("Provide either --content or --rich-text.");
|
|
211
198
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const effectiveContent = draft ? draft.content : opts.content;
|
|
215
|
-
const effectiveVaultSlugOpt = opts.vaultSlug ?? draft?.vaultSlug ?? undefined;
|
|
216
|
-
let authorVaultSlug;
|
|
217
|
-
if (effectiveVaultSlugOpt || opts.autoAttachments) {
|
|
218
|
-
authorVaultSlug = resolveVaultSlug({ vaultSlug: effectiveVaultSlugOpt });
|
|
199
|
+
if (opts.content && opts.richText) {
|
|
200
|
+
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
219
201
|
}
|
|
220
202
|
const body = {};
|
|
221
|
-
if (
|
|
222
|
-
body.title =
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
226
|
-
const token = await getValidToken();
|
|
227
|
-
const links = extractWikiLinks(content);
|
|
228
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
229
|
-
}
|
|
230
|
-
body.content = content;
|
|
203
|
+
if (opts.title != null)
|
|
204
|
+
body.title = opts.title;
|
|
205
|
+
if (opts.content != null) {
|
|
206
|
+
body.content = readContent(opts.content);
|
|
231
207
|
}
|
|
232
208
|
if (opts.richText != null) {
|
|
233
209
|
let parsed;
|
|
@@ -239,8 +215,8 @@ export function registerPersonalCommand(program) {
|
|
|
239
215
|
}
|
|
240
216
|
body.richText = parsed;
|
|
241
217
|
}
|
|
242
|
-
if (
|
|
243
|
-
body.
|
|
218
|
+
if (opts.artifact && opts.artifact.length > 0)
|
|
219
|
+
body.artifactIds = opts.artifact;
|
|
244
220
|
if (opts.attach && opts.attach.length > 0) {
|
|
245
221
|
assertPostAttachmentMix(opts.attach);
|
|
246
222
|
body.attachments = await uploadPostAttachments(opts.attach);
|
|
@@ -254,17 +230,6 @@ export function registerPersonalCommand(program) {
|
|
|
254
230
|
}
|
|
255
231
|
const resp = (await apiPost(`/posts/personal-space`, body));
|
|
256
232
|
const post = unwrapResp(resp);
|
|
257
|
-
if (opts.draftId && post.id != null) {
|
|
258
|
-
try {
|
|
259
|
-
await apiPatch(`/app/drafts/${opts.draftId}/metadata`, {
|
|
260
|
-
postId: typeof post.id === "number" ? post.id : Number(post.id),
|
|
261
|
-
spaceSlug: null,
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
catch (e) {
|
|
265
|
-
console.error(`Warning: failed to link post to draft ${opts.draftId}: ${e.message}`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
233
|
if (isJsonMode(personal)) {
|
|
269
234
|
jsonOut(post);
|
|
270
235
|
return;
|
|
@@ -273,8 +238,7 @@ export function registerPersonalCommand(program) {
|
|
|
273
238
|
` ID: ${post.id}\n` +
|
|
274
239
|
(post.title ? ` Title: ${post.title}\n` : "") +
|
|
275
240
|
` Created: ${post.createdAt}\n` +
|
|
276
|
-
` Visibility: private (only you can see this)`
|
|
277
|
-
(opts.draftId ? `\n Linked to draft: ${opts.draftId}` : ""));
|
|
241
|
+
` Visibility: private (only you can see this)`);
|
|
278
242
|
});
|
|
279
243
|
// ── Edit post ──
|
|
280
244
|
//
|
|
@@ -287,37 +251,23 @@ export function registerPersonalCommand(program) {
|
|
|
287
251
|
.option("--title <title>", "New title")
|
|
288
252
|
.option("--content <content>", "New content (markdown supported, use \"-\" for stdin)")
|
|
289
253
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
290
|
-
.option("--vault-slug <vaultSlug>", "Attribute the post to this vault (sets authorVaultSlug).")
|
|
291
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before editing (uses --vault-slug or .gobi vault)")
|
|
292
254
|
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged.", (value, prev = []) => [...prev, value], [])
|
|
293
255
|
.action(async (postId, opts) => {
|
|
294
|
-
const wantsVaultChange = !!(opts.vaultSlug || opts.autoAttachments);
|
|
295
256
|
const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
|
|
296
257
|
if (opts.title == null &&
|
|
297
258
|
opts.content == null &&
|
|
298
259
|
opts.richText == null &&
|
|
299
|
-
!wantsVaultChange &&
|
|
300
260
|
!wantsAttachChange) {
|
|
301
|
-
throw new Error("Provide at least --title, --content, --rich-text,
|
|
261
|
+
throw new Error("Provide at least --title, --content, --rich-text, or --attach to update.");
|
|
302
262
|
}
|
|
303
263
|
if (opts.content && opts.richText) {
|
|
304
264
|
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
305
265
|
}
|
|
306
|
-
let authorVaultSlug;
|
|
307
|
-
if (opts.vaultSlug || opts.autoAttachments) {
|
|
308
|
-
authorVaultSlug = resolveVaultSlug(opts);
|
|
309
|
-
}
|
|
310
266
|
const body = {};
|
|
311
267
|
if (opts.title != null)
|
|
312
268
|
body.title = opts.title;
|
|
313
269
|
if (opts.content != null) {
|
|
314
|
-
|
|
315
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
316
|
-
const token = await getValidToken();
|
|
317
|
-
const links = extractWikiLinks(content);
|
|
318
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
319
|
-
}
|
|
320
|
-
body.content = content;
|
|
270
|
+
body.content = readContent(opts.content);
|
|
321
271
|
}
|
|
322
272
|
if (opts.richText != null) {
|
|
323
273
|
let parsed;
|
|
@@ -329,8 +279,6 @@ export function registerPersonalCommand(program) {
|
|
|
329
279
|
}
|
|
330
280
|
body.richText = parsed;
|
|
331
281
|
}
|
|
332
|
-
if (authorVaultSlug !== undefined)
|
|
333
|
-
body.authorVaultSlug = authorVaultSlug;
|
|
334
282
|
if (opts.attach && opts.attach.length > 0) {
|
|
335
283
|
assertPostAttachmentMix(opts.attach);
|
|
336
284
|
body.attachments = await uploadPostAttachments(opts.attach);
|
|
@@ -366,8 +314,6 @@ export function registerPersonalCommand(program) {
|
|
|
366
314
|
.description("Reply to a personal-space post. The reply inherits the parent's private scope automatically.")
|
|
367
315
|
.option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
|
|
368
316
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
369
|
-
.option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
|
|
370
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting (also attributes the reply to that vault)")
|
|
371
317
|
.option("--attach <file>", "Local media file to attach to this reply. Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video.", (value, prev = []) => [...prev, value], [])
|
|
372
318
|
.action(async (postId, opts) => {
|
|
373
319
|
if (!opts.content && !opts.richText) {
|
|
@@ -376,19 +322,9 @@ export function registerPersonalCommand(program) {
|
|
|
376
322
|
if (opts.content && opts.richText) {
|
|
377
323
|
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
378
324
|
}
|
|
379
|
-
let authorVaultSlug;
|
|
380
|
-
if (opts.vaultSlug || opts.autoAttachments) {
|
|
381
|
-
authorVaultSlug = resolveVaultSlug(opts);
|
|
382
|
-
}
|
|
383
325
|
const body = {};
|
|
384
326
|
if (opts.content != null) {
|
|
385
|
-
|
|
386
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
387
|
-
const token = await getValidToken();
|
|
388
|
-
const links = extractWikiLinks(content);
|
|
389
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
390
|
-
}
|
|
391
|
-
body.content = content;
|
|
327
|
+
body.content = readContent(opts.content);
|
|
392
328
|
}
|
|
393
329
|
if (opts.richText != null) {
|
|
394
330
|
let parsed;
|
|
@@ -400,8 +336,6 @@ export function registerPersonalCommand(program) {
|
|
|
400
336
|
}
|
|
401
337
|
body.richText = parsed;
|
|
402
338
|
}
|
|
403
|
-
if (authorVaultSlug)
|
|
404
|
-
body.authorVaultSlug = authorVaultSlug;
|
|
405
339
|
if (opts.attach && opts.attach.length > 0) {
|
|
406
340
|
assertPostAttachmentMix(opts.attach);
|
|
407
341
|
body.attachments = await uploadPostAttachments(opts.attach);
|
|
@@ -419,29 +353,16 @@ export function registerPersonalCommand(program) {
|
|
|
419
353
|
.description("Edit a reply you authored in your personal space.")
|
|
420
354
|
.option("--content <content>", "New reply content (markdown supported, use \"-\" for stdin)")
|
|
421
355
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
422
|
-
.option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
|
|
423
|
-
.option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before editing (also attributes the reply to that vault)")
|
|
424
356
|
.action(async (replyId, opts) => {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
throw new Error("Provide at least --content, --rich-text, or --vault-slug to update.");
|
|
357
|
+
if (opts.content == null && opts.richText == null) {
|
|
358
|
+
throw new Error("Provide at least --content or --rich-text to update.");
|
|
428
359
|
}
|
|
429
360
|
if (opts.content && opts.richText) {
|
|
430
361
|
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
431
362
|
}
|
|
432
|
-
let authorVaultSlug;
|
|
433
|
-
if (opts.vaultSlug || opts.autoAttachments) {
|
|
434
|
-
authorVaultSlug = resolveVaultSlug(opts);
|
|
435
|
-
}
|
|
436
363
|
const body = {};
|
|
437
364
|
if (opts.content != null) {
|
|
438
|
-
|
|
439
|
-
if (opts.autoAttachments && authorVaultSlug) {
|
|
440
|
-
const token = await getValidToken();
|
|
441
|
-
const links = extractWikiLinks(content);
|
|
442
|
-
await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
|
|
443
|
-
}
|
|
444
|
-
body.content = content;
|
|
365
|
+
body.content = readContent(opts.content);
|
|
445
366
|
}
|
|
446
367
|
if (opts.richText != null) {
|
|
447
368
|
let parsed;
|
|
@@ -453,8 +374,6 @@ export function registerPersonalCommand(program) {
|
|
|
453
374
|
}
|
|
454
375
|
body.richText = parsed;
|
|
455
376
|
}
|
|
456
|
-
if (authorVaultSlug !== undefined)
|
|
457
|
-
body.authorVaultSlug = authorVaultSlug;
|
|
458
377
|
const resp = (await apiPatch(`/posts/replies/${replyId}`, body));
|
|
459
378
|
const reply = unwrapResp(resp);
|
|
460
379
|
if (isJsonMode(personal)) {
|