@myvillage/cli 1.10.1 → 1.17.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/package.json +1 -1
- package/src/agent-runtime/loop.js +81 -3
- package/src/commands/agent-client.js +435 -0
- package/src/commands/agent-grant.js +131 -0
- package/src/commands/agent-local.js +354 -1
- package/src/commands/create-app.js +61 -1
- package/src/commands/game.js +66 -0
- package/src/commands/media.js +185 -187
- package/src/commands/wisdom.js +185 -0
- package/src/index.js +205 -0
- package/src/utils/agentic-templates.js +10 -2
- package/src/utils/api.js +157 -0
- package/src/utils/formatters.js +72 -0
- package/src/utils/wisdom.js +102 -0
package/src/commands/media.js
CHANGED
|
@@ -35,15 +35,34 @@ function statusBadge(status) {
|
|
|
35
35
|
return colorFn(status);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
// ──
|
|
38
|
+
// ── Content-type display names ─────────────────────────
|
|
39
|
+
// Reels in the mvp-mobile-react Soulprint feed are categorized by content
|
|
40
|
+
// type, not by external platform. The legacy LinkedIn/YouTube/TikTok/Instagram
|
|
41
|
+
// labels are kept temporarily as aliases so existing scripts keep working.
|
|
42
|
+
|
|
43
|
+
const CONTENT_TYPE_LABELS = {
|
|
44
|
+
VIDEO: 'Video',
|
|
45
|
+
PHOTO: 'Photo',
|
|
46
|
+
CAROUSEL: 'Carousel',
|
|
47
|
+
AUDIO: 'Audio / Podcast',
|
|
48
|
+
TEXT: 'Quote / Text',
|
|
49
|
+
};
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
// Back-compat: legacy platform → content type. Used only for display fallback
|
|
52
|
+
// when reading older drafts from the API.
|
|
53
|
+
const LEGACY_PLATFORM_TO_CONTENT_TYPE = {
|
|
54
|
+
LINKEDIN: 'TEXT',
|
|
55
|
+
YOUTUBE: 'VIDEO',
|
|
56
|
+
TIKTOK: 'VIDEO',
|
|
57
|
+
INSTAGRAM: 'PHOTO',
|
|
45
58
|
};
|
|
46
59
|
|
|
60
|
+
function resolveContentTypeLabel(draft) {
|
|
61
|
+
const ct = draft?.contentType
|
|
62
|
+
|| (draft?.platform ? LEGACY_PLATFORM_TO_CONTENT_TYPE[draft.platform] : undefined);
|
|
63
|
+
return ct ? (CONTENT_TYPE_LABELS[ct] || ct) : 'Unknown';
|
|
64
|
+
}
|
|
65
|
+
|
|
47
66
|
// ── Size thresholds ────────────────────────────────────
|
|
48
67
|
|
|
49
68
|
// Files under this size use the simple buffered upload endpoint.
|
|
@@ -79,154 +98,107 @@ function inferAssetType(fileName) {
|
|
|
79
98
|
return null;
|
|
80
99
|
}
|
|
81
100
|
|
|
82
|
-
// ──
|
|
101
|
+
// ── Content-type-specific extra fields ─────────────────
|
|
102
|
+
//
|
|
103
|
+
// Asks for fields the reel renderer needs but caption/title don't cover —
|
|
104
|
+
// e.g. quote text + attribution for TEXT reels, podcast/episode title for
|
|
105
|
+
// AUDIO reels. Returns a sparse object suitable for spreading into the
|
|
106
|
+
// API payload. Empty strings are dropped.
|
|
83
107
|
|
|
84
|
-
async function
|
|
108
|
+
async function promptContentTypeFields(contentType, existing = {}) {
|
|
85
109
|
const prompts = [];
|
|
86
110
|
|
|
87
|
-
if (
|
|
111
|
+
if (contentType === 'TEXT') {
|
|
88
112
|
prompts.push(
|
|
89
|
-
{
|
|
90
|
-
type: 'list',
|
|
91
|
-
name: 'privacy',
|
|
92
|
-
message: 'Privacy:',
|
|
93
|
-
choices: [
|
|
94
|
-
{ name: 'Public', value: 'public' },
|
|
95
|
-
{ name: 'Unlisted', value: 'unlisted' },
|
|
96
|
-
{ name: 'Private', value: 'private' },
|
|
97
|
-
],
|
|
98
|
-
default: existing.privacy || 'public',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
type: 'list',
|
|
102
|
-
name: 'category',
|
|
103
|
-
message: 'Category:',
|
|
104
|
-
choices: [
|
|
105
|
-
{ name: 'Education', value: 'Education' },
|
|
106
|
-
{ name: 'Science & Technology', value: 'Science & Technology' },
|
|
107
|
-
{ name: 'People & Blogs', value: 'People & Blogs' },
|
|
108
|
-
{ name: 'Entertainment', value: 'Entertainment' },
|
|
109
|
-
{ name: 'Howto & Style', value: 'Howto & Style' },
|
|
110
|
-
{ name: 'Gaming', value: 'Gaming' },
|
|
111
|
-
{ name: 'Nonprofits & Activism', value: 'Nonprofits & Activism' },
|
|
112
|
-
],
|
|
113
|
-
default: existing.category || 'Education',
|
|
114
|
-
},
|
|
115
113
|
{
|
|
116
114
|
type: 'input',
|
|
117
|
-
name: '
|
|
118
|
-
message: '
|
|
119
|
-
default: existing.
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (platform === 'LINKEDIN') {
|
|
125
|
-
prompts.push(
|
|
126
|
-
{
|
|
127
|
-
type: 'list',
|
|
128
|
-
name: 'visibility',
|
|
129
|
-
message: 'Visibility:',
|
|
130
|
-
choices: [
|
|
131
|
-
{ name: 'Public (anyone)', value: 'PUBLIC' },
|
|
132
|
-
{ name: 'Connections only', value: 'CONNECTIONS' },
|
|
133
|
-
],
|
|
134
|
-
default: existing.visibility || 'PUBLIC',
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
type: 'list',
|
|
138
|
-
name: 'contentType',
|
|
139
|
-
message: 'Post type:',
|
|
140
|
-
choices: [
|
|
141
|
-
{ name: 'Share / Status update', value: 'share' },
|
|
142
|
-
{ name: 'Article', value: 'article' },
|
|
143
|
-
],
|
|
144
|
-
default: existing.contentType || 'share',
|
|
145
|
-
},
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (platform === 'TIKTOK') {
|
|
150
|
-
prompts.push(
|
|
151
|
-
{
|
|
152
|
-
type: 'list',
|
|
153
|
-
name: 'privacy',
|
|
154
|
-
message: 'Privacy:',
|
|
155
|
-
choices: [
|
|
156
|
-
{ name: 'Public', value: 'PUBLIC_TO_EVERYONE' },
|
|
157
|
-
{ name: 'Friends only', value: 'MUTUAL_FOLLOW_FRIENDS' },
|
|
158
|
-
{ name: 'Private (only me)', value: 'SELF_ONLY' },
|
|
159
|
-
],
|
|
160
|
-
default: existing.privacy || 'PUBLIC_TO_EVERYONE',
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
type: 'confirm',
|
|
164
|
-
name: 'allowComments',
|
|
165
|
-
message: 'Allow comments?',
|
|
166
|
-
default: existing.allowComments !== undefined ? existing.allowComments : true,
|
|
115
|
+
name: 'quoteText',
|
|
116
|
+
message: 'Quote / body text:',
|
|
117
|
+
default: existing.quoteText || '',
|
|
118
|
+
validate: (v) => v.trim() ? true : 'Quote text is required for TEXT reels',
|
|
167
119
|
},
|
|
168
120
|
{
|
|
169
|
-
type: '
|
|
170
|
-
name: '
|
|
171
|
-
message: '
|
|
172
|
-
default: existing.
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
type: 'confirm',
|
|
176
|
-
name: 'allowStitch',
|
|
177
|
-
message: 'Allow stitches?',
|
|
178
|
-
default: existing.allowStitch !== undefined ? existing.allowStitch : true,
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
type: 'confirm',
|
|
182
|
-
name: 'brandedContent',
|
|
183
|
-
message: 'Is this branded content?',
|
|
184
|
-
default: existing.brandedContent || false,
|
|
121
|
+
type: 'input',
|
|
122
|
+
name: 'attribution',
|
|
123
|
+
message: 'Attribution (e.g., “— Maya Angelou”, optional):',
|
|
124
|
+
default: existing.attribution || '',
|
|
185
125
|
},
|
|
186
126
|
);
|
|
187
127
|
}
|
|
188
128
|
|
|
189
|
-
if (
|
|
129
|
+
if (contentType === 'AUDIO') {
|
|
190
130
|
prompts.push(
|
|
191
131
|
{
|
|
192
132
|
type: 'input',
|
|
193
|
-
name: '
|
|
194
|
-
message: '
|
|
195
|
-
default: existing.
|
|
133
|
+
name: 'podcastTitle',
|
|
134
|
+
message: 'Podcast / show title:',
|
|
135
|
+
default: existing.podcastTitle || '',
|
|
196
136
|
},
|
|
197
137
|
{
|
|
198
138
|
type: 'input',
|
|
199
|
-
name: '
|
|
200
|
-
message: '
|
|
201
|
-
default: existing.
|
|
139
|
+
name: 'episodeTitle',
|
|
140
|
+
message: 'Episode title:',
|
|
141
|
+
default: existing.episodeTitle || '',
|
|
202
142
|
},
|
|
203
143
|
{
|
|
204
144
|
type: 'input',
|
|
205
|
-
name: '
|
|
206
|
-
message: '
|
|
207
|
-
default: existing.
|
|
145
|
+
name: 'audioDuration',
|
|
146
|
+
message: 'Duration in seconds (optional):',
|
|
147
|
+
default: existing.audioDuration ? String(existing.audioDuration) : '',
|
|
148
|
+
filter: (v) => v ? Number(v) || undefined : undefined,
|
|
208
149
|
},
|
|
209
150
|
);
|
|
210
151
|
}
|
|
211
152
|
|
|
212
|
-
if (
|
|
153
|
+
if (contentType === 'VIDEO') {
|
|
154
|
+
prompts.push({
|
|
155
|
+
type: 'input',
|
|
156
|
+
name: 'videoDuration',
|
|
157
|
+
message: 'Duration in seconds (optional):',
|
|
158
|
+
default: existing.videoDuration ? String(existing.videoDuration) : '',
|
|
159
|
+
filter: (v) => v ? Number(v) || undefined : undefined,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (prompts.length === 0) return {};
|
|
213
164
|
|
|
214
|
-
console.log(brand.teal(`\n ${
|
|
165
|
+
console.log(brand.teal(`\n ${CONTENT_TYPE_LABELS[contentType]} details:\n`));
|
|
215
166
|
const answers = await inquirer.prompt(prompts);
|
|
216
167
|
|
|
217
|
-
|
|
218
|
-
const metadata = {};
|
|
168
|
+
const out = {};
|
|
219
169
|
for (const [key, value] of Object.entries(answers)) {
|
|
220
170
|
if (value === '' || value === undefined) continue;
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
171
|
+
out[key] = typeof value === 'string' ? value.trim() : value;
|
|
172
|
+
}
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function promptVisibilityAndCommunity(existing = {}) {
|
|
177
|
+
const { visibility } = await inquirer.prompt([{
|
|
178
|
+
type: 'list',
|
|
179
|
+
name: 'visibility',
|
|
180
|
+
message: 'Who can see this reel?',
|
|
181
|
+
choices: [
|
|
182
|
+
{ name: 'Public — everyone in the app', value: 'PUBLIC' },
|
|
183
|
+
{ name: 'Community only — pick one below', value: 'COMMUNITY' },
|
|
184
|
+
{ name: 'Private — only me (preview)', value: 'PRIVATE' },
|
|
185
|
+
],
|
|
186
|
+
default: existing.visibility || 'PUBLIC',
|
|
187
|
+
}]);
|
|
188
|
+
|
|
189
|
+
let communityId = existing.communityId || undefined;
|
|
190
|
+
if (visibility === 'COMMUNITY') {
|
|
191
|
+
const { id } = await inquirer.prompt([{
|
|
192
|
+
type: 'input',
|
|
193
|
+
name: 'id',
|
|
194
|
+
message: 'Community ID (uuid):',
|
|
195
|
+
default: communityId || '',
|
|
196
|
+
validate: (v) => v.trim() ? true : 'Community ID is required for COMMUNITY visibility',
|
|
197
|
+
}]);
|
|
198
|
+
communityId = id.trim();
|
|
227
199
|
}
|
|
228
200
|
|
|
229
|
-
return
|
|
201
|
+
return { visibility, communityId };
|
|
230
202
|
}
|
|
231
203
|
|
|
232
204
|
// ── media draft create ─────────────────────────────────
|
|
@@ -238,51 +210,53 @@ export async function mediaDraftCreateCommand(options = {}) {
|
|
|
238
210
|
}
|
|
239
211
|
|
|
240
212
|
try {
|
|
241
|
-
console.log(`\n ${brand.gold(chalk.bold('\u2726
|
|
213
|
+
console.log(`\n ${brand.gold(chalk.bold('\u2726 Soulprint Reel \u2014 New Submission'))}\n`);
|
|
242
214
|
|
|
243
|
-
//
|
|
244
|
-
const
|
|
215
|
+
// Content-type selection (replaces "platform")
|
|
216
|
+
const { contentType } = await inquirer.prompt([{
|
|
245
217
|
type: 'list',
|
|
246
|
-
name: '
|
|
247
|
-
message: '
|
|
218
|
+
name: 'contentType',
|
|
219
|
+
message: 'Content type:',
|
|
248
220
|
choices: [
|
|
249
|
-
{ name: '
|
|
250
|
-
{ name: '
|
|
251
|
-
{ name: '
|
|
252
|
-
{ name: '
|
|
221
|
+
{ name: 'Video', value: 'VIDEO' },
|
|
222
|
+
{ name: 'Photo', value: 'PHOTO' },
|
|
223
|
+
{ name: 'Photo carousel', value: 'CAROUSEL' },
|
|
224
|
+
{ name: 'Audio / podcast', value: 'AUDIO' },
|
|
225
|
+
{ name: 'Quote / text', value: 'TEXT' },
|
|
253
226
|
],
|
|
254
227
|
}]);
|
|
255
228
|
|
|
256
|
-
|
|
257
|
-
const
|
|
229
|
+
const needsTitle = contentType === 'VIDEO' || contentType === 'AUDIO';
|
|
230
|
+
const isLongForm = contentType === 'VIDEO' || contentType === 'AUDIO';
|
|
258
231
|
|
|
259
232
|
const contentPrompts = [];
|
|
260
233
|
|
|
261
|
-
if (
|
|
234
|
+
if (needsTitle) {
|
|
262
235
|
contentPrompts.push({
|
|
263
236
|
type: 'input',
|
|
264
237
|
name: 'title',
|
|
265
|
-
message: '
|
|
266
|
-
validate: (v) => v.trim() ? true : 'Title is required for YouTube',
|
|
238
|
+
message: 'Title:',
|
|
267
239
|
});
|
|
268
240
|
}
|
|
269
241
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
242
|
+
if (contentType !== 'TEXT') {
|
|
243
|
+
contentPrompts.push({
|
|
244
|
+
type: 'input',
|
|
245
|
+
name: 'caption',
|
|
246
|
+
message: 'Caption:',
|
|
247
|
+
validate: (v, answers) => {
|
|
248
|
+
if (v.trim()) return true;
|
|
249
|
+
if (answers?.title?.trim()) return true;
|
|
250
|
+
return 'Caption is required';
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
}
|
|
280
254
|
|
|
281
|
-
if (
|
|
255
|
+
if (isLongForm) {
|
|
282
256
|
contentPrompts.push({
|
|
283
257
|
type: 'input',
|
|
284
258
|
name: 'script',
|
|
285
|
-
message: 'Script (optional, press Enter to skip):',
|
|
259
|
+
message: 'Script / show notes (optional, press Enter to skip):',
|
|
286
260
|
});
|
|
287
261
|
}
|
|
288
262
|
|
|
@@ -300,12 +274,16 @@ export async function mediaDraftCreateCommand(options = {}) {
|
|
|
300
274
|
|
|
301
275
|
const answers = await inquirer.prompt(contentPrompts);
|
|
302
276
|
|
|
303
|
-
//
|
|
304
|
-
const
|
|
277
|
+
// Type-specific extras (quote/attribution for TEXT, podcast/episode for AUDIO, etc.)
|
|
278
|
+
const typeExtras = await promptContentTypeFields(contentType);
|
|
279
|
+
|
|
280
|
+
// Visibility + community
|
|
281
|
+
const { visibility, communityId } = await promptVisibilityAndCommunity();
|
|
305
282
|
|
|
306
283
|
// Build draft payload
|
|
307
284
|
const data = {
|
|
308
|
-
|
|
285
|
+
contentType,
|
|
286
|
+
visibility,
|
|
309
287
|
intakeMethod: 'CLI',
|
|
310
288
|
};
|
|
311
289
|
|
|
@@ -318,9 +296,8 @@ export async function mediaDraftCreateCommand(options = {}) {
|
|
|
318
296
|
if (answers.suggestedPublishAt?.trim()) {
|
|
319
297
|
data.suggestedPublishAt = new Date(answers.suggestedPublishAt.trim()).toISOString();
|
|
320
298
|
}
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
}
|
|
299
|
+
if (communityId) data.communityId = communityId;
|
|
300
|
+
Object.assign(data, typeExtras);
|
|
324
301
|
|
|
325
302
|
// Auto-submit flag
|
|
326
303
|
if (options.submit) {
|
|
@@ -334,7 +311,7 @@ export async function mediaDraftCreateCommand(options = {}) {
|
|
|
334
311
|
|
|
335
312
|
const draft = result.draft;
|
|
336
313
|
console.log(brand.green(`\n \u2713 Draft ID: ${chalk.bold(draft.draftId)}`));
|
|
337
|
-
console.log(brand.teal(`
|
|
314
|
+
console.log(brand.teal(` Type: ${resolveContentTypeLabel(draft)}`));
|
|
338
315
|
console.log(brand.teal(` Status: ${statusBadge(draft.status)}`));
|
|
339
316
|
|
|
340
317
|
// ── File uploads ───────────────────────────────────
|
|
@@ -586,11 +563,13 @@ export async function mediaDraftEditCommand(id) {
|
|
|
586
563
|
return;
|
|
587
564
|
}
|
|
588
565
|
|
|
589
|
-
const
|
|
590
|
-
|
|
566
|
+
const contentType = draft.contentType
|
|
567
|
+
|| LEGACY_PLATFORM_TO_CONTENT_TYPE[draft.platform]
|
|
568
|
+
|| 'VIDEO';
|
|
569
|
+
const isLongForm = contentType === 'VIDEO' || contentType === 'AUDIO';
|
|
591
570
|
|
|
592
|
-
console.log(`\n ${brand.gold(chalk.bold(`\u2726 Edit
|
|
593
|
-
console.log(brand.teal(`
|
|
571
|
+
console.log(`\n ${brand.gold(chalk.bold(`\u2726 Edit Reel \u2014 ${draft.draftId}`))}`);
|
|
572
|
+
console.log(brand.teal(` Type: ${resolveContentTypeLabel({ ...draft, contentType })} Status: ${statusBadge(draft.status)}`));
|
|
594
573
|
|
|
595
574
|
if (draft.rejectionReason) {
|
|
596
575
|
console.log(chalk.red(` Rejection reason: ${draft.rejectionReason}`));
|
|
@@ -609,27 +588,29 @@ export async function mediaDraftEditCommand(id) {
|
|
|
609
588
|
// Content fields
|
|
610
589
|
const contentPrompts = [];
|
|
611
590
|
|
|
612
|
-
if (
|
|
591
|
+
if (contentType === 'VIDEO' || contentType === 'AUDIO') {
|
|
613
592
|
contentPrompts.push({
|
|
614
593
|
type: 'input',
|
|
615
594
|
name: 'title',
|
|
616
|
-
message:
|
|
595
|
+
message: 'Title:',
|
|
617
596
|
default: draft.title || '',
|
|
618
597
|
});
|
|
619
598
|
}
|
|
620
599
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
600
|
+
if (contentType !== 'TEXT') {
|
|
601
|
+
contentPrompts.push({
|
|
602
|
+
type: 'input',
|
|
603
|
+
name: 'caption',
|
|
604
|
+
message: isLongForm ? 'Description / caption:' : 'Caption:',
|
|
605
|
+
default: draft.caption || '',
|
|
606
|
+
});
|
|
607
|
+
}
|
|
627
608
|
|
|
628
|
-
if (
|
|
609
|
+
if (isLongForm) {
|
|
629
610
|
contentPrompts.push({
|
|
630
611
|
type: 'input',
|
|
631
612
|
name: 'script',
|
|
632
|
-
message: 'Script:',
|
|
613
|
+
message: 'Script / show notes:',
|
|
633
614
|
default: draft.script || '',
|
|
634
615
|
});
|
|
635
616
|
}
|
|
@@ -652,20 +633,22 @@ export async function mediaDraftEditCommand(id) {
|
|
|
652
633
|
|
|
653
634
|
const answers = await inquirer.prompt(contentPrompts);
|
|
654
635
|
|
|
655
|
-
//
|
|
656
|
-
const
|
|
657
|
-
const platformMetadata = await promptPlatformMetadata(platform, existingMetadata);
|
|
636
|
+
// Type-specific extras (quote/attribution, podcast/episode, etc.)
|
|
637
|
+
const typeExtras = await promptContentTypeFields(contentType, draft);
|
|
658
638
|
|
|
659
639
|
// Build update payload — only send changed fields
|
|
660
640
|
const updates = {};
|
|
661
641
|
|
|
662
|
-
if (
|
|
642
|
+
if (
|
|
643
|
+
(contentType === 'VIDEO' || contentType === 'AUDIO') &&
|
|
644
|
+
answers.title !== (draft.title || '')
|
|
645
|
+
) {
|
|
663
646
|
updates.title = answers.title.trim() || undefined;
|
|
664
647
|
}
|
|
665
|
-
if (answers.caption !== (draft.caption || '')) {
|
|
648
|
+
if (contentType !== 'TEXT' && answers.caption !== (draft.caption || '')) {
|
|
666
649
|
updates.caption = answers.caption.trim() || undefined;
|
|
667
650
|
}
|
|
668
|
-
if (
|
|
651
|
+
if (isLongForm && answers.script !== (draft.script || '')) {
|
|
669
652
|
updates.script = answers.script.trim() || undefined;
|
|
670
653
|
}
|
|
671
654
|
|
|
@@ -686,9 +669,7 @@ export async function mediaDraftEditCommand(id) {
|
|
|
686
669
|
: null;
|
|
687
670
|
}
|
|
688
671
|
|
|
689
|
-
|
|
690
|
-
updates.platformMetadata = platformMetadata;
|
|
691
|
-
}
|
|
672
|
+
Object.assign(updates, typeExtras);
|
|
692
673
|
|
|
693
674
|
// File uploads
|
|
694
675
|
const filesToUpload = [];
|
|
@@ -816,7 +797,15 @@ export async function mediaDraftListCommand(options = {}) {
|
|
|
816
797
|
sort: options.sort || 'newest',
|
|
817
798
|
};
|
|
818
799
|
if (options.status) params.status = options.status.toUpperCase();
|
|
819
|
-
if (options.
|
|
800
|
+
if (options.contentType) params.contentType = options.contentType.toUpperCase();
|
|
801
|
+
// Back-compat: --platform still accepted on the CLI; map it forward.
|
|
802
|
+
if (options.platform) {
|
|
803
|
+
params.contentType =
|
|
804
|
+
LEGACY_PLATFORM_TO_CONTENT_TYPE[options.platform.toUpperCase()] ||
|
|
805
|
+
options.platform.toUpperCase();
|
|
806
|
+
}
|
|
807
|
+
if (options.visibility) params.visibility = options.visibility.toUpperCase();
|
|
808
|
+
if (options.communityId) params.communityId = options.communityId;
|
|
820
809
|
if (options.search) params.search = options.search;
|
|
821
810
|
|
|
822
811
|
const result = await listMediaDrafts(params);
|
|
@@ -842,16 +831,22 @@ export async function mediaDraftListCommand(options = {}) {
|
|
|
842
831
|
console.log(`\n ${chalk.bold(`Media Drafts${statusLabel}`)}\n`);
|
|
843
832
|
|
|
844
833
|
for (const draft of drafts) {
|
|
845
|
-
const
|
|
834
|
+
const typeLabel = resolveContentTypeLabel(draft);
|
|
846
835
|
const status = statusBadge(draft.status);
|
|
847
836
|
const preview = draft.caption
|
|
848
837
|
? draft.caption.substring(0, 60) + (draft.caption.length > 60 ? '...' : '')
|
|
849
|
-
: draft.title
|
|
838
|
+
: draft.title
|
|
839
|
+
|| draft.quoteText?.substring(0, 60)
|
|
840
|
+
|| draft.script?.substring(0, 60)
|
|
841
|
+
|| '(no content)';
|
|
850
842
|
const date = new Date(draft.createdAt).toLocaleDateString();
|
|
851
843
|
const assetCount = draft.assets?.length || draft._count?.assets || 0;
|
|
852
844
|
const assetLabel = assetCount > 0 ? chalk.dim(` [${assetCount} file${assetCount > 1 ? 's' : ''}]`) : '';
|
|
845
|
+
const engagement = (draft.likeCount || draft.commentCount)
|
|
846
|
+
? chalk.dim(` ♥${draft.likeCount || 0} 💬${draft.commentCount || 0}`)
|
|
847
|
+
: '';
|
|
853
848
|
|
|
854
|
-
console.log(` ${brand.gold(draft.draftId)} ${status} ${chalk.dim(
|
|
849
|
+
console.log(` ${brand.gold(draft.draftId)} ${status} ${chalk.dim(typeLabel)} ${chalk.dim(date)}${assetLabel}${engagement}`);
|
|
855
850
|
console.log(` ${brand.cream(preview)}`);
|
|
856
851
|
console.log('');
|
|
857
852
|
}
|
|
@@ -889,13 +884,16 @@ export async function mediaDraftStatusCommand(id) {
|
|
|
889
884
|
const result = await getMediaDraftStatus(id);
|
|
890
885
|
spinner.stop();
|
|
891
886
|
|
|
892
|
-
const
|
|
887
|
+
const typeLabel = resolveContentTypeLabel(result);
|
|
893
888
|
const status = statusBadge(result.status);
|
|
894
889
|
|
|
895
|
-
console.log(`\n ${chalk.bold('
|
|
890
|
+
console.log(`\n ${chalk.bold('Reel Status')}\n`);
|
|
896
891
|
console.log(` ${brand.gold('Draft ID:')} ${result.draftId}`);
|
|
897
892
|
console.log(` ${brand.gold('Status:')} ${status}`);
|
|
898
|
-
console.log(` ${brand.gold('
|
|
893
|
+
console.log(` ${brand.gold('Type:')} ${typeLabel}`);
|
|
894
|
+
if (result.visibility) {
|
|
895
|
+
console.log(` ${brand.gold('Visibility:')} ${result.visibility}`);
|
|
896
|
+
}
|
|
899
897
|
|
|
900
898
|
if (result.title) {
|
|
901
899
|
console.log(` ${brand.gold('Title:')} ${result.title}`);
|
|
@@ -909,8 +907,8 @@ export async function mediaDraftStatusCommand(id) {
|
|
|
909
907
|
if (result.publishedAt) {
|
|
910
908
|
console.log(` ${brand.gold('Published:')} ${new Date(result.publishedAt).toLocaleString()}`);
|
|
911
909
|
}
|
|
912
|
-
if (result.
|
|
913
|
-
console.log(` ${brand.gold('
|
|
910
|
+
if (result.likeCount !== undefined) {
|
|
911
|
+
console.log(` ${brand.gold('Engagement:')} ♥ ${result.likeCount} 💬 ${result.commentCount || 0} ↗ ${result.shareCount || 0}`);
|
|
914
912
|
}
|
|
915
913
|
if (result.rejectionReason) {
|
|
916
914
|
console.log(` ${chalk.red('Rejection:')} ${result.rejectionReason}`);
|