beervid-app-cli 0.2.2 → 0.2.4

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.
Files changed (35) hide show
  1. package/README.md +45 -0
  2. package/SKILL.md +204 -376
  3. package/dist/cli.mjs +117 -151
  4. package/docs/database-schema.md +231 -0
  5. package/docs/oauth-callback.md +282 -0
  6. package/docs/retry-and-idempotency.md +295 -0
  7. package/docs/tt-poll-task.md +239 -0
  8. package/docs/tts-product-cache.md +256 -0
  9. package/example/express/README.md +58 -0
  10. package/example/express/package.json +20 -0
  11. package/example/express/server.ts +431 -0
  12. package/example/express/tsconfig.json +12 -0
  13. package/example/nextjs/.env.example +3 -0
  14. package/example/nextjs/README.md +54 -0
  15. package/example/nextjs/app/api/oauth/callback/route.ts +34 -0
  16. package/example/nextjs/app/api/oauth/url/route.ts +30 -0
  17. package/example/nextjs/app/api/products/route.ts +43 -0
  18. package/example/nextjs/app/api/publish/tt/route.ts +116 -0
  19. package/example/nextjs/app/api/publish/tts/route.ts +58 -0
  20. package/example/nextjs/app/api/status/[shareId]/route.ts +41 -0
  21. package/example/nextjs/app/layout.tsx +9 -0
  22. package/example/nextjs/app/page.tsx +80 -0
  23. package/example/nextjs/lib/beervid-client.ts +107 -0
  24. package/example/nextjs/next.config.ts +4 -0
  25. package/example/nextjs/package.json +19 -0
  26. package/example/nextjs/tsconfig.json +23 -0
  27. package/example/standard/README.md +51 -0
  28. package/example/standard/api-client.ts +181 -0
  29. package/example/standard/get-oauth-url.ts +44 -0
  30. package/example/standard/package.json +18 -0
  31. package/example/standard/query-products.ts +141 -0
  32. package/example/standard/tsconfig.json +12 -0
  33. package/example/standard/tt-publish-flow.ts +194 -0
  34. package/example/standard/tts-publish-flow.ts +246 -0
  35. package/package.json +3 -1
package/dist/cli.mjs CHANGED
@@ -151,6 +151,28 @@ function rethrowIfProcessExit(error) {
151
151
  throw error;
152
152
  }
153
153
  }
154
+ function getRawOptionValues(rawArgs, optionName) {
155
+ const values = [];
156
+ const prefix = `${optionName}=`;
157
+ for (let i = 0; i < rawArgs.length; i++) {
158
+ const arg = rawArgs[i];
159
+ if (arg === optionName) {
160
+ const next = rawArgs[i + 1];
161
+ if (typeof next === "string" && !next.startsWith("-")) {
162
+ values.push(next);
163
+ i++;
164
+ }
165
+ continue;
166
+ }
167
+ if (arg.startsWith(prefix)) {
168
+ values.push(arg.slice(prefix.length));
169
+ }
170
+ }
171
+ return values;
172
+ }
173
+ function getRawOptionValue(rawArgs, optionName) {
174
+ return getRawOptionValues(rawArgs, optionName).at(-1);
175
+ }
154
176
 
155
177
  // src/commands/oauth.ts
