beervid-app-cli 0.2.6 → 0.2.7

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 CHANGED
@@ -30,6 +30,14 @@ node dist/cli.mjs --help
30
30
  node dist/cli.mjs config --show
31
31
  ```
32
32
 
33
+ ### 导出类型
34
+
35
+ 构建后包根会导出 CLI 相关类型,可直接在 TypeScript 中引用:
36
+
37
+ ```ts
38
+ import type { PublishCommandOptions, PublishTTFlowCommandOptions } from 'beervid-app-cli'
39
+ ```
40
+
33
41
  ### 在 Claude Code / Codex / Antigravity 中使用(以下说明面向 AI Agent)
34
42
 
35
43
  #### 方式一:通过 `npx skills add` 安装(推荐)
@@ -52,6 +60,9 @@ npx skills add Lupeiwen0/beervid-app-cli
52
60
  ```text
53
61
  skills/beervid-app-cli/
54
62
  SKILL.md
63
+ skill.json
64
+ QUICKSTART.md
65
+ FAQ.md
55
66
  references/
56
67
  docs/
57
68
  example/
@@ -117,6 +128,14 @@ beervid publish-tts-flow --creator-id open_user_abc --file ./video.mp4 --interac
117
128
  beervid publish-tts-flow --creator-id open_user_abc --file ./video.mp4 --product-id prod_123 --product-title "Widget"
