@socialized.dev/cli 0.1.0 → 0.2.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/README.md +7 -2
- package/dist/bin.d.ts +1 -1
- package/dist/bin.js +283 -197
- package/package.json +5 -13
package/README.md
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
npm install -g @socialized.dev/cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
This installs the `socialized.dev` command:
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
|
|
14
|
+
socialized.dev --help
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## Auth
|
|
@@ -43,6 +43,11 @@ socialized.dev publications create \
|
|
|
43
43
|
`publications create` uploads local video and cover files internally, then creates the
|
|
44
44
|
publication. You can also pass an existing `asset_...` id to `--video` or `--cover`.
|
|
45
45
|
|
|
46
|
+
YouTube targets take `--youtube-tags`, `--youtube-category`, `--youtube-language`,
|
|
47
|
+
`--youtube-audio-language`, `--youtube-privacy`, `--youtube-made-for-kids`,
|
|
48
|
+
`--youtube-synthetic-media` and `--youtube-publish-at` (schedule the release); run
|
|
49
|
+
`socialized.dev publications create --help` for the full list.
|
|
50
|
+
|
|
46
51
|
## Brands
|
|
47
52
|
|
|
48
53
|
If your account has multiple brands, you can switch between them:
|
package/dist/bin.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/bin.js
CHANGED
|
@@ -6,7 +6,7 @@ import { basename, extname, join } from "node:path";
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
8
8
|
//#region package.json
|
|
9
|
-
var version = "0.
|
|
9
|
+
var version = "0.2.0";
|
|
10
10
|
//#endregion
|
|
11
11
|
//#region src/api.ts
|
|
12
12
|
var ApiError = class extends Error {
|
|
@@ -45,7 +45,7 @@ const createApiClient = (config) => {
|
|
|
45
45
|
if (!res.ok) throw new ApiError(res.status, await res.text(), init.method ?? "GET", path);
|
|
46
46
|
return res;
|
|
47
47
|
};
|
|
48
|
-
const json = (path, init) => request(path, init)
|
|
48
|
+
const json = async (path, init) => (await request(path, init)).json();
|
|
49
49
|
return {
|
|
50
50
|
listBrands: () => json("/brands"),
|
|
51
51
|
startAssetUpload: (body) => json("/assets", {
|
|
@@ -139,7 +139,8 @@ const loginCommand = buildCommand({
|
|
|
139
139
|
const piped = flags.key === void 0 && !process.stdin.isTTY ? (await readStdin()).trim() : void 0;
|
|
140
140
|
const key = (flags.key ?? piped)?.trim();
|
|
141
141
|
if (!key) throw new UsageError("Pass an API key with `--key sk_...` or pipe one to stdin.");
|
|
142
|
-
const
|
|
142
|
+
const config = resolveConfig({ apiKey: key });
|
|
143
|
+
const { brands } = await createApiClient(config).listBrands();
|
|
143
144
|
saveConfig({
|
|
144
145
|
apiKey: key,
|
|
145
146
|
brandId: brands.length === 1 ? brands[0]?.id : void 0
|
|
@@ -187,43 +188,45 @@ const resolveBrand = (brands, selector) => {
|
|
|
187
188
|
if (matches.length > 1) throw new UsageError(`Brand name is ambiguous: ${selector}`);
|
|
188
189
|
throw new UsageError(`Brand not found: ${selector}`);
|
|
189
190
|
};
|
|
191
|
+
const listCommand = buildCommand({
|
|
192
|
+
docs: { brief: "List brands available to the API key" },
|
|
193
|
+
parameters: { flags: { json: {
|
|
194
|
+
kind: "boolean",
|
|
195
|
+
brief: "Print JSON output"
|
|
196
|
+
} } },
|
|
197
|
+
async func(flags) {
|
|
198
|
+
requireAuth(this);
|
|
199
|
+
const { brands } = await this.api.listBrands();
|
|
200
|
+
if (flags.json) return jsonOut(this, { brands });
|
|
201
|
+
if (brands.length === 0) {
|
|
202
|
+
out(this, "No brands found. Go to the web dashboard to create one.");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
for (const brand of brands) out(this, `${brand.id}\t${brand.name}\t${brand.connectedPlatforms.join(",") || "-"}`);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
const useCommand = buildCommand({
|
|
209
|
+
docs: { brief: "Persist the default brand for publication commands" },
|
|
210
|
+
parameters: { positional: {
|
|
211
|
+
kind: "tuple",
|
|
212
|
+
parameters: [{
|
|
213
|
+
brief: "Brand id or exact brand name",
|
|
214
|
+
parse: String
|
|
215
|
+
}]
|
|
216
|
+
} },
|
|
217
|
+
async func(_flags, selector) {
|
|
218
|
+
requireAuth(this);
|
|
219
|
+
const { brands } = await this.api.listBrands();
|
|
220
|
+
if (brands.length === 0) throw new UsageError("No brands found. Go to the web dashboard to create one.");
|
|
221
|
+
const brand = resolveBrand(brands, selector);
|
|
222
|
+
saveConfig({ brandId: brand.id });
|
|
223
|
+
out(this, `Using ${brand.name} (${brand.id}).`);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
190
226
|
const brandsRoutes = buildRouteMap({
|
|
191
227
|
routes: {
|
|
192
|
-
list:
|
|
193
|
-
|
|
194
|
-
parameters: { flags: { json: {
|
|
195
|
-
kind: "boolean",
|
|
196
|
-
brief: "Print JSON output"
|
|
197
|
-
} } },
|
|
198
|
-
async func(flags) {
|
|
199
|
-
requireAuth(this);
|
|
200
|
-
const { brands } = await this.api.listBrands();
|
|
201
|
-
if (flags.json) return jsonOut(this, { brands });
|
|
202
|
-
if (brands.length === 0) {
|
|
203
|
-
out(this, "No brands found. Go to the web dashboard to create one.");
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
for (const brand of brands) out(this, `${brand.id}\t${brand.name}\t${brand.connectedPlatforms.join(",") || "-"}`);
|
|
207
|
-
}
|
|
208
|
-
}),
|
|
209
|
-
use: buildCommand({
|
|
210
|
-
docs: { brief: "Persist the default brand for publication commands" },
|
|
211
|
-
parameters: { positional: {
|
|
212
|
-
kind: "tuple",
|
|
213
|
-
parameters: [{
|
|
214
|
-
brief: "Brand id or exact brand name",
|
|
215
|
-
parse: String
|
|
216
|
-
}]
|
|
217
|
-
} },
|
|
218
|
-
async func(_flags, selector) {
|
|
219
|
-
requireAuth(this);
|
|
220
|
-
const { brands } = await this.api.listBrands();
|
|
221
|
-
if (brands.length === 0) throw new UsageError("No brands found. Go to the web dashboard to create one.");
|
|
222
|
-
const brand = resolveBrand(brands, selector);
|
|
223
|
-
saveConfig({ brandId: brand.id });
|
|
224
|
-
out(this, `Using ${brand.name} (${brand.id}).`);
|
|
225
|
-
}
|
|
226
|
-
})
|
|
228
|
+
list: listCommand,
|
|
229
|
+
use: useCommand
|
|
227
230
|
},
|
|
228
231
|
docs: { brief: "Manage default brand selection" }
|
|
229
232
|
});
|
|
@@ -240,6 +243,30 @@ const parseTargets = (value) => {
|
|
|
240
243
|
}
|
|
241
244
|
return targets;
|
|
242
245
|
};
|
|
246
|
+
const parseTags = (value) => value.split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
247
|
+
const ISO_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})?$/;
|
|
248
|
+
/** A date-time the user typed (an offset-less one reads as local time) → the UTC ISO string the API
|
|
249
|
+
* takes. Strict: `Date` alone would silently roll 2026-02-30 over to March 2 — the wrong release day. */
|
|
250
|
+
const parseDateTime = (value) => {
|
|
251
|
+
const fields = ISO_DATE_TIME.exec(value)?.slice(1, 7).map((field) => Number(field ?? "0"));
|
|
252
|
+
const [year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0] = fields ?? [];
|
|
253
|
+
const calendar = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
|
|
254
|
+
const date = new Date(value);
|
|
255
|
+
if (fields === void 0 || calendar.getUTCFullYear() !== year || calendar.getUTCMonth() !== month - 1 || calendar.getUTCDate() !== day || calendar.getUTCHours() !== hour || calendar.getUTCMinutes() !== minute || calendar.getUTCSeconds() !== second || Number.isNaN(date.getTime())) throw new UsageError(`Not a date-time: ${value} (e.g. 2026-10-01T09:00 or 2026-10-01T09:00Z)`);
|
|
256
|
+
return date.toISOString();
|
|
257
|
+
};
|
|
258
|
+
/** The YouTube target's `options` bag from the `--youtube-*` flags. Only set fields are sent, so the
|
|
259
|
+
* API's defaults (public, not made for kids) and its validation stay the single source of truth. */
|
|
260
|
+
const youtubeOptions = (flags) => ({
|
|
261
|
+
...flags.youtubeTags !== void 0 ? { tags: flags.youtubeTags } : {},
|
|
262
|
+
...flags.youtubeCategory !== void 0 ? { categoryId: flags.youtubeCategory } : {},
|
|
263
|
+
...flags.youtubeLanguage !== void 0 ? { defaultLanguage: flags.youtubeLanguage } : {},
|
|
264
|
+
...flags.youtubeAudioLanguage !== void 0 ? { defaultAudioLanguage: flags.youtubeAudioLanguage } : {},
|
|
265
|
+
...flags.youtubePrivacy !== void 0 ? { privacyStatus: flags.youtubePrivacy } : {},
|
|
266
|
+
...flags.youtubeMadeForKids !== void 0 ? { madeForKids: flags.youtubeMadeForKids } : {},
|
|
267
|
+
...flags.youtubePublishAt !== void 0 ? { publishAt: flags.youtubePublishAt } : {},
|
|
268
|
+
...flags.youtubeSyntheticMedia !== void 0 ? { containsSyntheticMedia: flags.youtubeSyntheticMedia } : {}
|
|
269
|
+
});
|
|
243
270
|
const resolveBrandId = async (ctx, flagBrand) => {
|
|
244
271
|
let brandId = flagBrand ?? ctx.config.brandId;
|
|
245
272
|
if (!brandId) {
|
|
@@ -256,12 +283,13 @@ const resolveBrandId = async (ctx, flagBrand) => {
|
|
|
256
283
|
const uploadAsset = async (ctx, filePath, kind, jsonMode) => {
|
|
257
284
|
const ext = extname(filePath).toLowerCase();
|
|
258
285
|
let contentType = "application/octet-stream";
|
|
259
|
-
if (kind === "video")
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
286
|
+
if (kind === "video") {
|
|
287
|
+
if (ext === ".mp4") contentType = "video/mp4";
|
|
288
|
+
else if (ext === ".mov") contentType = "video/quicktime";
|
|
289
|
+
else if (ext === ".m4v") contentType = "video/mp4";
|
|
290
|
+
else if (ext === ".webm") contentType = "video/webm";
|
|
291
|
+
else contentType = "video/mp4";
|
|
292
|
+
} else if (ext === ".jpg" || ext === ".jpeg") contentType = "image/jpeg";
|
|
265
293
|
else if (ext === ".png") contentType = "image/png";
|
|
266
294
|
else if (ext === ".webp") contentType = "image/webp";
|
|
267
295
|
else contentType = "application/octet-stream";
|
|
@@ -338,161 +366,218 @@ const printPublicationSummary = (ctx, pub) => {
|
|
|
338
366
|
out(ctx, "Targets:");
|
|
339
367
|
for (const target of pub.targets) out(ctx, ` - ${target.platform}: ${target.status}${target.externalUrl ? ` (${target.externalUrl})` : ""}${target.error ? ` - Error: ${target.error}` : ""}`);
|
|
340
368
|
};
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
369
|
+
const createCommand = buildCommand({
|
|
370
|
+
docs: { brief: "Create a publication, uploading local video/cover files as needed" },
|
|
371
|
+
parameters: { flags: {
|
|
372
|
+
video: {
|
|
373
|
+
kind: "parsed",
|
|
374
|
+
parse: String,
|
|
375
|
+
brief: "Video file path or asset id"
|
|
376
|
+
},
|
|
377
|
+
cover: {
|
|
378
|
+
kind: "parsed",
|
|
379
|
+
parse: String,
|
|
380
|
+
optional: true,
|
|
381
|
+
brief: "Cover image path or asset id"
|
|
382
|
+
},
|
|
383
|
+
to: {
|
|
384
|
+
kind: "parsed",
|
|
385
|
+
parse: parseTargets,
|
|
386
|
+
brief: "Comma-separated platforms"
|
|
387
|
+
},
|
|
388
|
+
title: {
|
|
389
|
+
kind: "parsed",
|
|
390
|
+
parse: String,
|
|
391
|
+
brief: "Publication title"
|
|
392
|
+
},
|
|
393
|
+
description: {
|
|
394
|
+
kind: "parsed",
|
|
395
|
+
parse: String,
|
|
396
|
+
optional: true,
|
|
397
|
+
brief: "Publication description"
|
|
398
|
+
},
|
|
399
|
+
youtubeTags: {
|
|
400
|
+
kind: "parsed",
|
|
401
|
+
parse: parseTags,
|
|
402
|
+
optional: true,
|
|
403
|
+
brief: "YouTube: comma-separated tags (500 characters total)"
|
|
404
|
+
},
|
|
405
|
+
youtubeCategory: {
|
|
406
|
+
kind: "parsed",
|
|
407
|
+
parse: String,
|
|
408
|
+
optional: true,
|
|
409
|
+
brief: "YouTube: numeric category id, e.g. 27 (Education)"
|
|
410
|
+
},
|
|
411
|
+
youtubeLanguage: {
|
|
412
|
+
kind: "parsed",
|
|
413
|
+
parse: String,
|
|
414
|
+
optional: true,
|
|
415
|
+
brief: "YouTube: language of the title and description, e.g. en-GB"
|
|
416
|
+
},
|
|
417
|
+
youtubeAudioLanguage: {
|
|
418
|
+
kind: "parsed",
|
|
419
|
+
parse: String,
|
|
420
|
+
optional: true,
|
|
421
|
+
brief: "YouTube: language spoken in the video, e.g. en"
|
|
422
|
+
},
|
|
423
|
+
youtubePrivacy: {
|
|
424
|
+
kind: "enum",
|
|
425
|
+
values: [
|
|
426
|
+
"public",
|
|
427
|
+
"unlisted",
|
|
428
|
+
"private"
|
|
429
|
+
],
|
|
430
|
+
optional: true,
|
|
431
|
+
brief: "YouTube: privacy status (default public)"
|
|
432
|
+
},
|
|
433
|
+
youtubeMadeForKids: {
|
|
434
|
+
kind: "boolean",
|
|
435
|
+
optional: true,
|
|
436
|
+
brief: "YouTube: declare the video made for kids"
|
|
437
|
+
},
|
|
438
|
+
youtubePublishAt: {
|
|
439
|
+
kind: "parsed",
|
|
440
|
+
parse: parseDateTime,
|
|
441
|
+
optional: true,
|
|
442
|
+
brief: "YouTube: upload now as private, go public at this time (local unless offset given)"
|
|
443
|
+
},
|
|
444
|
+
youtubeSyntheticMedia: {
|
|
445
|
+
kind: "boolean",
|
|
446
|
+
optional: true,
|
|
447
|
+
brief: "YouTube: disclose realistic altered or synthetic content"
|
|
448
|
+
},
|
|
449
|
+
brand: {
|
|
450
|
+
kind: "parsed",
|
|
451
|
+
parse: String,
|
|
452
|
+
optional: true,
|
|
453
|
+
brief: "Brand id"
|
|
454
|
+
},
|
|
455
|
+
idempotencyKey: {
|
|
456
|
+
kind: "parsed",
|
|
457
|
+
parse: String,
|
|
458
|
+
optional: true,
|
|
459
|
+
brief: "Idempotency-Key value"
|
|
460
|
+
},
|
|
461
|
+
watch: {
|
|
462
|
+
kind: "boolean",
|
|
463
|
+
brief: "Poll until final status"
|
|
464
|
+
},
|
|
465
|
+
json: {
|
|
466
|
+
kind: "boolean",
|
|
467
|
+
brief: "Print JSON output"
|
|
468
|
+
}
|
|
469
|
+
} },
|
|
470
|
+
async func(flags) {
|
|
471
|
+
requireAuth(this);
|
|
472
|
+
const youtube = youtubeOptions(flags);
|
|
473
|
+
if (Object.keys(youtube).length > 0 && !flags.to.includes("youtube")) throw new UsageError("`--youtube-*` flags need `youtube` in `--to`.");
|
|
474
|
+
const brandId = await resolveBrandId(this, flags.brand);
|
|
475
|
+
let videoAssetId;
|
|
476
|
+
if (flags.video.startsWith("asset_")) videoAssetId = flags.video;
|
|
477
|
+
else videoAssetId = await uploadAsset(this, flags.video, "video", flags.json);
|
|
478
|
+
let coverAssetId = null;
|
|
479
|
+
if (flags.cover) {
|
|
480
|
+
if (flags.cover.startsWith("asset_")) coverAssetId = flags.cover;
|
|
481
|
+
else coverAssetId = await uploadAsset(this, flags.cover, "cover", flags.json);
|
|
482
|
+
}
|
|
483
|
+
const idempotencyKey = flags.idempotencyKey ?? randomUUID();
|
|
484
|
+
const result = await this.api.createPublication(brandId, {
|
|
485
|
+
videoAssetId,
|
|
486
|
+
coverAssetId,
|
|
487
|
+
title: flags.title,
|
|
488
|
+
description: flags.description ?? "",
|
|
489
|
+
targets: flags.to.map((platform) => ({
|
|
490
|
+
platform,
|
|
491
|
+
options: platform === "youtube" ? youtube : {}
|
|
492
|
+
}))
|
|
493
|
+
}, idempotencyKey);
|
|
494
|
+
if (flags.watch) {
|
|
495
|
+
const finalResult = await pollPublication(this, brandId, result.publicationId, flags.json);
|
|
496
|
+
if (flags.json) return jsonOut(this, finalResult);
|
|
497
|
+
printPublicationSummary(this, finalResult.publication);
|
|
498
|
+
} else {
|
|
499
|
+
if (flags.json) return jsonOut(this, result);
|
|
500
|
+
printDispatchResult(this, result);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
const getCommand = buildCommand({
|
|
505
|
+
docs: { brief: "Read a publication" },
|
|
506
|
+
parameters: {
|
|
507
|
+
flags: {
|
|
508
|
+
brand: {
|
|
509
|
+
kind: "parsed",
|
|
510
|
+
parse: String,
|
|
511
|
+
optional: true,
|
|
512
|
+
brief: "Brand id"
|
|
446
513
|
},
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const result = await this.api.getPublication(brandId, id);
|
|
451
|
-
if (flags.json) return jsonOut(this, result);
|
|
452
|
-
printPublicationSummary(this, result.publication);
|
|
514
|
+
json: {
|
|
515
|
+
kind: "boolean",
|
|
516
|
+
brief: "Print JSON output"
|
|
453
517
|
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
parameters: {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}
|
|
518
|
+
},
|
|
519
|
+
positional: {
|
|
520
|
+
kind: "tuple",
|
|
521
|
+
parameters: [{
|
|
522
|
+
brief: "Publication id",
|
|
523
|
+
parse: String
|
|
524
|
+
}]
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
async func(flags, id) {
|
|
528
|
+
requireAuth(this);
|
|
529
|
+
const brandId = await resolveBrandId(this, flags.brand);
|
|
530
|
+
const result = await this.api.getPublication(brandId, id);
|
|
531
|
+
if (flags.json) return jsonOut(this, result);
|
|
532
|
+
printPublicationSummary(this, result.publication);
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
const statusCommand = buildCommand({
|
|
536
|
+
docs: { brief: "Show publication status" },
|
|
537
|
+
parameters: {
|
|
538
|
+
flags: {
|
|
539
|
+
brand: {
|
|
540
|
+
kind: "parsed",
|
|
541
|
+
parse: String,
|
|
542
|
+
optional: true,
|
|
543
|
+
brief: "Brand id"
|
|
481
544
|
},
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
} else {
|
|
490
|
-
const result = await this.api.getPublication(brandId, id);
|
|
491
|
-
if (flags.json) return jsonOut(this, result);
|
|
492
|
-
printPublicationSummary(this, result.publication);
|
|
493
|
-
}
|
|
545
|
+
watch: {
|
|
546
|
+
kind: "boolean",
|
|
547
|
+
brief: "Poll until final status"
|
|
548
|
+
},
|
|
549
|
+
json: {
|
|
550
|
+
kind: "boolean",
|
|
551
|
+
brief: "Print JSON output"
|
|
494
552
|
}
|
|
495
|
-
}
|
|
553
|
+
},
|
|
554
|
+
positional: {
|
|
555
|
+
kind: "tuple",
|
|
556
|
+
parameters: [{
|
|
557
|
+
brief: "Publication id",
|
|
558
|
+
parse: String
|
|
559
|
+
}]
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
async func(flags, id) {
|
|
563
|
+
requireAuth(this);
|
|
564
|
+
const brandId = await resolveBrandId(this, flags.brand);
|
|
565
|
+
if (flags.watch) {
|
|
566
|
+
const result = await pollPublication(this, brandId, id, flags.json);
|
|
567
|
+
if (flags.json) return jsonOut(this, result);
|
|
568
|
+
printPublicationSummary(this, result.publication);
|
|
569
|
+
} else {
|
|
570
|
+
const result = await this.api.getPublication(brandId, id);
|
|
571
|
+
if (flags.json) return jsonOut(this, result);
|
|
572
|
+
printPublicationSummary(this, result.publication);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
const publicationsRoutes = buildRouteMap({
|
|
577
|
+
routes: {
|
|
578
|
+
create: createCommand,
|
|
579
|
+
get: getCommand,
|
|
580
|
+
status: statusCommand
|
|
496
581
|
},
|
|
497
582
|
docs: { brief: "Create and inspect publications" }
|
|
498
583
|
});
|
|
@@ -504,7 +589,7 @@ const text = {
|
|
|
504
589
|
exceptionWhileRunningCommand: cleanError,
|
|
505
590
|
commandErrorResult: cleanError
|
|
506
591
|
};
|
|
507
|
-
const
|
|
592
|
+
const root = buildRouteMap({
|
|
508
593
|
routes: {
|
|
509
594
|
login: loginCommand,
|
|
510
595
|
logout: logoutCommand,
|
|
@@ -513,7 +598,8 @@ const app = buildApplication(buildRouteMap({
|
|
|
513
598
|
publications: publicationsRoutes
|
|
514
599
|
},
|
|
515
600
|
docs: { brief: "socialized.dev — publish videos from the terminal" }
|
|
516
|
-
})
|
|
601
|
+
});
|
|
602
|
+
const app = buildApplication(root, {
|
|
517
603
|
name: "socialized.dev",
|
|
518
604
|
versionInfo: { currentVersion: version },
|
|
519
605
|
scanner: { caseStyle: "allow-kebab-for-camel" },
|
package/package.json
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socialized.dev/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "socialized.dev CLI — publish a video once, everywhere, from the terminal.",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/fishballapp/fishballapps.git",
|
|
11
|
-
"directory": "projects/socialized.dev/apps/cli"
|
|
12
|
-
},
|
|
13
8
|
"homepage": "https://socialized.dev",
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/fishballapp/fishballapps/issues"
|
|
16
|
-
},
|
|
17
9
|
"bin": {
|
|
18
10
|
"socialized.dev": "./dist/bin.js"
|
|
19
11
|
},
|
|
@@ -25,12 +17,12 @@
|
|
|
25
17
|
"access": "public"
|
|
26
18
|
},
|
|
27
19
|
"dependencies": {
|
|
28
|
-
"@stricli/core": "^1.
|
|
20
|
+
"@stricli/core": "^1.3.0"
|
|
29
21
|
},
|
|
30
22
|
"devDependencies": {
|
|
31
|
-
"@types/node": "^24.
|
|
32
|
-
"tsdown": "^0.
|
|
33
|
-
"vitest": "^4.1.
|
|
23
|
+
"@types/node": "^24.13.4",
|
|
24
|
+
"tsdown": "^0.23.0",
|
|
25
|
+
"vitest": "^4.1.8"
|
|
34
26
|
},
|
|
35
27
|
"scripts": {
|
|
36
28
|
"build": "tsdown",
|