156
178
  function register(cli2) {
@@ -169,11 +191,11 @@ function register(cli2) {
169
191
  if (type === "tt") {
170
192
  const data = await openApiGet("/api/v1/open/thirdparty-auth/tt-url");
171
193
  console.log("TT OAuth \u6388\u6743\u94FE\u63A5:");
172
- printResult({ type: "tt", url: data });
194
+ printResult(data);
173
195
  } else {
174
196
  const data = await openApiGet("/api/v1/open/thirdparty-auth/tts-url");
175
197
  console.log("TTS OAuth \u6388\u6743\u94FE\u63A5:");
176
- printResult({ type: "tts", url: data.crossBorderUrl });
198
+ printResult(data);
177
199
  }
178
200
  } catch (err) {
179
201
  rethrowIfProcessExit(err);
@@ -186,8 +208,9 @@ function register(cli2) {
186
208
  // src/commands/account.ts
187
209
  function register2(cli2) {
188
210
  cli2.command("get-account-info", "\u67E5\u8BE2 TikTok \u8D26\u53F7\u4FE1\u606F").option("--type <type>", "\u8D26\u53F7\u7C7B\u578B: TT \u6216 TTS").option("--account-id <id>", "\u8D26\u53F7 ID").action(async (options) => {
189
- if (!options.type || !options.accountId) {
190
- const missing = [!options.type && "--type", !options.accountId && "--account-id"].filter(Boolean).join(", ");
211
+ const accountId = getRawOptionValue(cli2.rawArgs, "--account-id");
212
+ if (!options.type || !accountId) {
213
+ const missing = [!options.type && "--type", !accountId && "--account-id"].filter(Boolean).join(", ");
191
214
  console.error(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${missing}
192
215
  `);
193
216
  console.error("\u7528\u6CD5: beervid get-account-info --type <TT|TTS> --account-id <id>");
@@ -201,7 +224,7 @@ function register2(cli2) {
201
224
  try {
202
225
  const data = await openApiPost("/api/v1/open/account/info", {
203
226
  accountType,
204
- accountId: options.accountId
227
+ accountId
205
228
  });
206
229
  printResult(data);
207
230
  } catch (err) {
@@ -256,19 +279,20 @@ function register3(cli2) {
256
279
  process.exit(1);
257
280
  }
258
281
  const uploadType = (options.type ?? "normal").toLowerCase();
282
+ const creatorId = getRawOptionValue(cli2.rawArgs, "--creator-id");
259
283
  if (!VALID_UPLOAD_TYPES.includes(uploadType)) {
260
284
  console.error("\u9519\u8BEF: --type \u5FC5\u987B\u4E3A normal \u6216 tts");
261
285
  process.exit(1);
262
286
  }
263
- if (uploadType === "tts" && !options.creatorId) {
287
+ if (uploadType === "tts" && !creatorId) {
264
288
  console.error("\u9519\u8BEF: TTS \u4E0A\u4F20\u6A21\u5F0F\u9700\u8981 --creator-id \u53C2\u6570");
265
289
  process.exit(1);
266
290
  }
267
291
  try {
268
292
  let data;
269
293
  if (uploadType === "tts") {
270
- console.log(`TTS \u4E0A\u4F20\u6A21\u5F0F\uFF0CcreatorUserOpenId: ${options.creatorId}`);
271
- data = await uploadTtsVideo(options.file, options.creatorId, options.token);
294
+ console.log(`TTS \u4E0A\u4F20\u6A21\u5F0F\uFF0CcreatorUserOpenId: ${creatorId}`);
295
+ data = await uploadTtsVideo(options.file, creatorId, options.token);
272
296
  } else {
273
297
  console.log("\u666E\u901A\u4E0A\u4F20\u6A21\u5F0F");
274
298
  data = await uploadNormalVideo(options.file, options.token);
@@ -293,6 +317,10 @@ function register4(cli2) {
293
317
  }).option("--caption <text>", "\u89C6\u9891\u63CF\u8FF0/\u6587\u6848\uFF08\u53EF\u9009\uFF09").action(
294
318
  async (options) => {
295
319
  const publishType = (options.type ?? "normal").toLowerCase();
320
+ const businessId = getRawOptionValue(cli2.rawArgs, "--business-id");
321
+ const creatorId = getRawOptionValue(cli2.rawArgs, "--creator-id");
322
+ const fileId = getRawOptionValue(cli2.rawArgs, "--file-id");
323
+ const productId = getRawOptionValue(cli2.rawArgs, "--product-id");
296
324
  if (!VALID_PUBLISH_TYPES.includes(publishType)) {
297
325
  console.error("\u9519\u8BEF: --type \u5FC5\u987B\u4E3A normal \u6216 shoppable");
298
326
  process.exit(1);
@@ -301,9 +329,9 @@ function register4(cli2) {
301
329
  let data;
302
330
  if (publishType === "shoppable") {
303
331
  const missing = [
304
- !options.creatorId && "--creator-id",
305
- !options.fileId && "--file-id",
306
- !options.productId && "--product-id",
332
+ !creatorId && "--creator-id",
333
+ !fileId && "--file-id",
334
+ !productId && "--product-id",
307
335
  !options.productTitle && "--product-title"
308
336
  ].filter(Boolean);
309
337
  if (missing.length > 0) {
@@ -325,17 +353,17 @@ function register4(cli2) {
325
353
  data = await openApiPost(
326
354
  "/api/v1/open/tts/shoppable-video/publish",
327
355
  {
328
- creatorUserOpenId: options.creatorId,
329
- fileId: options.fileId,
356
+ creatorUserOpenId: creatorId,
357
+ fileId,
330
358
  title: options.caption ?? "",
331
- productId: options.productId,
359
+ productId,
332
360
  productTitle
333
361
  }
334
362
  );
335
363
  console.log("\n\u53D1\u5E03\u6210\u529F\uFF08\u6302\u8F66\u89C6\u9891\u7ACB\u5373\u5B8C\u6210\uFF09:");
336
364
  } else {
337
365
  const missing = [
338
- !options.businessId && "--business-id",
366
+ !businessId && "--business-id",
339
367
  !options.videoUrl && "--video-url"
340
368
  ].filter(Boolean);
341
369
  if (missing.length > 0) {
@@ -350,14 +378,14 @@ function register4(cli2) {
350
378
  data = await openApiPost(
351
379
  "/api/v1/open/tiktok/video/publish",
352
380
  {
353
- businessId: options.businessId,
381
+ businessId,
354
382
  videoUrl: options.videoUrl,
355
383
  caption: options.caption ?? ""
356
384
  }
357
385
  );
358
386
  console.log("\n\u53D1\u5E03\u5DF2\u63D0\u4EA4\uFF08\u9700\u8F6E\u8BE2\u72B6\u6001\uFF09:");
359
387
  console.log(
360
- `\u63D0\u793A: \u4F7F\u7528 beervid poll-status --business-id ${options.businessId} --share-id ${data.shareId} \u8F6E\u8BE2\u8FDB\u5EA6`
388
+ `\u63D0\u793A: \u4F7F\u7528 beervid poll-status --business-id ${businessId} --share-id ${data.shareId} \u8F6E\u8BE2\u8FDB\u5EA6`
361
389
  );
362
390
  }
363
391
  printResult(data);
@@ -377,10 +405,12 @@ function sleep(ms) {
377
405
  function register5(cli2) {
378
406
  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(
379
407
  async (options) => {
380
- if (!options.businessId || !options.shareId) {
408
+ const businessId = getRawOptionValue(cli2.rawArgs, "--business-id");
409
+ const shareId = getRawOptionValue(cli2.rawArgs, "--share-id");
410
+ if (!businessId || !shareId) {
381
411
  const missing = [
382
- !options.businessId && "--business-id",
383
- !options.shareId && "--share-id"
412
+ !businessId && "--business-id",
413
+ !shareId && "--share-id"
384
414
  ].filter(Boolean);
385
415
  console.error(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${missing.join(", ")}
386
416
  `);
@@ -399,14 +429,14 @@ function register5(cli2) {
399
429
  }
400
430
  try {
401
431
  console.log(`\u5F00\u59CB\u8F6E\u8BE2\u53D1\u5E03\u72B6\u6001 (\u95F4\u9694 ${intervalSec}s, \u6700\u591A ${maxPolls} \u6B21)`);
402
- console.log(`businessId: ${options.businessId}`);
403
- console.log(`shareId: ${options.shareId}
432
+ console.log(`businessId: ${businessId}`);
433
+ console.log(`shareId: ${shareId}
404
434
  `);
405
435
  let lastStatus = "UNKNOWN";
406
436
  for (let i = 1; i <= maxPolls; i++) {
407
437
  const data = await openApiPost("/api/v1/open/tiktok/video/status", {
408
- businessId: options.businessId,
409
- shareId: options.shareId
438
+ businessId,
439
+ shareId
410
440
  });
411
441
  const status = data.status ?? data.Status ?? "UNKNOWN";
412
442
  const postIds = data.post_ids ?? [];
@@ -423,7 +453,7 @@ function register5(cli2) {
423
453
  console.log("\u53D1\u5E03\u6210\u529F!");
424
454
  console.log(`\u89C6\u9891 ID: ${postIds[0]}`);
425
455
  console.log(
426
- `\u63D0\u793A: \u4F7F\u7528 beervid query-video --business-id ${options.businessId} --item-ids ${postIds[0]} \u67E5\u8BE2\u6570\u636E`
456
+ `\u63D0\u793A: \u4F7F\u7528 beervid query-video --business-id ${businessId} --item-ids ${postIds[0]} \u67E5\u8BE2\u6570\u636E`
427
457
  );
428
458
  printResult(data);
429
459
  process.exit(0);
@@ -451,12 +481,14 @@ function register5(cli2) {
451
481
 
452
482
  // src/commands/query-video.ts
453
483
  function register6(cli2) {
454
- 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\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF08\u5FC5\u586B\uFF09").action(
455
- async (options) => {
456
- if (!options.businessId || !options.itemIds) {
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) {
457
489
  const missing = [
458
- !options.businessId && "--business-id",
459
- !options.itemIds && "--item-ids"
490
+ !businessId && "--business-id",
491
+ rawItemIdArgs.length === 0 && "--item-ids"
460
492
  ].filter(Boolean);
461
493
  console.error(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${missing.join(", ")}
462
494
  `);
@@ -465,7 +497,7 @@ function register6(cli2) {
465
497
  );
466
498
  process.exit(1);
467
499
  }
468
- const itemIds = options.itemIds.split(",").map((id) => id.trim()).filter(Boolean);
500
+ const itemIds = rawItemIdArgs.flatMap((value) => value.split(",")).map((id) => id.trim()).filter(Boolean);
469
501
  if (itemIds.length === 0) {
470
502
  console.error("\u9519\u8BEF: --item-ids \u4E0D\u80FD\u4E3A\u7A7A");
471
503
  process.exit(1);
@@ -474,7 +506,7 @@ function register6(cli2) {
474
506
  console.log(`\u67E5\u8BE2 ${itemIds.length} \u4E2A\u89C6\u9891\u7684\u6570\u636E...
475
507
  `);
476
508
  const data = await openApiPost("/api/v1/open/tiktok/video/query", {
477
- businessId: options.businessId,
509
+ businessId,
478
510
  itemIds
479
511
  });
480
512
  const list = data.videoList ?? data.videos ?? [];
@@ -501,7 +533,7 @@ function register6(cli2) {
501
533
  if (v.shareUrl) console.log(` \u94FE\u63A5: ${v.shareUrl}`);
502
534
  console.log("");
503
535
  }
504
- printResult({ videos: normalized, _raw: data });
536
+ printResult(data);
505
537
  } catch (err) {
506
538
  rethrowIfProcessExit(err);
507
539
  console.error("\u67E5\u8BE2\u89C6\u9891\u6570\u636E\u5931\u8D25:", err.message);
@@ -581,23 +613,6 @@ async function pollNormalVideoStatus(businessId, shareId, intervalSec, maxPolls)
581
613
  raw: lastData
582
614
  };
583
615
  }
584
- function normalizeVideoQuery(data) {
585
- const list = data.videoList ?? data.videos ?? [];
586
- const videos = list.map((video) => ({
587
- itemId: video.itemId ?? video.item_id,
588
- videoViews: video.videoViews ?? video.video_views ?? 0,
589
- likes: video.likes ?? 0,
590
- comments: video.comments ?? 0,
591
- shares: video.shares ?? 0,
592
- thumbnailUrl: video.thumbnailUrl ?? video.thumbnail_url ?? "",
593
- shareUrl: video.shareUrl ?? video.share_url ?? ""
594
- }));
595
- return {
596
- attempts: 1,
597
- videos,
598
- raw: data
599
- };
600
- }
601
616
  async function queryVideoWithRetry(businessId, itemId, intervalSec, maxAttempts) {
602
617
  const warnings = [];
603
618
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -605,10 +620,9 @@ async function queryVideoWithRetry(businessId, itemId, intervalSec, maxAttempts)
605
620
  businessId,
606
621
  itemIds: [itemId]
607
622
  });
608
- const normalized = normalizeVideoQuery(data);
609
- normalized.attempts = attempt;
610
- if (normalized.videos.length > 0) {
611
- return { query: normalized, warnings };
623
+ const list = data.videoList ?? data.videos ?? [];
624
+ if (list.length > 0) {
625
+ return { query: data, warnings };
612
626
  }
613
627
  if (attempt < maxAttempts) {
614
628
  await sleep2(intervalSec * 1e3);
@@ -642,6 +656,7 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
642
656
  return token !== null;
643
657
  });
644
658
  const allProducts = /* @__PURE__ */ new Map();
659
+ const rawGroups = [];
645
660
  let nextShopToken = cursor.shopToken;
646
661
  let nextShowcaseToken = cursor.showcaseToken;
647
662
  let successCount = 0;
@@ -669,6 +684,7 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
669
684
  successCount += 1;
670
685
  const { data } = result.value;
671
686
  const groups = Array.isArray(data) ? data : [data];
687
+ rawGroups.push(...groups);
672
688
  for (const group of groups) {
673
689
  const token = group.nextPageToken === void 0 ? null : group.nextPageToken;
674
690
  if (type === "shop") nextShopToken = token;
@@ -694,6 +710,7 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
694
710
  const nextCursor = encodeCursor({ shopToken: nextShopToken, showcaseToken: nextShowcaseToken });
695
711
  return {
696
712
  products: Array.from(allProducts.values()),
713
+ rawGroups,
697
714
  nextCursor,
698
715
  successCount,
699
716
  failedSources
@@ -701,6 +718,7 @@ async function queryProductsPage(creatorId, productType, pageSize, cursor) {
701
718
  }
702
719
  async function fetchProductPool(creatorId, productType, pageSize, maxPages) {
703
720
  const allProducts = /* @__PURE__ */ new Map();
721
+ const rawGroups = [];
704
722
  let cursor = { shopToken: "", showcaseToken: "" };
705
723
  let nextCursor = null;
706
724
  let pagesScanned = 0;
@@ -714,6 +732,7 @@ async function fetchProductPool(creatorId, productType, pageSize, maxPages) {
714
732
  failedSourcesSet.add(src);
715
733
  }
716
734
  pagesScanned = page;
735
+ rawGroups.push(...pageResult.rawGroups);
717
736
  for (const product of pageResult.products) {
718
737
  if (!allProducts.has(product.id)) {
719
738
  allProducts.set(product.id, product);
@@ -727,6 +746,7 @@ async function fetchProductPool(creatorId, productType, pageSize, maxPages) {
727
746
  }
728
747
  return {
729
748
  products: Array.from(allProducts.values()),
749
+ rawGroups,
730
750
  summary: {
731
751
  productType,
732
752
  pageSize,
@@ -738,17 +758,6 @@ async function fetchProductPool(creatorId, productType, pageSize, maxPages) {
738
758
  }
739
759
  };
740
760
  }
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
761
  function isProductPublishable(product) {
753
762
  if (product.reviewStatus && product.reviewStatus.toUpperCase() !== "APPROVED") return false;
754
763
  if (product.inventoryStatus && product.inventoryStatus.toUpperCase() !== "IN_STOCK") return false;
@@ -757,18 +766,6 @@ function isProductPublishable(product) {
757
766
  function sortProductsForSelection(products) {
758
767
  return products.filter(isProductPublishable).sort((a, b) => b.salesCount - a.salesCount);
759
768
  }
760
- function buildSelectedProductSummary(product, selectionMode) {
761
- return {
762
- selectionMode,
763
- id: product.id,
764
- title: product.title,
765
- salesCount: product.salesCount,
766
- source: product.source,
767
- price: product.price,
768
- brandName: product.brandName,
769
- shopName: product.shopName
770
- };
771
- }
772
769
  async function promptForProductSelection(products) {
773
770
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
774
771
  throw new Error("\u4EA4\u4E92\u6A21\u5F0F\u9700\u8981\u5728 TTY \u7EC8\u7AEF\u4E2D\u8FD0\u884C");
@@ -806,12 +803,12 @@ var VALID_PRODUCT_TYPES = ["shop", "showcase", "all"];
806
803
  function register7(cli2) {
807
804
  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
805
  async (options) => {
809
- if (!options.creatorId) {
806
+ const creatorId = getRawOptionValue(cli2.rawArgs, "--creator-id");
807
+ if (!creatorId) {
810
808
  console.error("\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: --creator-id\n");
811
809
  console.error("\u7528\u6CD5: beervid query-products --creator-id <id>");
812
810
  process.exit(1);
813
811
  }
814
- const creatorId = options.creatorId;
815
812
  const productType = (options.productType ?? "all").toLowerCase();
816
813
  const pageSize = parseInt(options.pageSize ?? "20", 10);
817
814
  const cursor = options.cursor ?? "";
@@ -858,7 +855,7 @@ function register7(cli2) {
858
855
  } else {
859
856
  console.log("\u5DF2\u5230\u6700\u540E\u4E00\u9875");
860
857
  }
861
- printResult({ products: productList, nextCursor: pageResult.nextCursor });
858
+ printResult(pageResult.rawGroups);
862
859
  } catch (err) {
863
860
  rethrowIfProcessExit(err);
864
861
  console.error("\u67E5\u8BE2\u5546\u54C1\u5931\u8D25:", err.message);
@@ -880,9 +877,10 @@ function parsePositiveInt(value, optionName, defaultValue) {
880
877
  function register8(cli2) {
881
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(
882
879
  async (options) => {
883
- if (!options.businessId || !options.file) {
880
+ const businessId = getRawOptionValue(cli2.rawArgs, "--business-id");
881
+ if (!businessId || !options.file) {
884
882
  const missing = [
885
- !options.businessId && "--business-id",
883
+ !businessId && "--business-id",
886
884
  !options.file && "--file"
887
885
  ].filter(Boolean);
888
886
  console.error(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${missing.join(", ")}
@@ -905,42 +903,25 @@ function register8(cli2) {
905
903
  console.log("1/4 \u6B63\u5728\u4E0A\u4F20\u89C6\u9891...");
906
904
  const upload = await uploadNormalVideo(options.file, options.token);
907
905
  console.log("2/4 \u6B63\u5728\u53D1\u5E03\u89C6\u9891...");
908
- const publish = await publishNormalVideo(
909
- options.businessId,
910
- upload.fileUrl,
911
- options.caption
912
- );
906
+ const publish = await publishNormalVideo(businessId, upload.fileUrl, options.caption);
913
907
  console.log("3/4 \u6B63\u5728\u8F6E\u8BE2\u53D1\u5E03\u72B6\u6001...");
914
- const status = await pollNormalVideoStatus(
915
- options.businessId,
916
- publish.shareId,
917
- intervalSec,
918
- maxPolls
919
- );
920
- const warnings = [];
908
+ const status = await pollNormalVideoStatus(businessId, publish.shareId, intervalSec, maxPolls);
921
909
  const videoId = status.postIds[0] ?? null;
922
910
  let query = null;
923
911
  if (status.finalStatus === "PUBLISH_COMPLETE" && videoId) {
924
912
  console.log("4/4 \u6B63\u5728\u67E5\u8BE2\u89C6\u9891\u6570\u636E...");
925
- const queryResult = await queryVideoWithRetry(
926
- options.businessId,
927
- videoId,
928
- queryIntervalSec,
929
- queryMaxAttempts
930
- );
913
+ const queryResult = await queryVideoWithRetry(businessId, videoId, queryIntervalSec, queryMaxAttempts);
931
914
  query = queryResult.query;
932
- warnings.push(...queryResult.warnings);
915
+ for (const warning of queryResult.warnings) {
916
+ console.warn(warning.message);
917
+ }
933
918
  }
934
- const result = {
935
- flowType: "tt",
919
+ printResult({
936
920
  upload,
937
921
  publish,
938
- status,
939
- videoId,
940
- query,
941
- warnings
942
- };
943
- printResult(result);
922
+ status: status.raw,
923
+ query
924
+ });
944
925
  if (status.finalStatus === "FAILED") {
945
926
  process.exit(1);
946
927
  }
@@ -990,9 +971,11 @@ function buildManualProduct(productId, productTitle, matchedProduct) {
990
971
  function register9(cli2) {
991
972
  cli2.command("publish-tts-flow", "\u6267\u884C TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\uFF1A\u67E5\u5546\u54C1\u3001\u9009\u5546\u54C1\u3001\u4E0A\u4F20\u3001\u53D1\u5E03").option("--creator-id <id>", "TTS \u8D26\u53F7 creatorUserOpenId\uFF08\u5FC5\u586B\uFF09").option("--file <path>", "\u89C6\u9891\u6587\u4EF6\u8DEF\u5F84\u6216 URL\uFF08\u5FC5\u586B\uFF09").option("--caption <text>", "\u89C6\u9891\u6807\u9898/\u6587\u6848\uFF08\u53EF\u9009\uFF09").option("--token <token>", "\u5DF2\u6709\u4E0A\u4F20\u51ED\u8BC1\uFF08\u53EF\u9009\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("--max-product-pages <n>", "\u5546\u54C1\u626B\u63CF\u6700\u5927\u9875\u6570\uFF08\u9ED8\u8BA4 5\uFF09").option("--product-id <id>", "\u624B\u52A8\u6307\u5B9A\u5546\u54C1 ID").option("--product-title <title>", "\u624B\u52A8\u6307\u5B9A\u5546\u54C1\u6807\u9898").option("--interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u5546\u54C1").action(
992
973
  async (options) => {
993
- if (!options.creatorId || !options.file) {
974
+ const creatorId = getRawOptionValue(cli2.rawArgs, "--creator-id");
975
+ const productId = getRawOptionValue(cli2.rawArgs, "--product-id");
976
+ if (!creatorId || !options.file) {
994
977
  const missing = [
995
- !options.creatorId && "--creator-id",
978
+ !creatorId && "--creator-id",
996
979
  !options.file && "--file"
997
980
  ].filter(Boolean);
998
981
  console.error(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${missing.join(", ")}
@@ -1007,11 +990,11 @@ function register9(cli2) {
1007
990
  console.error("\u9519\u8BEF: --product-type \u5FC5\u987B\u4E3A shop\u3001showcase \u6216 all");
1008
991
  process.exit(1);
1009
992
  }
1010
- if (options.productId && options.interactive) {
993
+ if (productId && options.interactive) {
1011
994
  console.error("\u9519\u8BEF: --product-id \u4E0E --interactive \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
1012
995
  process.exit(1);
1013
996
  }
1014
- if (options.productTitle && !options.productId) {
997
+ if (options.productTitle && !productId) {
1015
998
  console.error("\u9519\u8BEF: --product-title \u9700\u8981\u4E0E --product-id \u4E00\u8D77\u4F7F\u7528");
1016
999
  process.exit(1);
1017
1000
  }
@@ -1023,40 +1006,30 @@ function register9(cli2) {
1023
1006
  );
1024
1007
  try {
1025
1008
  console.log("\u5F00\u59CB\u6267\u884C TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B...");
1026
- const warnings = [];
1027
- let selectionMode = "automatic";
1028
1009
  let selectedProduct;
1029
- let productPoolSummary;
1030
- if (options.productId && options.productTitle) {
1031
- selectionMode = "manual";
1010
+ let queriedProducts = null;
1011
+ if (productId && options.productTitle) {
1032
1012
  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);
1013
+ selectedProduct = buildManualProduct(productId, options.productTitle);
1034
1014
  } else {
1035
1015
  console.log("1/4 \u6B63\u5728\u67E5\u8BE2\u5546\u54C1\u5217\u8868...");
1036
1016
  const productPool = await fetchProductPool(
1037
- options.creatorId,
1017
+ creatorId,
1038
1018
  productType,
1039
1019
  pageSize,
1040
1020
  maxProductPages
1041
1021
  );
1042
- productPoolSummary = productPool.summary;
1022
+ queriedProducts = productPool.rawGroups;
1043
1023
  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
- });
1024
+ console.warn(`\u5546\u54C1\u626B\u63CF\u5DF2\u8FBE\u5230\u9875\u6570\u4E0A\u9650 ${maxProductPages}\uFF0C\u4ECD\u5B58\u5728\u672A\u62C9\u53D6\u5206\u9875`);
1048
1025
  }
1049
1026
  if (productPool.summary.failedSources.length > 0) {
1050
- warnings.push({
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`
1053
- });
1054
- }
1055
- if (options.productId) {
1056
- selectionMode = "manual";
1057
- const matchedProduct = productPool.products.find(
1058
- (product) => product.id === options.productId
1027
+ console.warn(
1028
+ `\u4EE5\u4E0B\u5546\u54C1\u6E90\u8BF7\u6C42\u5931\u8D25: ${productPool.summary.failedSources.join(", ")}\uFF0C\u5546\u54C1\u6C60\u53EF\u80FD\u4E0D\u5B8C\u6574`
1059
1029
  );
1030
+ }
1031
+ if (productId) {
1032
+ const matchedProduct = productPool.products.find((product) => product.id === productId);
1060
1033
  const resolvedTitle = matchedProduct?.title;
1061
1034
  if (!resolvedTitle) {
1062
1035
  console.error(
@@ -1064,13 +1037,12 @@ function register9(cli2) {
1064
1037
  );
1065
1038
  process.exit(1);
1066
1039
  }
1067
- selectedProduct = buildManualProduct(options.productId, resolvedTitle, matchedProduct);
1040
+ selectedProduct = buildManualProduct(productId, resolvedTitle, matchedProduct);
1068
1041
  } else if (options.interactive) {
1069
1042
  if (productPool.products.length === 0) {
1070
1043
  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
1044
  process.exit(1);
1072
1045
  }
1073
- selectionMode = "interactive";
1074
1046
  console.log("2/4 \u8BF7\u9009\u62E9\u8981\u6302\u8F66\u7684\u5546\u54C1...");
1075
1047
  selectedProduct = await promptForProductSelection(productPool.products);
1076
1048
  } else {
@@ -1090,30 +1062,24 @@ function register9(cli2) {
1090
1062
  }
1091
1063
  }
1092
1064
  console.log("3/4 \u6B63\u5728\u4E0A\u4F20\u6302\u8F66\u89C6\u9891...");
1093
- const upload = await uploadTtsVideo(options.file, options.creatorId, options.token);
1065
+ const upload = await uploadTtsVideo(options.file, creatorId, options.token);
1094
1066
  console.log("4/4 \u6B63\u5728\u53D1\u5E03\u6302\u8F66\u89C6\u9891...");
1095
1067
  const publishResult = await publishTtsVideo(
1096
- options.creatorId,
1068
+ creatorId,
1097
1069
  upload.videoFileId,
1098
1070
  selectedProduct.id,
1099
1071
  selectedProduct.title,
1100
1072
  options.caption
1101
1073
  );
1102
1074
  if (publishResult.productTitle !== selectedProduct.title) {
1103
- warnings.push({
1104
- code: "PRODUCT_TITLE_TRUNCATED",
1105
- message: "\u5546\u54C1\u6807\u9898\u8D85\u8FC7 29 \u5B57\u7B26\uFF0C\u53D1\u5E03\u65F6\u5DF2\u81EA\u52A8\u622A\u65AD"
1106
- });
1075
+ console.warn("\u5546\u54C1\u6807\u9898\u8D85\u8FC7 29 \u5B57\u7B26\uFF0C\u53D1\u5E03\u65F6\u5DF2\u81EA\u52A8\u622A\u65AD");
1107
1076
  }
1108
- const result = {
1109
- flowType: "tts",
1110
- productQuery: productPoolSummary ?? buildSkippedProductQuerySummary(productType, pageSize),
1111
- selectedProduct: buildSelectedProductSummary(selectedProduct, selectionMode),
1077
+ printResult({
1078
+ products: queriedProducts,
1079
+ selectedProduct,
1112
1080
  upload,
1113
- publish: publishResult.publish,
1114
- warnings
1115
- };
1116
- printResult(result);
1081
+ publish: publishResult.publish
1082
+ });
1117
1083
  } catch (err) {
1118
1084
  rethrowIfProcessExit(err);
1119
1085
  console.error("TTS \u5B8C\u6574\u53D1\u5E03\u6D41\u7A0B\u5931\u8D25:", err.message);
@@ -1164,7 +1130,7 @@ function register10(cli2) {
1164
1130
 
1165
1131
  // src/cli.ts
1166
1132
  var cli = cac("beervid");
1167
- var cliVersion = true ? "0.2.2" : pkg.version;
1133
+ var cliVersion = true ? "0.2.4" : pkg.version;
1168
1134
  register10(cli);
1169
1135
  register(cli);
1170
1136
  register2(cli);