118
129
  ```
119
130
 
131
+ ## TT / TTS 账号关联说明
132
+
133
+ - TTS 账号可用于挂车发布和商品查询,但不能直接查询视频数据。
134
+ - 如果同一达人既要走 TTS 挂车发布,又要查询该账号的视频数据,需要分别完成 TTS 和 TT 两种 OAuth 授权。
135
+ - 官方当前没有提供 `uno_id` 这类可直接关联 TT/TTS 账号的稳定字段。
136
+ - 当前推荐在授权后调用 `account/info`,以两边返回的 `username` 作为关联键,在你方系统里建立 TT 和 TTS 的软关联。
137
+ - 真正调用 API 时仍然使用各自的业务主键:TTS 用 `creatorUserOpenId`,TT 用 `businessId`;`username` 只用于你方系统内的关联映射。
138
+
120
139
  详细用法见 [SKILL.md](./skills/beervid-app-cli/SKILL.md)。完整 API 参考见 [references/api-reference.md](./skills/beervid-app-cli/references/api-reference.md)。
121
140
 
122
141
  ## 落地文档
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/cli.mjs CHANGED
@@ -173,6 +173,20 @@ function getRawOptionValues(rawArgs, optionName) {
173
173
  function getRawOptionValue(rawArgs, optionName) {
174
174
  return getRawOptionValues(rawArgs, optionName).at(-1);
175
175
  }
176
+ function parseStrictInteger(value, optionName) {
177
+ if (value === void 0) return void 0;
178
+ const normalized = String(value).trim();
179
+ if (!/^-?\d+$/.test(normalized)) {
180
+ console.error(`\u9519\u8BEF: ${optionName} \u5FC5\u987B\u4E3A\u6574\u6570`);
181
+ process.exit(1);
182
+ }
183
+ const parsed = Number(normalized);
184
+ if (!Number.isSafeInteger(parsed)) {
185
+ console.error(`\u9519\u8BEF: ${optionName} \u8D85\u51FA\u5B89\u5168\u6574\u6570\u8303\u56F4`);
186
+ process.exit(1);
187
+ }
188
+ return parsed;
189
+ }
176
190
 
177
191
  // src/commands/oauth.ts
178
192
  function register(cli2) {
@@ -309,12 +323,21 @@ function register3(cli2) {
309
323
  }
310
324
 
311
325
  // src/commands/publish.ts
312
- var MAX_PRODUCT_TITLE_LENGTH = 29;
326
+ var MAX_PRODUCT_TITLE_LENGTH = 30;
313
327
  var VALID_PUBLISH_TYPES = ["normal", "shoppable"];
328
+ function parseNonNegativeInt(value, optionName) {
329
+ const parsed = parseStrictInteger(value, optionName);
330
+ if (parsed === void 0) return void 0;
331
+ if (parsed < 0) {
332
+ console.error(`\u9519\u8BEF: ${optionName} \u5FC5\u987B\u4E3A\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6574\u6570`);
333
+ process.exit(1);
334
+ }
335
+ return parsed;
336
+ }
314
337
  function register4(cli2) {
315
- cli2.command("publish", "\u53D1\u5E03 TikTok \u89C6\u9891\uFF08\u666E\u901A/\u6302\u8F66\uFF09").option("--type <type>", "\u53D1\u5E03\u7C7B\u578B: normal\uFF08\u9ED8\u8BA4\uFF09\u6216 shoppable").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u666E\u901A\u53D1\u5E03\u5FC5\u586B\uFF09").option("--video-url <url>", "\u4E0A\u4F20\u540E\u7684\u89C6\u9891 URL\uFF08\u666E\u901A\u53D1\u5E03\u5FC5\u586B\uFF09").option("--creator-id <id>", "TTS \u8D26\u53F7 creatorUserOpenId\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09").option("--file-id <id>", "\u4E0A\u4F20\u8FD4\u56DE\u7684 videoFileId\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09").option("--product-id <id>", "\u5546\u54C1 ID\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09").option("--product-title <title>", "\u5546\u54C1\u6807\u9898\uFF0C\u6700\u591A 29 \u5B57\u7B26\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09", {
338
+ cli2.command("publish", "\u53D1\u5E03 TikTok \u89C6\u9891\uFF08\u666E\u901A/\u6302\u8F66\uFF09").option("--type <type>", "\u53D1\u5E03\u7C7B\u578B: normal\uFF08\u9ED8\u8BA4\uFF09\u6216 shoppable").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u666E\u901A\u53D1\u5E03\u5FC5\u586B\uFF09").option("--video-url <url>", "\u4E0A\u4F20\u540E\u7684\u89C6\u9891 URL\uFF08\u666E\u901A\u53D1\u5E03\u5FC5\u586B\uFF09").option("--creator-id <id>", "TTS \u8D26\u53F7 creatorUserOpenId\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09").option("--file-id <id>", "\u4E0A\u4F20\u8FD4\u56DE\u7684 videoFileId\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09").option("--product-id <id>", "\u5546\u54C1 ID\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09").option("--product-title <title>", "\u5546\u54C1\u6807\u9898\uFF0C\u6700\u591A 30 \u5B57\u7B26\uFF08\u6302\u8F66\u53D1\u5E03\u5FC5\u586B\uFF09", {
316
339
  type: [String]
317
- }).option("--caption <text>", "\u89C6\u9891\u63CF\u8FF0/\u6587\u6848\uFF08\u53EF\u9009\uFF09").action(
340
+ }).option("--caption <text>", "\u89C6\u9891\u63CF\u8FF0/\u6587\u6848\uFF08\u53EF\u9009\uFF09").option("--brand-organic", "\u6807\u8BB0\u4E3A\u54C1\u724C\u6709\u673A\u5185\u5BB9\uFF08TT \u666E\u901A\u53D1\u5E03\u53EF\u9009\uFF09").option("--branded-content", "\u6807\u8BB0\u4E3A\u54C1\u724C\u5185\u5BB9\uFF08TT \u666E\u901A\u53D1\u5E03\u53EF\u9009\uFF09").option("--disable-comment", "\u7981\u7528\u8BC4\u8BBA\uFF08TT \u666E\u901A\u53D1\u5E03\u53EF\u9009\uFF09").option("--disable-duet", "\u7981\u7528\u5408\u62CD\uFF08TT \u666E\u901A\u53D1\u5E03\u53EF\u9009\uFF09").option("--disable-stitch", "\u7981\u7528\u62FC\u63A5\uFF08TT \u666E\u901A\u53D1\u5E03\u53EF\u9009\uFF09").option("--thumbnail-offset <n>", "\u7F29\u7565\u56FE\u504F\u79FB\uFF08TT \u666E\u901A\u53D1\u5E03\u53EF\u9009\uFF0C>= 0\uFF09").action(
318
341
  async (options) => {
319
342
  const publishType = (options.type ?? "normal").toLowerCase();
320
343
  const businessId = getRawOptionValue(cli2.rawArgs, "--business-id");
@@ -374,13 +397,34 @@ function register4(cli2) {
374
397
  );
375
398
  process.exit(1);
376
399
  }
400
+ const publishOptions = {
401
+ caption: options.caption
402
+ };
403
+ if (options.brandOrganic) publishOptions.isBrandOrganic = true;
404
+ if (options.brandedContent) publishOptions.isBrandedContent = true;
405
+ if (options.disableComment) publishOptions.disableComment = true;
406
+ if (options.disableDuet) publishOptions.disableDuet = true;
407
+ if (options.disableStitch) publishOptions.disableStitch = true;
408
+ const thumbnailOffset = parseNonNegativeInt(
409
+ options.thumbnailOffset,
410
+ "--thumbnail-offset"
411
+ );
412
+ if (thumbnailOffset !== void 0) {
413
+ publishOptions.thumbnailOffset = thumbnailOffset;
414
+ }
377
415
  console.log("\u666E\u901A\u89C6\u9891\u53D1\u5E03\u4E2D...");
378
416
  data = await openApiPost(
379
417
  "/api/v1/open/tiktok/video/publish",
380
418
  {
381
419
  businessId,
382
420
  videoUrl: options.videoUrl,
383
- caption: options.caption ?? ""
421
+ caption: publishOptions.caption ?? "",
422
+ ...publishOptions.isBrandOrganic !== void 0 ? { isBrandOrganic: publishOptions.isBrandOrganic } : {},
423
+ ...publishOptions.isBrandedContent !== void 0 ? { isBrandedContent: publishOptions.isBrandedContent } : {},
424
+ ...publishOptions.disableComment !== void 0 ? { disableComment: publishOptions.disableComment } : {},
425
+ ...publishOptions.disableDuet !== void 0 ? { disableDuet: publishOptions.disableDuet } : {},
426
+ ...publishOptions.disableStitch !== void 0 ? { disableStitch: publishOptions.disableStitch } : {},
427
+ ...publishOptions.thumbnailOffset !== void 0 ? { thumbnailOffset: publishOptions.thumbnailOffset } : {}
384
428
  }
385
429
  );
386
430
  console.log("\n\u53D1\u5E03\u5DF2\u63D0\u4EA4\uFF08\u9700\u8F6E\u8BE2\u72B6\u6001\uFF09:");
@@ -481,81 +525,106 @@ function register5(cli2) {
481
525
 
482
526
  // src/commands/query-video.ts
483
527
  function register6(cli2) {
484
- cli2.command("query-video", "\u67E5\u8BE2\u89C6\u9891\u7EDF\u8BA1\u6570\u636E").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u5FC5\u586B\uFF09").option("--item-ids <ids>", "\u89C6\u9891 ID\uFF0C\u652F\u6301\u91CD\u590D\u4F20\u53C2\u6216\u9017\u53F7\u5206\u9694\uFF08\u5FC5\u586B\uFF09").action(
485
- async () => {
486
- const businessId = getRawOptionValue(cli2.rawArgs, "--business-id");
487
- const rawItemIdArgs = getRawOptionValues(cli2.rawArgs, "--item-ids");
488
- if (!businessId || rawItemIdArgs.length === 0) {
489
- const missing = [
490
- !businessId && "--business-id",
491
- rawItemIdArgs.length === 0 && "--item-ids"
492
- ].filter(Boolean);
493
- console.error(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${missing.join(", ")}
528
+ cli2.command("query-video", "\u67E5\u8BE2\u89C6\u9891\u7EDF\u8BA1\u6570\u636E").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u5FC5\u586B\uFF09").option("--item-ids <ids>", "\u89C6\u9891 ID\uFF0C\u652F\u6301\u91CD\u590D\u4F20\u53C2\u6216\u9017\u53F7\u5206\u9694\uFF08\u53EF\u9009\uFF1B\u4E0D\u4F20\u5219\u67E5\u8BE2\u5168\u90E8\uFF09").option("--cursor <n>", "\u5206\u9875\u6E38\u6807\uFF08\u53EF\u9009\uFF09").option("--max-count <n>", "\u6BCF\u9875\u6570\u91CF\uFF08\u53EF\u9009\uFF0C10-20\uFF09").action(async () => {
529
+ const businessId = getRawOptionValue(cli2.rawArgs, "--business-id");
530
+ const rawItemIdArgs = getRawOptionValues(cli2.rawArgs, "--item-ids");
531
+ const rawCursor = getRawOptionValue(cli2.rawArgs, "--cursor");
532
+ const rawMaxCount = getRawOptionValue(cli2.rawArgs, "--max-count");
533
+ if (!businessId) {
534
+ const missing = [!businessId && "--business-id"].filter(Boolean);
535
+ console.error(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${missing.join(", ")}
494
536
  `);
