beervid-app-cli 0.2.0 → 0.2.2

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/dist/cli.mjs CHANGED
@@ -371,12 +371,11 @@ function register4(cli2) {
371
371
  }
372
372
 
373
373
  // src/commands/poll-status.ts
374
- var TERMINAL_STATUSES = ["PUBLISH_COMPLETE", "FAILED"];
375
374
  function sleep(ms) {
376
375
  return new Promise((resolve2) => setTimeout(resolve2, ms));
377
376
  }
378
377
  function register5(cli2) {
379
- cli2.command("poll-status", "\u8F6E\u8BE2\u666E\u901A\u89C6\u9891\u53D1\u5E03\u72B6\u6001").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u5FC5\u586B\uFF09").option("--share-id <id>", "\u53D1\u5E03\u65F6\u8FD4\u56DE\u7684 shareId\uFF08\u5FC5\u586B\uFF09").option("--interval <sec>", "\u8F6E\u8BE2\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 3\uFF09").option("--max-polls <n>", "\u6700\u5927\u8F6E\u8BE2\u6B21\u6570\uFF08\u9ED8\u8BA4 60\uFF09").action(
378
+ cli2.command("poll-status", "\u8F6E\u8BE2\u666E\u901A\u89C6\u9891\u53D1\u5E03\u72B6\u6001").option("--business-id <id>", "TT \u8D26\u53F7 businessId\uFF08\u5FC5\u586B\uFF09").option("--share-id <id>", "\u53D1\u5E03\u65F6\u8FD4\u56DE\u7684 shareId\uFF08\u5FC5\u586B\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").action(
380
379
  async (options) => {
381
380
  if (!options.businessId || !options.shareId) {
382
381
  const missing = [
@@ -388,7 +387,7 @@ function register5(cli2) {
388
387
  console.error("\u7528\u6CD5: beervid poll-status --business-id <id> --share-id <id>");
389
388
  process.exit(1);
390
389
  }
391
- const intervalSec = parseInt(options.interval ?? "3", 10);
390
+ const intervalSec = parseInt(options.interval ?? "5", 10);
392
391
  const maxPolls = parseInt(options.maxPolls ?? "60", 10);
393
392
  if (Number.isNaN(intervalSec) || intervalSec <= 0) {
394
393
  console.error("\u9519\u8BEF: --interval \u5FC5\u987B\u4E3A\u5927\u4E8E 0 \u7684\u6574\u6570");
@@ -403,35 +402,43 @@ function register5(cli2) {
403
402
  console.log(`businessId: ${options.businessId}`);
404
403
  console.log(`shareId: ${options.shareId}
405
404
  `);
405
+ let lastStatus = "UNKNOWN";
406
406
  for (let i = 1; i <= maxPolls; i++) {
407
407
  const data = await openApiPost("/api/v1/open/tiktok/video/status", {
408
408
  businessId: options.businessId,
409
409
  shareId: options.shareId
410
410
  });
411
411
  const status = data.status ?? data.Status ?? "UNKNOWN";
412
+ const postIds = data.post_ids ?? [];
413
+ lastStatus = status;
412
414
  console.log(`[${i}/${maxPolls}] \u72B6\u6001: ${status}`);
413
- if (TERMINAL_STATUSES.includes(status)) {
415
+ if (status === "FAILED") {
414
416
  console.log("");
415
- if (status === "PUBLISH_COMPLETE") {
416
- console.log("\u53D1\u5E03\u6210\u529F!");
417
- if (data.post_ids && data.post_ids.length > 0) {
418
- console.log(`\u89C6\u9891 ID: ${data.post_ids[0]}`);
419
- console.log(
420
- `\u63D0\u793A: \u4F7F\u7528 beervid query-video --business-id ${options.businessId} --item-ids ${data.post_ids[0]} \u67E5\u8BE2\u6570\u636E`
421
- );
422
- }
423
- } else {
424
- console.log(`\u53D1\u5E03\u5931\u8D25: ${data.reason ?? "\u672A\u77E5\u539F\u56E0"}`);
425
- }
417
+ console.log(`\u53D1\u5E03\u5931\u8D25: ${data.reason ?? "\u672A\u77E5\u539F\u56E0"}`);
426
418
  printResult(data);
427
- process.exit(status === "PUBLISH_COMPLETE" ? 0 : 1);
419
+ process.exit(1);
420
+ }
421
+ if (status === "PUBLISH_COMPLETE" && postIds.length > 0) {
422
+ console.log("");
423
+ console.log("\u53D1\u5E03\u6210\u529F!");
424
+ console.log(`\u89C6\u9891 ID: ${postIds[0]}`);
425
+ console.log(
426
+ `\u63D0\u793A: \u4F7F\u7528 beervid query-video --business-id ${options.businessId} --item-ids ${postIds[0]} \u67E5\u8BE2\u6570\u636E`
427
+ );
428
+ printResult(data);
429
+ process.exit(0);
428
430
  }
429
431
  if (i < maxPolls) {
430
432
  await sleep(intervalSec * 1e3);
431
433
  }
432
434
  }
433
- console.error(`
434
- \u8D85\u8FC7\u6700\u5927\u8F6E\u8BE2\u6B21\u6570 (${maxPolls})\uFF0C\u72B6\u6001\u4ECD\u672A\u7EC8\u7ED3`);
435
+ if (lastStatus === "PUBLISH_COMPLETE") {
436
+ console.error(`
437
+ \u8D85\u8FC7\u6700\u5927\u8F6E\u8BE2\u6B21\u6570 (${maxPolls})\uFF0C\u72B6\u6001\u4E3A PUBLISH_COMPLETE \u4F46 post_ids \u4ECD\u4E3A\u7A7A`);
438
+ } else {
439
+ console.error(`
440
+ \u8D85\u8FC7\u6700\u5927\u8F6E\u8BE2\u6B21\u6570 (${maxPolls})\uFF0C\u4ECD\u672A\u62FF\u5230 post_ids`);
441
+ }
435
442
  process.exit(2);
436
443
  } catch (err) {
437
444
  rethrowIfProcessExit(err);
@@ -504,139 +511,10 @@ function register6(cli2) {
504
511
  );
505
512
  }
506
513
 
507
- // src/commands/query-products.ts
508
- var VALID_PRODUCT_TYPES = ["shop", "showcase", "all"];
509
- function extractImageUrl(imageStr) {
510
- const match = imageStr.match(/url=([^,}]+)/);
511
- return match?.[1]?.trim() ?? imageStr;
512
- }
513
- async function queryProducts(creatorId, type, pageSize, pageToken) {
514
- return openApiPost("/api/v1/open/tts/products/query", {
515
- creatorUserOpenId: creatorId,
516
- productType: type,
517
- pageSize,
518
- pageToken
519
- });
520
- }
521
- function register7(cli2) {
522
- cli2.command("query-products", "\u67E5\u8BE2 TTS \u5546\u54C1\u5217\u8868").option("--creator-id <id>", "TTS \u8D26\u53F7 creatorUserOpenId\uFF08\u5FC5\u586B\uFF09").option("--product-type <type>", "\u5546\u54C1\u6765\u6E90: shop / showcase / all\uFF08\u9ED8\u8BA4 all\uFF09").option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF\uFF08\u9ED8\u8BA4 20\uFF09").option("--cursor <cursor>", "\u5206\u9875\u6E38\u6807\uFF08\u9996\u9875\u4E0D\u4F20\uFF09").action(
523
- async (options) => {
524
- if (!options.creatorId) {
525
- console.error("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: --creator-id\n");
526
- console.error("\u7528\u6CD5: beervid query-products --creator-id <id>");
527
- process.exit(1);
528
- }
529
- const creatorId = options.creatorId;
530
- const productType = (options.productType ?? "all").toLowerCase();
531
- const pageSize = parseInt(options.pageSize ?? "20", 10);
532
- const cursor = options.cursor ?? "";
533
- if (!VALID_PRODUCT_TYPES.includes(productType)) {
534
- console.error("\u9519\u8BEF: --product-type \u5FC5\u987B\u4E3A shop\u3001showcase \u6216 all");
535
- process.exit(1);
536
- }
537
- if (Number.isNaN(pageSize) || pageSize <= 0) {
538
- console.error("\u9519\u8BEF: --page-size \u5FC5\u987B\u4E3A\u5927\u4E8E 0 \u7684\u6574\u6570");
539
- process.exit(1);
540
- }
541
- try {
542
- let shopToken = "";
543
- let showcaseToken = "";
544
- if (cursor) {
545
- try {
546
- const decoded = JSON.parse(
547
- Buffer.from(cursor, "base64").toString()
548
- );
549
- shopToken = decoded.shopToken ?? "";
550
- showcaseToken = decoded.showcaseToken ?? "";
551
- } catch {
552
- console.error("\u9519\u8BEF: \u65E0\u6548\u7684 cursor \u683C\u5F0F");
553
- process.exit(1);
554
- }
555
- }
556
- const typesToQuery = productType === "all" ? ["shop", "showcase"] : [productType];
557
- const allProducts = /* @__PURE__ */ new Map();
558
- let nextShopToken = null;
559
- let nextShowcaseToken = null;
560
- let successCount = 0;
561
- const results = await Promise.allSettled(
562
- typesToQuery.map(async (type) => {
563
- const token = type === "shop" ? shopToken : showcaseToken;
564
- const data = await queryProducts(creatorId, type, pageSize, token);
565
- return { type, data };
566
- })
567
- );
568
- for (const result of results) {
569
- if (result.status === "rejected") {
570
- console.error("\u67E5\u8BE2\u5931\u8D25:", result.reason?.message);
571
- continue;
572
- }
573
- const { type, data } = result.value;
574
- successCount += 1;
575
- const items = Array.isArray(data) ? data : [data];
576
- for (const group of items) {
577
- if (type === "shop") nextShopToken = group.nextPageToken ?? null;
578
- if (type === "showcase") nextShowcaseToken = group.nextPageToken ?? null;
579
- for (const product of group.products ?? []) {
580
- if (!allProducts.has(product.id)) {
581
- allProducts.set(product.id, {
582
- id: product.id,
583
- title: product.title,
584
- price: product.price,
585
- images: (product.images ?? []).map(extractImageUrl),
586
- salesCount: product.salesCount ?? 0,
587
- brandName: product.brandName ?? "",
588
- shopName: product.shopName ?? "",
589
- source: product.source ?? type,
590
- reviewStatus: product.reviewStatus,
591
- inventoryStatus: product.inventoryStatus
592
- });
593
- }
594
- }
595
- }
596
- }
597
- if (successCount === 0) {
598
- console.error("\u67E5\u8BE2\u5546\u54C1\u5931\u8D25: \u6240\u6709\u5546\u54C1\u6E90\u90FD\u8BF7\u6C42\u5931\u8D25");
599
- process.exit(1);
600
- }
601
- const productList = Array.from(allProducts.values());
602
- let nextCursor = null;
603
- if (nextShopToken || nextShowcaseToken) {
604
- nextCursor = Buffer.from(
605
- JSON.stringify({
606
- shopToken: nextShopToken ?? "",
607
- showcaseToken: nextShowcaseToken ?? ""
608
- })
609
- ).toString("base64");
610
- }
611
- console.log(`\u67E5\u8BE2\u5230 ${productList.length} \u4E2A\u5546\u54C1:
612
- `);
613
- for (const p of productList) {
614
- console.log(` [${p.source}] ${p.title}`);
615
- console.log(` ID: ${p.id} \u9500\u91CF: ${p.salesCount} \u54C1\u724C: ${p.brandName}`);
616
- if (p.images.length > 0) console.log(` \u56FE\u7247: ${p.images[0]}`);
617
- console.log("");
618
- }
619
- if (nextCursor) {
620
- console.log(`\u4E0B\u4E00\u9875\u6E38\u6807: ${nextCursor}`);
621
- console.log(`\u4F7F\u7528: beervid query-products --creator-id ${creatorId} --cursor ${nextCursor}`);
622
- } else {
623
- console.log("\u5DF2\u5230\u6700\u540E\u4E00\u9875");
624
- }
625
- printResult({ products: productList, nextCursor });
626
- } catch (err) {
627
- rethrowIfProcessExit(err);
628
- console.error("\u67E5\u8BE2\u5546\u54C1\u5931\u8D25:", err.message);
629
- process.exit(1);
630
- }
631
- }
632
- );
633
- }
634
-
635
514
  // src/workflows/index.ts
636
515
  import { createInterface } from "readline/promises";
637
516
  import { stdin as input, stdout as output } from "process";
638
517
  var MAX_PRODUCT_TITLE_LENGTH2 = 29;
639
- var TERMINAL_STATUSES2 = ["PUBLISH_COMPLETE", "FAILED"];
640
518
  function sleep2(ms) {
641
519
  return new Promise((resolve2) => setTimeout(resolve2, ms));
642
520
  }
@@ -662,18 +540,32 @@ async function publishTtsVideo(creatorId, fileId, productId, productTitle, capti
662
540
  return { publish, productTitle: normalizedTitle };
663
541
  }
664
542
  async function pollNormalVideoStatus(businessId, shareId, intervalSec, maxPolls) {
543
+ let lastData = null;
544
+ let lastStatus = "UNKNOWN";
665
545
  for (let i = 1; i <= maxPolls; i++) {
666
546
  const data = await openApiPost("/api/v1/open/tiktok/video/status", {
667
547
  businessId,
668
548
  shareId
669
549
  });
550
+ lastData = data;
670
551
  const status = data.status ?? data.Status ?? "UNKNOWN";
671
- if (TERMINAL_STATUSES2.includes(status)) {
552
+ const postIds = data.post_ids ?? [];
553
+ lastStatus = status;
554
+ if (status === "FAILED") {
672
555
  return {
673
556
  pollCount: i,
674
557
  finalStatus: status,
675
558
  reason: data.reason ?? null,
676
- postIds: data.post_ids ?? [],
559
+ postIds,
560
+ raw: data
561
+ };
562
+ }
563
+ if (status === "PUBLISH_COMPLETE" && postIds.length > 0) {
564
+ return {
565
+ pollCount: i,
566
+ finalStatus: status,
567
+ reason: data.reason ?? null,
568
+ postIds,
677
569
  raw: data
678
570
  };
679
571
  }
@@ -684,9 +576,9 @@ async function pollNormalVideoStatus(businessId, shareId, intervalSec, maxPolls)
684
576
  return {
685
577
  pollCount: maxPolls,
686
578
  finalStatus: "TIMEOUT",
687
- reason: `\u8D85\u8FC7\u6700\u5927\u8F6E\u8BE2\u6B21\u6570 (${maxPolls})\uFF0C\u72B6\u6001\u4ECD\u672A\u7EC8\u7ED3`,
579
+ reason: lastStatus === "PUBLISH_COMPLETE" ? `\u8D85\u8FC7\u6700\u5927\u8F6E\u8BE2\u6B21\u6570 (${maxPolls})\uFF0C\u72B6\u6001\u4E3A PUBLISH_COMPLETE \u4F46 post_ids \u4ECD\u4E3A\u7A7A` : `\u8D85\u8FC7\u6700\u5927\u8F6E\u8BE2\u6B21\u6570 (${maxPolls})\uFF0C\u4ECD\u672A\u62FF\u5230 post_ids`,
688
580
  postIds: [],
689
- raw: null
581
+ raw: lastData
690
582
  };
691
583
  }
692
584
  function normalizeVideoQuery(data) {
@@ -728,25 +620,30 @@ async function queryVideoWithRetry(businessId, itemId, intervalSec, maxAttempts)
728
620
  });
729
621
  return { query: null, warnings };
730
622
  }
731
- function extractImageUrl2(imageStr) {
623
+ function extractImageUrl(imageStr) {
732
624
  const match = imageStr.match(/url=([^,}]+)/);
733
625
  return match?.[1]?.trim() ?? imageStr;
734
626
  }
735
627
  function decodeCursor(cursor) {
736
628
  const decoded = JSON.parse(Buffer.from(cursor, "base64").toString());
737
629
  return {
738
- shopToken: decoded.shopToken ?? "",
739
- showcaseToken: decoded.showcaseToken ?? ""
630
+ shopToken: decoded.shopToken === void 0 ? "" : decoded.shopToken,
631
+ showcaseToken: decoded.showcaseToken === void 0 ? "" : decoded.showcaseToken
740
632
  };
741
633
  }
742
634
  function encodeCursor(cursor) {
635
+ if (cursor.shopToken === null && cursor.showcaseToken === null) return null;
743
636
  return Buffer.from(JSON.stringify(cursor)).toString("base64");
744
637
  }
745
638
  async function queryProductsPage(creatorId, productType, pageSize, cursor) {
746
- const typesToQuery = productType === "all" ? ["shop", "showcase"] : [productType];
639
+ const allTypesToQuery = productType === "all" ? ["shop", "showcase"] : [productType];
640
+ const typesToQuery = allTypesToQuery.filter((type) => {
641
+ const token = type === "shop" ? cursor.shopToken : cursor.showcaseToken;
642
+ return token !== null;
643
+ });
747
644
  const allProducts = /* @__PURE__ */ new Map();
748
- let nextShopToken = "";
749
- let nextShowcaseToken = "";
645
+ let nextShopToken = cursor.shopToken;
646
+ let nextShowcaseToken = cursor.showcaseToken;
750
647
  let successCount = 0;
751
648
  const failedSources = [];
752
649
  const results = await Promise.allSettled(
@@ -756,7 +653,7 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
756
653
  creatorUserOpenId: creatorId,
757
654
  productType: type,
758
655
  pageSize,
759
- pageToken
656
+ pageToken: pageToken ?? ""
760
657
  });
761
658
  return { type, data };
762
659
  })
@@ -773,15 +670,16 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
773
670
  const { data } = result.value;
774
671
  const groups = Array.isArray(data) ? data : [data];
775
672
  for (const group of groups) {
776
- if (type === "shop") nextShopToken = group.nextPageToken ?? "";
777
- if (type === "showcase") nextShowcaseToken = group.nextPageToken ?? "";
673
+ const token = group.nextPageToken === void 0 ? null : group.nextPageToken;
674
+ if (type === "shop") nextShopToken = token;
675
+ if (type === "showcase") nextShowcaseToken = token;
778
676
  for (const product of group.products ?? []) {
779
677
  if (!allProducts.has(product.id)) {
780
678
  allProducts.set(product.id, {
781
679
  id: product.id,
782
680
  title: product.title,
783
681
  price: product.price,
784
- images: (product.images ?? []).map(extractImageUrl2),
682
+ images: (product.images ?? []).map(extractImageUrl),
785
683
  salesCount: product.salesCount ?? 0,
786
684
  brandName: product.brandName ?? "",
787
685
  shopName: product.shopName ?? "",
@@ -793,7 +691,7 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
793
691
  }
794
692
  }
795
693
  }
796
- const nextCursor = nextShopToken || nextShowcaseToken ? encodeCursor({ shopToken: nextShopToken, showcaseToken: nextShowcaseToken }) : null;
694
+ const nextCursor = encodeCursor({ shopToken: nextShopToken, showcaseToken: nextShowcaseToken });
797
695
  return {
798
696
  products: Array.from(allProducts.values()),
799
697
  nextCursor,
@@ -840,8 +738,24 @@ async function fetchProductPool(creatorId, productType, pageSize, maxPages) {
840
738
  }
841
739
  };
842
740
  }
741
+ function buildSkippedProductQuerySummary(productType, pageSize) {
742
+ return {
743
+ productType,
744
+ pageSize,
745
+ pagesScanned: 0,
746
+ productCount: 0,
747
+ nextCursor: null,
748
+ reachedPageLimit: false,
749
+ failedSources: []
750
+ };
751
+ }
752
+ function isProductPublishable(product) {
753
+ if (product.reviewStatus && product.reviewStatus.toUpperCase() !== "APPROVED") return false;
754
+ if (product.inventoryStatus && product.inventoryStatus.toUpperCase() !== "IN_STOCK") return false;
755
+ return true;
756
+ }
843
757
  function sortProductsForSelection(products) {
844
- return [...products].sort((a, b) => b.salesCount - a.salesCount);
758
+ return products.filter(isProductPublishable).sort((a, b) => b.salesCount - a.salesCount);
845
759
  }
846
760
  function buildSelectedProductSummary(product, selectionMode) {
847
761
  return {
@@ -859,8 +773,11 @@ async function promptForProductSelection(products) {
859
773
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
860
774
  throw new Error("\u4EA4\u4E92\u6A21\u5F0F\u9700\u8981\u5728 TTY \u7EC8\u7AEF\u4E2D\u8FD0\u884C");
861
775
  }
862
- const candidates = sortProductsForSelection(products).slice(0, 20);
863
- console.log(`\u53EF\u9009\u5546\u54C1\uFF08\u5C55\u793A\u524D ${candidates.length} \u4E2A\uFF0C\u6309\u9500\u91CF\u964D\u5E8F\uFF09\uFF1A`);
776
+ const candidates = products.filter(isProductPublishable).sort((a, b) => b.salesCount - a.salesCount).slice(0, 20);
777
+ if (candidates.length === 0) {
778
+ throw new Error("\u5F53\u524D\u5546\u54C1\u6C60\u4E2D\u6CA1\u6709\u53EF\u53D1\u5E03\u5546\u54C1\uFF08\u5BA1\u6838\u672A\u901A\u8FC7\u6216\u65E0\u5E93\u5B58\uFF09\uFF0C\u5982\u9700\u5F3A\u5236\u6307\u5B9A\u8BF7\u4F7F\u7528 --product-id/--product-title");
779
+ }
780
+ console.log(`\u53EF\u9009\u5546\u54C1\uFF08\u5C55\u793A\u524D ${candidates.length} \u4E2A\uFF0C\u6309\u9500\u91CF\u964D\u5E8F\uFF0C\u5DF2\u8FC7\u6EE4\u4E0D\u53EF\u53D1\u5E03\u5546\u54C1\uFF09\uFF1A`);
864
781
  for (const [index, product] of candidates.entries()) {
865
782
  console.log(
866
783
  `${index + 1}. [${product.source}] ${product.title} | ID: ${product.id} | \u9500\u91CF: ${product.salesCount}`
@@ -884,6 +801,73 @@ async function promptForProductSelection(products) {
884
801
  }
885
802
  }
886
803
 
804
+ // src/commands/query-products.ts
805
+ var VALID_PRODUCT_TYPES = ["shop", "showcase", "all"];
806
+ function register7(cli2) {
807
+ cli2.command("query-products", "\u67E5\u8BE2 TTS \u5546\u54C1\u5217\u8868").option("--creator-id <id>", "TTS \u8D26\u53F7 creatorUserOpenId\uFF08\u5FC5\u586B\uFF09").option("--product-type <type>", "\u5546\u54C1\u6765\u6E90: shop / showcase / all\uFF08\u9ED8\u8BA4 all\uFF09").option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF\uFF08\u9ED8\u8BA4 20\uFF09").option("--cursor <cursor>", "\u5206\u9875\u6E38\u6807\uFF08\u9996\u9875\u4E0D\u4F20\uFF09").action(
808
+ async (options) => {
809
+ if (!options.creatorId) {
810
+ console.error("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: --creator-id\n");
811
+ console.error("\u7528\u6CD5: beervid query-products --creator-id <id>");
812
+ process.exit(1);
813
+ }
814
+ const creatorId = options.creatorId;
815
+ const productType = (options.productType ?? "all").toLowerCase();
816
+ const pageSize = parseInt(options.pageSize ?? "20", 10);
817
+ const cursor = options.cursor ?? "";
818
+ if (!VALID_PRODUCT_TYPES.includes(productType)) {
819
+ console.error("\u9519\u8BEF: --product-type \u5FC5\u987B\u4E3A shop\u3001showcase \u6216 all");
820
+ process.exit(1);
821
+ }
822
+ if (Number.isNaN(pageSize) || pageSize <= 0) {
823
+ console.error("\u9519\u8BEF: --page-size \u5FC5\u987B\u4E3A\u5927\u4E8E 0 \u7684\u6574\u6570");
824
+ process.exit(1);
825
+ }
826
+ try {
827
+ let inputCursor = { shopToken: "", showcaseToken: "" };
828
+ if (cursor) {
829
+ try {
830
+ inputCursor = decodeCursor(cursor);
831
+ } catch {
832
+ console.error("\u9519\u8BEF: \u65E0\u6548\u7684 cursor \u683C\u5F0F");
833
+ process.exit(1);
834
+ }
835
+ }
836
+ const pageResult = await queryProductsPage(
837
+ creatorId,
838
+ productType,
839
+ pageSize,
840
+ inputCursor
841
+ );
842
+ if (pageResult.successCount === 0 && pageResult.failedSources.length > 0) {
843
+ console.error("\u67E5\u8BE2\u5546\u54C1\u5931\u8D25: \u6240\u6709\u5546\u54C1\u6E90\u90FD\u8BF7\u6C42\u5931\u8D25");
844
+ process.exit(1);
845
+ }
846
+ const productList = pageResult.products;
847
+ console.log(`\u67E5\u8BE2\u5230 ${productList.length} \u4E2A\u5546\u54C1:
848
+ `);
849
+ for (const p of productList) {
850
+ console.log(` [${p.source}] ${p.title}`);
851
+ console.log(` ID: ${p.id} \u9500\u91CF: ${p.salesCount} \u54C1\u724C: ${p.brandName}`);
852
+ if (p.images.length > 0) console.log(` \u56FE\u7247: ${p.images[0]}`);
853
+ console.log("");
854
+ }
855
+ if (pageResult.nextCursor) {
856
+ console.log(`\u4E0B\u4E00\u9875\u6E38\u6807: ${pageResult.nextCursor}`);
857
+ console.log(`\u4F7F\u7528: beervid query-products --creator-id ${creatorId} --cursor ${pageResult.nextCursor}`);
858
+ } else {
859
+ console.log("\u5DF2\u5230\u6700\u540E\u4E00\u9875");
860
+ }
861
+ printResult({ products: productList, nextCursor: pageResult.nextCursor });
862
+ } catch (err) {
863
+ rethrowIfProcessExit(err);
864
+ console.error("\u67E5\u8BE2\u5546\u54C1\u5931\u8D25:", err.message);
865
+ process.exit(1);
866
+ }
867
+ }
868
+ );
869
+ }
870
+
887
871
  // src/commands/publish-tt-flow.ts
888
872
  function parsePositiveInt(value, optionName, defaultValue) {
889
873
  const parsed = parseInt(value ?? `${defaultValue}`, 10);
@@ -894,7 +878,7 @@ function parsePositiveInt(value, optionName, defaultValue) {
894
878
  return parsed;
895
879
  }
896
880
  function register8(cli2) {
897
- 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 3\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(
881
+ 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(
898
882
  async (options) => {
899
883
  if (!options.businessId || !options.file) {
900
884
  const missing = [
@@ -908,7 +892,7 @@ function register8(cli2) {
908
892
  );
909
893
  process.exit(1);
910
894
  }
911
- const intervalSec = parsePositiveInt(options.interval, "--interval", 3);
895
+ const intervalSec = parsePositiveInt(options.interval, "--interval", 5);
912
896
  const maxPolls = parsePositiveInt(options.maxPolls, "--max-polls", 60);
913
897
  const queryIntervalSec = parsePositiveInt(options.queryInterval, "--query-interval", 5);
914
898
  const queryMaxAttempts = parsePositiveInt(
@@ -946,11 +930,6 @@ function register8(cli2) {
946
930
  );
947
931
  query = queryResult.query;
948
932
  warnings.push(...queryResult.warnings);
949
- } else if (status.finalStatus === "PUBLISH_COMPLETE") {
950
- warnings.push({
951
- code: "VIDEO_ID_MISSING",
952
- message: "\u53D1\u5E03\u6210\u529F\uFF0C\u4F46\u72B6\u6001\u7ED3\u679C\u4E2D\u672A\u8FD4\u56DE videoId\uFF0C\u5DF2\u8DF3\u8FC7\u89C6\u9891\u6570\u636E\u67E5\u8BE2"
953
- });
954
933
  }
955
934
  const result = {
956
935
  flowType: "tt",
@@ -1044,58 +1023,71 @@ function register9(cli2) {
1044
1023
  );
1045
1024
  try {
1046
1025
  console.log("\u5F00\u59CB\u6267\u884C TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B...");
1047
- console.log("1/4 \u6B63\u5728\u67E5\u8BE2\u5546\u54C1\u5217\u8868...");
1048
- const productPool = await fetchProductPool(
1049
- options.creatorId,
1050
- productType,
1051
- pageSize,
1052
- maxProductPages
1053
- );
1054
1026
  const warnings = [];
1055
- if (productPool.summary.reachedPageLimit && productPool.summary.nextCursor) {
1056
- warnings.push({
1057
- code: "PRODUCT_SCAN_LIMIT_REACHED",
1058
- message: `\u5546\u54C1\u626B\u63CF\u5DF2\u8FBE\u5230\u9875\u6570\u4E0A\u9650 ${maxProductPages}\uFF0C\u4ECD\u5B58\u5728\u672A\u62C9\u53D6\u5206\u9875`
1059
- });
1060
- }
1061
- if (productPool.summary.failedSources.length > 0) {
1062
- warnings.push({
1063
- code: "PRODUCT_SOURCE_PARTIAL_FAILURE",
1064
- message: `\u4EE5\u4E0B\u5546\u54C1\u6E90\u8BF7\u6C42\u5931\u8D25: ${productPool.summary.failedSources.join(", ")}\uFF0C\u5546\u54C1\u6C60\u53EF\u80FD\u4E0D\u5B8C\u6574`
1065
- });
1066
- }
1067
- if (productPool.products.length === 0) {
1068
- console.error("TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\u5931\u8D25: \u5F53\u524D\u5546\u54C1\u6C60\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u9009\u62E9\u5546\u54C1");
1069
- process.exit(1);
1070
- }
1071
1027
  let selectionMode = "automatic";
1072
1028
  let selectedProduct;
1073
- if (options.productId) {
1029
+ let productPoolSummary;
1030
+ if (options.productId && options.productTitle) {
1074
1031
  selectionMode = "manual";
1075
- const matchedProduct = productPool.products.find(
1076
- (product) => product.id === options.productId
1032
+ console.log("1/4 \u5DF2\u624B\u52A8\u6307\u5B9A\u5546\u54C1\uFF0C\u8DF3\u8FC7\u5546\u54C1\u67E5\u8BE2...");
1033
+ selectedProduct = buildManualProduct(options.productId, options.productTitle);
1034
+ } else {
1035
+ console.log("1/4 \u6B63\u5728\u67E5\u8BE2\u5546\u54C1\u5217\u8868...");
1036
+ const productPool = await fetchProductPool(
1037
+ options.creatorId,
1038
+ productType,
1039
+ pageSize,
1040
+ maxProductPages
1077
1041
  );
1078
- const resolvedTitle = options.productTitle ?? matchedProduct?.title;
1079
- if (!resolvedTitle) {
1080
- console.error(
1081
- "\u9519\u8BEF: \u624B\u52A8\u6307\u5B9A --product-id \u65F6\uFF0C\u5982\u65E0\u6CD5\u4ECE\u5DF2\u626B\u63CF\u5546\u54C1\u6C60\u8865\u9F50\u6807\u9898\uFF0C\u5219\u5FC5\u987B\u663E\u5F0F\u4F20\u5165 --product-title"
1082
- );
1083
- process.exit(1);
1042
+ productPoolSummary = productPool.summary;
1043
+ if (productPool.summary.reachedPageLimit && productPool.summary.nextCursor) {
1044
+ warnings.push({
1045
+ code: "PRODUCT_SCAN_LIMIT_REACHED",
1046
+ message: `\u5546\u54C1\u626B\u63CF\u5DF2\u8FBE\u5230\u9875\u6570\u4E0A\u9650 ${maxProductPages}\uFF0C\u4ECD\u5B58\u5728\u672A\u62C9\u53D6\u5206\u9875`
1047
+ });
1084
1048
  }
1085
- selectedProduct = buildManualProduct(options.productId, resolvedTitle, matchedProduct);
1086
- if (!matchedProduct) {
1049
+ if (productPool.summary.failedSources.length > 0) {
1087
1050
  warnings.push({
1088
- code: "PRODUCT_NOT_IN_SCANNED_POOL",
1089
- message: "\u624B\u52A8\u6307\u5B9A\u7684\u5546\u54C1 ID \u672A\u51FA\u73B0\u5728\u5DF2\u626B\u63CF\u5546\u54C1\u6C60\u4E2D\uFF0C\u5DF2\u4F7F\u7528\u663E\u5F0F\u53C2\u6570\u7EE7\u7EED\u53D1\u5E03"
1051
+ code: "PRODUCT_SOURCE_PARTIAL_FAILURE",
1052
+ message: `\u4EE5\u4E0B\u5546\u54C1\u6E90\u8BF7\u6C42\u5931\u8D25: ${productPool.summary.failedSources.join(", ")}\uFF0C\u5546\u54C1\u6C60\u53EF\u80FD\u4E0D\u5B8C\u6574`
1090
1053
  });
1091
1054
  }
1092
- } else if (options.interactive) {
1093
- selectionMode = "interactive";
1094
- console.log("2/4 \u8BF7\u9009\u62E9\u8981\u6302\u8F66\u7684\u5546\u54C1...");
1095
- selectedProduct = await promptForProductSelection(productPool.products);
1096
- } else {
1097
- console.log("2/4 \u6B63\u5728\u81EA\u52A8\u9009\u62E9\u5546\u54C1\uFF08\u6309\u9500\u91CF\u6700\u9AD8\u4F18\u5148\uFF09...");
1098
- selectedProduct = sortProductsForSelection(productPool.products)[0];
1055
+ if (options.productId) {
1056
+ selectionMode = "manual";
1057
+ const matchedProduct = productPool.products.find(
1058
+ (product) => product.id === options.productId
1059
+ );
1060
+ const resolvedTitle = matchedProduct?.title;
1061
+ if (!resolvedTitle) {
1062
+ console.error(
1063
+ "\u9519\u8BEF: \u624B\u52A8\u6307\u5B9A --product-id \u65F6\uFF0C\u5982\u65E0\u6CD5\u4ECE\u5DF2\u626B\u63CF\u5546\u54C1\u6C60\u8865\u9F50\u6807\u9898\uFF0C\u5219\u5FC5\u987B\u663E\u5F0F\u4F20\u5165 --product-title"
1064
+ );
1065
+ process.exit(1);
1066
+ }
1067
+ selectedProduct = buildManualProduct(options.productId, resolvedTitle, matchedProduct);
1068
+ } else if (options.interactive) {
1069
+ if (productPool.products.length === 0) {
1070
+ console.error("TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\u5931\u8D25: \u5F53\u524D\u5546\u54C1\u6C60\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u9009\u62E9\u5546\u54C1");
1071
+ process.exit(1);
1072
+ }
1073
+ selectionMode = "interactive";
1074
+ console.log("2/4 \u8BF7\u9009\u62E9\u8981\u6302\u8F66\u7684\u5546\u54C1...");
1075
+ selectedProduct = await promptForProductSelection(productPool.products);
1076
+ } else {
1077
+ if (productPool.products.length === 0) {
1078
+ console.error("TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\u5931\u8D25: \u5F53\u524D\u5546\u54C1\u6C60\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u9009\u62E9\u5546\u54C1");
1079
+ process.exit(1);
1080
+ }
1081
+ console.log("2/4 \u6B63\u5728\u81EA\u52A8\u9009\u62E9\u5546\u54C1\uFF08\u6309\u9500\u91CF\u6700\u9AD8\u4F18\u5148\uFF09...");
1082
+ const publishable = sortProductsForSelection(productPool.products);
1083
+ if (publishable.length === 0) {
1084
+ console.error(
1085
+ "TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\u5931\u8D25: \u5546\u54C1\u6C60\u4E2D\u6CA1\u6709\u53EF\u53D1\u5E03\u5546\u54C1\uFF08\u5BA1\u6838\u672A\u901A\u8FC7\u6216\u65E0\u5E93\u5B58\uFF09\uFF0C\u5982\u9700\u5F3A\u5236\u6307\u5B9A\u8BF7\u4F7F\u7528 --product-id/--product-title"
1086
+ );
1087
+ process.exit(1);
1088
+ }
1089
+ selectedProduct = publishable[0];
1090
+ }
1099
1091
  }
1100
1092
  console.log("3/4 \u6B63\u5728\u4E0A\u4F20\u6302\u8F66\u89C6\u9891...");
1101
1093
  const upload = await uploadTtsVideo(options.file, options.creatorId, options.token);
@@ -1115,7 +1107,7 @@ function register9(cli2) {
1115
1107
  }
1116
1108
  const result = {
1117
1109
  flowType: "tts",
1118
- productQuery: productPool.summary,
1110
+ productQuery: productPoolSummary ?? buildSkippedProductQuerySummary(productType, pageSize),
1119
1111
  selectedProduct: buildSelectedProductSummary(selectedProduct, selectionMode),
1120
1112
  upload,
1121
1113
  publish: publishResult.publish,
@@ -1172,6 +1164,7 @@ function register10(cli2) {
1172
1164
 
1173
1165
  // src/cli.ts
1174
1166
  var cli = cac("beervid");
1167
+ var cliVersion = true ? "0.2.2" : pkg.version;
1175
1168
  register10(cli);
1176
1169
  register(cli);
1177
1170
  register2(cli);
@@ -1183,7 +1176,7 @@ register7(cli);
1183
1176
  register8(cli);
1184
1177
  register9(cli);
1185
1178
  cli.help();
1186
- cli.version("1.0.0");
1179
+ cli.version(cliVersion);
1187
1180
  if (process.argv.slice(2).length === 0) {
1188
1181
  cli.outputHelp();
1189
1182
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beervid-app-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "BEERVID App CLI — TikTok video publish, account auth, and data query",
5
5
  "type": "module",
6
6
  "engines": {
@@ -11,6 +11,8 @@
11
11
  },
12
12
  "files": [
13
13
  "dist/",
14
+ "agents/",
15
+ "references/",
14
16
  "SKILL.md",
15
17
  "README.md"
16
18
  ],