495
- console.error(
496
- "\u7528\u6CD5: beervid query-video --business-id <id> --item-ids <id1,id2,...>"
497
- );
537
+ console.error(
538
+ "\u7528\u6CD5: beervid query-video --business-id <id> [--item-ids <id1,id2,...>] [--cursor <n>] [--max-count <n>]"
539
+ );
540
+ process.exit(1);
541
+ }
542
+ const itemIds = rawItemIdArgs.flatMap((value) => value.split(",")).map((id) => id.trim()).filter(Boolean);
543
+ if (rawItemIdArgs.length > 0 && itemIds.length === 0) {
544
+ console.error("\u9519\u8BEF: --item-ids \u4E0D\u80FD\u4E3A\u7A7A");
545
+ process.exit(1);
546
+ }
547
+ let cursor;
548
+ if (rawCursor !== void 0) {
549
+ cursor = parseStrictInteger(rawCursor, "--cursor");
550
+ if (cursor === void 0 || cursor < 0) {
551
+ console.error("\u9519\u8BEF: --cursor \u5FC5\u987B\u4E3A\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6574\u6570");
498
552
  process.exit(1);
499
553
  }
500
- const itemIds = rawItemIdArgs.flatMap((value) => value.split(",")).map((id) => id.trim()).filter(Boolean);
501
- if (itemIds.length === 0) {
502
- console.error("\u9519\u8BEF: --item-ids \u4E0D\u80FD\u4E3A\u7A7A");
554
+ }
555
+ let maxCount;
556
+ if (rawMaxCount !== void 0) {
557
+ maxCount = parseStrictInteger(rawMaxCount, "--max-count");
558
+ if (maxCount === void 0 || maxCount < 10 || maxCount > 20) {
559
+ console.error("\u9519\u8BEF: --max-count \u5FC5\u987B\u4E3A 10 \u5230 20 \u4E4B\u95F4\u7684\u6574\u6570");
503
560
  process.exit(1);
504
561
  }
505
- try {
506
- console.log(`\u67E5\u8BE2 ${itemIds.length} \u4E2A\u89C6\u9891\u7684\u6570\u636E...
507
- `);
508
- const data = await openApiPost("/api/v1/open/tiktok/video/query", {
509
- businessId,
510
- itemIds
511
- });
512
- const list = data.videoList ?? data.videos ?? [];
513
- if (list.length === 0) {
514
- console.log("\u672A\u67E5\u5230\u89C6\u9891\u6570\u636E");
515
- process.exit(0);
516
- }
517
- const normalized = list.map((v) => ({
518
- itemId: v.itemId ?? v.item_id,
519
- videoViews: v.videoViews ?? v.video_views ?? 0,
520
- likes: v.likes ?? 0,
521
- comments: v.comments ?? 0,
522
- shares: v.shares ?? 0,
523
- thumbnailUrl: v.thumbnailUrl ?? v.thumbnail_url ?? "",
524
- shareUrl: v.shareUrl ?? v.share_url ?? ""
525
- }));
526
- console.log(`\u67E5\u8BE2\u5230 ${normalized.length} \u4E2A\u89C6\u9891:
562
+ }
563
+ try {
564
+ console.log(
565
+ itemIds.length > 0 ? `\u67E5\u8BE2 ${itemIds.length} \u4E2A\u89C6\u9891\u7684\u6570\u636E...
566
+ ` : "\u67E5\u8BE2\u89C6\u9891\u6570\u636E\u5217\u8868...\n"
567
+ );
568
+ const requestBody = {
569
+ businessId
570
+ };
571
+ if (itemIds.length > 0) requestBody.itemIds = itemIds;
572
+ if (cursor !== void 0) requestBody.cursor = cursor;
573
+ if (maxCount !== void 0) requestBody.maxCount = maxCount;
574
+ const data = await openApiPost("/api/v1/open/tiktok/video/query", requestBody);
575
+ const list = data.videoList ?? data.videos ?? [];
576
+ if (list.length === 0) {
577
+ console.log("\u672A\u67E5\u5230\u89C6\u9891\u6570\u636E");
578
+ process.exit(0);
579
+ }
580
+ const normalized = list.map((v) => ({
581
+ itemId: v.itemId ?? v.item_id,
582
+ videoViews: v.videoViews ?? v.video_views ?? 0,
583
+ likes: v.likes ?? 0,
584
+ comments: v.comments ?? 0,
585
+ shares: v.shares ?? 0,
586
+ thumbnailUrl: v.thumbnailUrl ?? v.thumbnail_url ?? "",
587
+ shareUrl: v.shareUrl ?? v.share_url ?? ""
588
+ }));
589
+ console.log(`\u67E5\u8BE2\u5230 ${normalized.length} \u4E2A\u89C6\u9891:
527
590
  `);
528
- for (const v of normalized) {
529
- console.log(` \u89C6\u9891 ${v.itemId}`);
530
- console.log(
531
- ` \u64AD\u653E: ${v.videoViews} \u70B9\u8D5E: ${v.likes} \u8BC4\u8BBA: ${v.comments} \u5206\u4EAB: ${v.shares}`
532
- );
533
- if (v.shareUrl) console.log(` \u94FE\u63A5: ${v.shareUrl}`);
534
- console.log("");
535
- }
536
- printResult(data);
537
- } catch (err) {
538
- rethrowIfProcessExit(err);
539
- console.error("\u67E5\u8BE2\u89C6\u9891\u6570\u636E\u5931\u8D25:", err.message);
540
- process.exit(1);
591
+ for (const v of normalized) {
592
+ console.log(` \u89C6\u9891 ${v.itemId}`);
593
+ console.log(
594
+ ` \u64AD\u653E: ${v.videoViews} \u70B9\u8D5E: ${v.likes} \u8BC4\u8BBA: ${v.comments} \u5206\u4EAB: ${v.shares}`
595
+ );
596
+ if (v.shareUrl) console.log(` \u94FE\u63A5: ${v.shareUrl}`);
597
+ console.log("");
541
598
  }
599
+ printResult(data);
600
+ } catch (err) {
601
+ rethrowIfProcessExit(err);
602
+ console.error("\u67E5\u8BE2\u89C6\u9891\u6570\u636E\u5931\u8D25:", err.message);
603
+ process.exit(1);
542
604
  }
543
- );
605
+ });
544
606
  }
545
607
 
546
608
  // src/workflows/index.ts
547
609
  import { createInterface } from "readline/promises";
548
610
  import { stdin as input, stdout as output } from "process";
549
- var MAX_PRODUCT_TITLE_LENGTH2 = 29;
611
+ var MAX_PRODUCT_TITLE_LENGTH2 = 30;
550
612
  function sleep2(ms) {
551
613
  return new Promise((resolve2) => setTimeout(resolve2, ms));
552
614
  }
553
- async function publishNormalVideo(businessId, videoUrl, caption) {
554
- return openApiPost("/api/v1/open/tiktok/video/publish", {
615
+ async function publishNormalVideo(businessId, videoUrl, options = {}) {
616
+ const body = {
555
617
  businessId,
556
618
  videoUrl,
557
- caption: caption ?? ""
558
- });
619
+ caption: options.caption ?? ""
620
+ };
621
+ if (options.isBrandOrganic !== void 0) body.isBrandOrganic = options.isBrandOrganic;
622
+ if (options.isBrandedContent !== void 0) body.isBrandedContent = options.isBrandedContent;
623
+ if (options.disableComment !== void 0) body.disableComment = options.disableComment;
624
+ if (options.disableDuet !== void 0) body.disableDuet = options.disableDuet;
625
+ if (options.disableStitch !== void 0) body.disableStitch = options.disableStitch;
626
+ if (options.thumbnailOffset !== void 0) body.thumbnailOffset = options.thumbnailOffset;
627
+ return openApiPost("/api/v1/open/tiktok/video/publish", body);
559
628
  }
560
629
  async function publishTtsVideo(creatorId, fileId, productId, productTitle, caption) {
561
630
  const normalizedTitle = productTitle.slice(0, MAX_PRODUCT_TITLE_LENGTH2);
@@ -650,6 +719,7 @@ function encodeCursor(cursor) {
650
719
  return Buffer.from(JSON.stringify(cursor)).toString("base64");
651
720
  }
652
721
  async function queryProductsPage(creatorId, productType, pageSize, cursor) {
722
+ const normalizedPageSize = Math.min(Math.max(pageSize, 1), 20);
653
723
  const allTypesToQuery = productType === "all" ? ["shop", "showcase"] : [productType];
654
724
  const typesToQuery = allTypesToQuery.filter((type) => {
655
725
  const token = type === "shop" ? cursor.shopToken : cursor.showcaseToken;
@@ -667,7 +737,7 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
667
737
  const data = await openApiPost("/api/v1/open/tts/products/query", {
668
738
  creatorUserOpenId: creatorId,
669
739
  productType: type,
670
- pageSize,
740
+ pageSize: normalizedPageSize,
671
741
  pageToken: pageToken ?? ""
672
742
  });
673
743
  return { type, data };
@@ -719,12 +789,13 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
719
789
  async function fetchProductPool(creatorId, productType, pageSize, maxPages) {
720
790
  const allProducts = /* @__PURE__ */ new Map();
721
791
  const rawGroups = [];
792
+ const normalizedPageSize = Math.min(Math.max(pageSize, 1), 20);
722
793
  let cursor = { shopToken: "", showcaseToken: "" };
723
794
  let nextCursor = null;
724
795
  let pagesScanned = 0;
725
796
  const failedSourcesSet = /* @__PURE__ */ new Set();
726
797
  for (let page = 1; page <= maxPages; page++) {
727
- const pageResult = await queryProductsPage(creatorId, productType, pageSize, cursor);
798
+ const pageResult = await queryProductsPage(creatorId, productType, normalizedPageSize, cursor);
728
799
  if (pageResult.successCount === 0) {
729
800
  throw new Error("\u6240\u6709\u5546\u54C1\u6E90\u90FD\u8BF7\u6C42\u5931\u8D25");
730
801
  }
@@ -749,7 +820,7 @@ async function fetchProductPool(creatorId, productType, pageSize, maxPages) {
749
820
  rawGroups,
750
821
  summary: {
751
822
  productType,
752
- pageSize,
823
+ pageSize: normalizedPageSize,
753
824
  pagesScanned,
754
825
  productCount: allProducts.size,
755
826
  nextCursor,
@@ -810,14 +881,14 @@ function register7(cli2) {
810
881
  process.exit(1);
811
882
  }
812
883
  const productType = (options.productType ?? "all").toLowerCase();
813
- const pageSize = parseInt(options.pageSize ?? "20", 10);
884
+ const pageSize = parseStrictInteger(options.pageSize ?? "20", "--page-size");
814
885
  const cursor = options.cursor ?? "";
815
886
  if (!VALID_PRODUCT_TYPES.includes(productType)) {
816
887
  console.error("\u9519\u8BEF: --product-type \u5FC5\u987B\u4E3A shop\u3001showcase \u6216 all");
817
888
  process.exit(1);
818
889
  }
819
- if (Number.isNaN(pageSize) || pageSize <= 0) {
820
- console.error("\u9519\u8BEF: --page-size \u5FC5\u987B\u4E3A\u5927\u4E8E 0 \u7684\u6574\u6570");
890
+ if (pageSize === void 0 || pageSize <= 0 || pageSize > 20) {
891
+ console.error("\u9519\u8BEF: --page-size \u5FC5\u987B\u4E3A 1 \u5230 20 \u4E4B\u95F4\u7684\u6574\u6570");
821
892
  process.exit(1);
822
893
  }
823
894
  try {
@@ -867,15 +938,24 @@ function register7(cli2) {
867
938
 
868
939
  // src/commands/publish-tt-flow.ts
869
940
  function parsePositiveInt(value, optionName, defaultValue) {
870
- const parsed = parseInt(value ?? `${defaultValue}`, 10);
871
- if (Number.isNaN(parsed) || parsed <= 0) {
941
+ const parsed = parseStrictInteger(value ?? `${defaultValue}`, optionName);
942
+ if (parsed === void 0 || parsed <= 0) {
872
943
  console.error(`\u9519\u8BEF: ${optionName} \u5FC5\u987B\u4E3A\u5927\u4E8E 0 \u7684\u6574\u6570`);
873
944
  process.exit(1);
874
945
  }
875
946
  return parsed;
876
947
  }
948
+ function parseNonNegativeInt2(value, optionName) {
949
+ const parsed = parseStrictInteger(value, optionName);
950
+ if (parsed === void 0) return void 0;
951
+ if (parsed < 0) {
952
+ console.error(`\u9519\u8BEF: ${optionName} \u5FC5\u987B\u4E3A\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6574\u6570`);
953
+ process.exit(1);
954
+ }
955
+ return parsed;
956
+ }
877
957
  function register8(cli2) {
878
- cli2.command("publish-tt-flow", "\u6267\u884C TT \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\uFF1A\u4E0A\u4F20\u3001\u53D1\u5E03\u3001\u8F6E\u8BE2\u3001\u67E5\u8BE2\u6570\u636E").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u5FC5\u586B\uFF09").option("--file <path>", "\u89C6\u9891\u6587\u4EF6\u8DEF\u5F84\u6216 URL\uFF08\u5FC5\u586B\uFF09").option("--caption <text>", "\u89C6\u9891\u63CF\u8FF0/\u6587\u6848\uFF08\u53EF\u9009\uFF09").option("--token <token>", "\u5DF2\u6709\u4E0A\u4F20\u51ED\u8BC1\uFF08\u53EF\u9009\uFF09").option("--interval <sec>", "\u8F6E\u8BE2\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 5\uFF09").option("--max-polls <n>", "\u6700\u5927\u8F6E\u8BE2\u6B21\u6570\uFF08\u9ED8\u8BA4 60\uFF09").option("--query-interval <sec>", "\u89C6\u9891\u6570\u636E\u67E5\u8BE2\u91CD\u8BD5\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 5\uFF09").option("--query-max-attempts <n>", "\u89C6\u9891\u6570\u636E\u67E5\u8BE2\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 3\uFF09").action(
958
+ cli2.command("publish-tt-flow", "\u6267\u884C TT \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\uFF1A\u4E0A\u4F20\u3001\u53D1\u5E03\u3001\u8F6E\u8BE2\u3001\u67E5\u8BE2\u6570\u636E").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u5FC5\u586B\uFF09").option("--file <path>", "\u89C6\u9891\u6587\u4EF6\u8DEF\u5F84\u6216 URL\uFF08\u5FC5\u586B\uFF09").option("--caption <text>", "\u89C6\u9891\u63CF\u8FF0/\u6587\u6848\uFF08\u53EF\u9009\uFF09").option("--token <token>", "\u5DF2\u6709\u4E0A\u4F20\u51ED\u8BC1\uFF08\u53EF\u9009\uFF09").option("--brand-organic", "\u6807\u8BB0\u4E3A\u54C1\u724C\u6709\u673A\u5185\u5BB9\uFF08\u53EF\u9009\uFF09").option("--branded-content", "\u6807\u8BB0\u4E3A\u54C1\u724C\u5185\u5BB9\uFF08\u53EF\u9009\uFF09").option("--disable-comment", "\u7981\u7528\u8BC4\u8BBA\uFF08\u53EF\u9009\uFF09").option("--disable-duet", "\u7981\u7528\u5408\u62CD\uFF08\u53EF\u9009\uFF09").option("--disable-stitch", "\u7981\u7528\u62FC\u63A5\uFF08\u53EF\u9009\uFF09").option("--thumbnail-offset <n>", "\u7F29\u7565\u56FE\u504F\u79FB\uFF08\u53EF\u9009\uFF0C>= 0\uFF09").option("--interval <sec>", "\u8F6E\u8BE2\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 5\uFF09").option("--max-polls <n>", "\u6700\u5927\u8F6E\u8BE2\u6B21\u6570\uFF08\u9ED8\u8BA4 60\uFF09").option("--query-interval <sec>", "\u89C6\u9891\u6570\u636E\u67E5\u8BE2\u91CD\u8BD5\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 5\uFF09").option("--query-max-attempts <n>", "\u89C6\u9891\u6570\u636E\u67E5\u8BE2\u6700\u5927\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 3\uFF09").action(
879
959
  async (options) => {
880
960
  const businessId = getRawOptionValue(cli2.rawArgs, "--business-id");
881
961
  if (!businessId || !options.file) {
@@ -898,12 +978,24 @@ function register8(cli2) {
898
978
  "--query-max-attempts",
899
979
  3
900
980
  );
981
+ const publishOptions = {
982
+ caption: options.caption
983
+ };
984
+ if (options.brandOrganic) publishOptions.isBrandOrganic = true;
985
+ if (options.brandedContent) publishOptions.isBrandedContent = true;
986
+ if (options.disableComment) publishOptions.disableComment = true;
987
+ if (options.disableDuet) publishOptions.disableDuet = true;
988
+ if (options.disableStitch) publishOptions.disableStitch = true;
989
+ const thumbnailOffset = parseNonNegativeInt2(options.thumbnailOffset, "--thumbnail-offset");
990
+ if (thumbnailOffset !== void 0) {
991
+ publishOptions.thumbnailOffset = thumbnailOffset;
992
+ }
901
993
  try {
902
994
  console.log("\u5F00\u59CB\u6267\u884C TT \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B...");
903
995
  console.log("1/4 \u6B63\u5728\u4E0A\u4F20\u89C6\u9891...");
904
996
  const upload = await uploadNormalVideo(options.file, options.token);
905
997
  console.log("2/4 \u6B63\u5728\u53D1\u5E03\u89C6\u9891...");
906
- const publish = await publishNormalVideo(businessId, upload.fileUrl, options.caption);
998
+ const publish = await publishNormalVideo(businessId, upload.fileUrl, publishOptions);
907
999
  console.log("3/4 \u6B63\u5728\u8F6E\u8BE2\u53D1\u5E03\u72B6\u6001...");
908
1000
  const status = await pollNormalVideoStatus(businessId, publish.shareId, intervalSec, maxPolls);
909
1001
  const videoId = status.postIds[0] ?? null;
@@ -1072,7 +1164,7 @@ function register9(cli2) {
1072
1164
  options.caption
1073
1165
  );
1074
1166
  if (publishResult.productTitle !== selectedProduct.title) {
1075
- console.warn("\u5546\u54C1\u6807\u9898\u8D85\u8FC7 29 \u5B57\u7B26\uFF0C\u53D1\u5E03\u65F6\u5DF2\u81EA\u52A8\u622A\u65AD");
1167
+ console.warn("\u5546\u54C1\u6807\u9898\u8D85\u8FC7 30 \u5B57\u7B26\uFF0C\u53D1\u5E03\u65F6\u5DF2\u81EA\u52A8\u622A\u65AD");
1076
1168
  }
1077
1169
  printResult({
1078
1170
  products: queriedProducts,
@@ -1130,7 +1222,7 @@ function register10(cli2) {
1130
1222
 
1131
1223
  // src/cli.ts
1132
1224
  var cli = cac("beervid");
1133
- var cliVersion = true ? "0.2.6" : pkg.version;
1225
+ var cliVersion = true ? "0.2.7" : pkg.version;
1134
1226
  register10(cli);
1135
1227
  register(cli);
1136
1228
  register2(cli);