@optima-chat/tiktok-cli 0.12.0 → 0.14.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.
Files changed (3) hide show
  1. package/README.md +14 -0
  2. package/dist/index.js +541 -7
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -55,6 +55,20 @@ tiktok promotions create-flashsale --shop=<id> --price=11.9 # 等号式
55
55
 
56
56
  (0.10.2 之前只认空格式,等号式会让那一格参数**静默消失**而命令照报成功,见 #11。)
57
57
 
58
+ **每条命令只认它自己的参数**(`tiktok <命令> --help` 列出来),外加全局 `--shop` / `--apply`。
59
+ 不认的参数、多余的位置参数、`-f` 这类短参数一律**当场报 `VALIDATION`、不发请求**,并尽量给出最接近的写法:
60
+
61
+ ```bash
62
+ tiktok orders list --shop <id> --days 14
63
+ # ⇒ VALIDATION:orders list:不认 --days(这条命令没有 --days;近 N 天用起点表示,如 --from 2026-09-06 …)。…
64
+ tiktok products get --shop <id> --product-id 1737…
65
+ # ⇒ VALIDATION:products get:不认 --product-id(你是想用 --product 吗?)。…
66
+ ```
67
+
68
+ 报错体里另有 `unknown_flags` / `extra_positional` / `allowed_flags` 三个机读字段。
69
+ (此前不认的参数被**静默丢掉**、按默认值跑出一份看起来正常的结果:`--days 14` 实际查的是 30 天,
70
+ `--product-id` 请求的是 `/products/undefined`,见 #25。)
71
+
58
72
  **开关型 flag**(`--apply` / `--enable` / `--default` / `--unprocessed`)除了裸传,也能带值:
59
73
  `true`/`1`/`yes`/`on` 为真,`false`/`0`/`no`/`off`/空串为假(大小写不敏感),
60
74
  给它别的值**当场报错**而不是猜。同一个 flag 写两次以后者为准(`--apply=false --apply` 会真发)。
package/dist/index.js CHANGED
@@ -3,6 +3,103 @@
3
3
  // src/commands.ts
4
4
  import { readFileSync as readFileSync2 } from "fs";
5
5
 
6
+ // src/flags.ts
7
+ var GLOBAL_FLAGS = ["shop", "actor", "apply", "help"];
8
+ var ENTITY_FLAGS = ["product", "order", "package", "video", "sku", "creator", "conversation", "category", "warehouse", "shop"];
9
+ var SYNONYMS = {
10
+ "limit": "page-size",
11
+ "size": "page-size",
12
+ "pagesize": "page-size",
13
+ "per-page": "page-size",
14
+ "start": "from",
15
+ "start-date": "from",
16
+ "begin": "from",
17
+ "since-date": "from",
18
+ "end": "to",
19
+ "end-date": "to",
20
+ "until": "to",
21
+ "qty": "quantity",
22
+ "shop-id": "shop",
23
+ "shopid": "shop",
24
+ "shop_id": "shop"
25
+ };
26
+ var DAY_MS = 864e5;
27
+ function editDistance(a, b) {
28
+ const dp = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
29
+ for (let j = 1; j <= b.length; j++) dp[0][j] = j;
30
+ for (let i = 1; i <= a.length; i++) {
31
+ for (let j = 1; j <= b.length; j++) {
32
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
33
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + 1);
34
+ }
35
+ }
36
+ return dp[a.length][b.length];
37
+ }
38
+ function suggestFlag(unknown, allowed, value, now = Date.now(), cmdName = "") {
39
+ const u = unknown.toLowerCase();
40
+ const dashed = u.replace(/_/g, "-");
41
+ const cands = [...allowed, ...GLOBAL_FLAGS.filter((g) => g !== "help")];
42
+ if (dashed === "days" && allowed.includes("from")) {
43
+ const n = Number(value);
44
+ const eg = Number.isFinite(n) && n > 0 ? new Date(now - n * DAY_MS).toISOString().slice(0, 10) : "<YYYY-MM-DD>";
45
+ const forms = allowed.includes("since") ? "YYYY-MM-DD \u6216 unix \u79D2" : "YYYY-MM-DD";
46
+ return `\u8FD9\u6761\u547D\u4EE4\u6CA1\u6709 --days\uFF1B\u8FD1 N \u5929\u7528\u8D77\u70B9\u8868\u793A\uFF0C\u5982 --from ${eg}\uFF08\u8D77\u6B62\u7528 --from / --to\uFF0C\u53D6 ${forms}\uFF09`;
47
+ }
48
+ if (dashed === "dry-run") {
49
+ return "\u4E0D\u9700\u8981 --dry-run\uFF1A\u5199\u547D\u4EE4\u9ED8\u8BA4\u5C31\u53EA\u9884\u89C8\u3001\u4E0D\u52A0 --apply \u4E0D\u4F1A\u771F\u53D1\uFF1B\u8BFB\u547D\u4EE4\u672C\u6765\u5C31\u4E0D\u5199\u3002\u53BB\u6389\u5B83\u5373\u53EF";
50
+ }
51
+ const syn = SYNONYMS[dashed] ?? SYNONYMS[u];
52
+ if (syn && cands.includes(syn)) return `--${syn}`;
53
+ if (dashed !== unknown && cands.includes(dashed)) return `--${dashed}`;
54
+ const base = dashed.replace(/-?ids?$/, "");
55
+ if (base && base !== dashed && ENTITY_FLAGS.includes(base) && cands.includes(base)) return `--${base}`;
56
+ if (dashed === "id") {
57
+ if (/\b(list|search|recommend)\b/.test(cmdName)) return null;
58
+ const ents = allowed.filter((f) => ENTITY_FLAGS.includes(f));
59
+ if (ents.length === 1) return `--${ents[0]}`;
60
+ return null;
61
+ }
62
+ let best = null;
63
+ let bestD = Infinity;
64
+ for (const f of cands) {
65
+ const d = editDistance(dashed, f);
66
+ if (d < bestD) {
67
+ best = f;
68
+ bestD = d;
69
+ }
70
+ }
71
+ const shortest = Math.min(dashed.length, (best ?? "").length);
72
+ const limit = shortest <= 3 ? 0 : shortest <= 6 ? 1 : 2;
73
+ const NEG = /^(un|dis|non|in)/;
74
+ if (best && best.replace(NEG, "") === dashed.replace(NEG, "") && best !== dashed) return null;
75
+ if (best && bestD <= limit) return `--${best}`;
76
+ return null;
77
+ }
78
+ function checkFlags(cmdName, args2, allowed, rest = [], now = Date.now(), noShop = false) {
79
+ const known = /* @__PURE__ */ new Set([...allowed, ...GLOBAL_FLAGS]);
80
+ const unknown = Object.keys(args2).filter((k) => !known.has(k));
81
+ if (unknown.length === 0 && rest.length === 0) return null;
82
+ const parts = [];
83
+ for (const k of unknown) {
84
+ if (k === "") {
85
+ parts.push(`\u4E0D\u8BA4 --=${args2[k]}\uFF08\u6CA1\u6709\u53C2\u6570\u540D\uFF09`);
86
+ continue;
87
+ }
88
+ const hint = suggestFlag(k, allowed, args2[k], now, cmdName);
89
+ parts.push(!hint ? `\u4E0D\u8BA4 --${k}` : hint.startsWith("--") ? `\u4E0D\u8BA4 --${k}\uFF08\u4F60\u662F\u60F3\u7528 ${hint} \u5417\uFF1F\uFF09` : `\u4E0D\u8BA4 --${k}\uFF08${hint}\uFF09`);
90
+ }
91
+ if (rest.length > 0) {
92
+ parts.push(`\u591A\u4F59\u7684\u4F4D\u7F6E\u53C2\u6570 ${rest.map((r) => JSON.stringify(r)).join(" ")}\uFF08\u672C CLI \u7684\u53C2\u6570\u90FD\u7528 --\u957F\u53C2\u6570 \u4F20\uFF0C\u4E0D\u6536\u4F4D\u7F6E\u53C2\u6570\u548C -x \u77ED\u53C2\u6570\uFF1B\u503C\u91CC\u6709\u7A7A\u683C\u5C31\u7ED9\u6574\u4E2A\u503C\u52A0\u5F15\u53F7\uFF0C\u5982 --title "Flash Sale"\uFF09`);
93
+ }
94
+ const allowedText = allowed.length ? allowed.map((f) => `--${f}`).join(" ") : "\uFF08\u6CA1\u6709\u4E13\u5C5E\u53C2\u6570\uFF09";
95
+ return {
96
+ unknown_flags: unknown,
97
+ extra_positional: [...rest],
98
+ allowed_flags: [...allowed],
99
+ message: `${cmdName}\uFF1A${parts.join("\uFF1B")}\u3002\u8FD9\u6761\u547D\u4EE4\u8BA4\u7684\u53C2\u6570\uFF1A${allowedText}\uFF08\u53E6\u6709\u5168\u5C40 ${noShop ? "--apply" : "--shop / --apply"}\uFF09\u3002\u26D4 \u6CA1\u53D1\u8BF7\u6C42 \u2014\u2014 \u4E0D\u8BA4\u7684\u53C2\u6570\u539F\u6765\u4F1A\u88AB\u9759\u9ED8\u4E22\u6389\u3001\u6309\u9ED8\u8BA4\u503C\u8DD1\u51FA\u4E00\u4EFD\u770B\u8D77\u6765\u6B63\u5E38\u7684\u7ED3\u679C\u3002`
100
+ };
101
+ }
102
+
6
103
  // src/client.ts
7
104
  import { readFileSync } from "fs";
8
105
  import { homedir } from "os";
@@ -33,7 +130,7 @@ function authHeaders() {
33
130
  return {};
34
131
  }
35
132
  function classify(status, body, shop) {
36
- const meta = { shop, tiktok_request_id: body?.request_id };
133
+ const meta = { shop, tiktok_request_id: body?.request_id, http_status: status };
37
134
  if (status === 0 || body?.code === 0) {
38
135
  return { ok: true, data: body.data, meta };
39
136
  }
@@ -209,9 +306,14 @@ function fillEmptyList(data, listKey) {
209
306
  if (!(listKey in d)) d[listKey] = [];
210
307
  }
211
308
  function commandUsage(cmd) {
212
- return `tiktok ${cmd.name} --shop <shopId> [--flags]
309
+ const flags = cmd.flags?.length ? cmd.flags.map((f) => `--${f}`).join(" ") : "\uFF08\u6CA1\u6709\u4E13\u5C5E\u53C2\u6570\uFF09";
310
+ const shop = cmd.noShop ? "" : " --shop <shopId>";
311
+ const globals = cmd.noShop ? "--apply" : "--shop / --apply";
312
+ return `tiktok ${cmd.name}${shop} [--flags]
213
313
 
214
314
  ${cmd.summary}
315
+
316
+ \u8BA4\u7684\u53C2\u6570\uFF1A${flags}\uFF08\u53E6\u6709\u5168\u5C40 ${globals}\uFF09\uFF1B\u4E0D\u8BA4\u7684\u4F1A\u5F53\u573A\u62A5\u9519\u3002
215
317
  `;
216
318
  }
217
319
  var today = () => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -222,6 +324,7 @@ var COMMANDS = [
222
324
  {
223
325
  name: "orders list",
224
326
  summary: "\u8BA2\u5355\u5217\u8868\uFF08\u9ED8\u8BA4\u8FD130\u5929\u3001\u6309\u521B\u5EFA\u65F6\u95F4\u5012\u5E8F\uFF1B--from/--to\uFF08unix\u79D2\u6216YYYY-MM-DD\uFF09--status --page-size --sort-order ASC/DESC --sort-field create_time/update_time\uFF09",
327
+ flags: ["from", "page-size", "page-token", "since", "sort-field", "sort-order", "status", "to"],
225
328
  listKey: "orders",
226
329
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/order/202309/orders/search", {
227
330
  query: {
@@ -243,12 +346,14 @@ var COMMANDS = [
243
346
  {
244
347
  name: "orders get",
245
348
  summary: "\u8BA2\u5355\u8BE6\u60C5\uFF08--id \u5FC5\u586B\uFF09",
349
+ flags: ["id"],
246
350
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/order/202309/orders/detail", { query: { ids: args2.id } })
247
351
  },
248
352
  // ── 售后(退货/取消)只读 ──
249
353
  {
250
354
  name: "orders cancellations",
251
355
  summary: "\u53D6\u6D88\u5355\u5217\u8868\uFF08--status \u53EF\u9009\uFF0C--page-size\uFF09",
356
+ flags: ["page-size", "status"],
252
357
  listKey: "cancellations",
253
358
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/return_refund/202309/cancellations/search", {
254
359
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -258,6 +363,7 @@ var COMMANDS = [
258
363
  {
259
364
  name: "orders returns",
260
365
  summary: "\u9000\u8D27\u5355\u5217\u8868\uFF08--status \u53EF\u9009\uFF0C--page-size\uFF09",
366
+ flags: ["page-size", "status"],
261
367
  listKey: "return_orders",
262
368
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/return_refund/202309/returns/search", {
263
369
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -267,16 +373,19 @@ var COMMANDS = [
267
373
  {
268
374
  name: "orders return-records",
269
375
  summary: "\u9000\u8D27\u5355\u5904\u7406\u8BB0\u5F55\uFF08--id return_id\uFF09",
376
+ flags: ["id"],
270
377
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/return_refund/202309/returns/${args2.id}/records`, {})
271
378
  },
272
379
  {
273
380
  name: "orders aftersale-eligibility",
274
381
  summary: "\u8BA2\u5355\u552E\u540E\u8D44\u683C\uFF08--order id\uFF09",
382
+ flags: ["order"],
275
383
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/return_refund/202512/orders/${args2.order}/aftersale_eligibility`, {})
276
384
  },
277
385
  {
278
386
  name: "orders reject-reasons",
279
387
  summary: "\u9A73\u56DE\u53D6\u6D88/\u9000\u8D27\u7684\u5408\u6CD5\u7406\u7531\uFF08--id return_or_cancel_id\uFF09",
388
+ flags: ["id"],
280
389
  listKey: "reasons",
281
390
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/return_refund/202309/reject_reasons", {
282
391
  query: { return_or_cancel_id: args2.id }
@@ -285,6 +394,7 @@ var COMMANDS = [
285
394
  {
286
395
  name: "orders refund-calculate",
287
396
  summary: "\u8BA1\u7B97\u9000\u6B3E\u989D\uFF08--body-file calc.json\uFF09",
397
+ flags: ["body-file"],
288
398
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/return_refund/202309/refunds/calculate", {
289
399
  body: readBodyFile(args2["body-file"])
290
400
  })
@@ -293,11 +403,13 @@ var COMMANDS = [
293
403
  {
294
404
  name: "orders tracking",
295
405
  summary: "\u8BA2\u5355\u7269\u6D41\u8DDF\u8E2A\uFF08--order id\uFF09",
406
+ flags: ["order"],
296
407
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/fulfillment/202309/orders/${args2.order}/tracking`, {})
297
408
  },
298
409
  {
299
410
  name: "orders shipping-services",
300
411
  summary: "\u8BA2\u5355\u53EF\u7528\u627F\u8FD0/\u914D\u9001\u670D\u52A1\uFF08--order id\uFF09",
412
+ flags: ["order"],
301
413
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/fulfillment/202309/orders/${args2.order}/shipping_services/query`, {
302
414
  body: {}
303
415
  })
@@ -305,6 +417,7 @@ var COMMANDS = [
305
417
  {
306
418
  name: "packages search",
307
419
  summary: "\u5305\u88F9\u5217\u8868\uFF08--page-size\uFF09",
420
+ flags: ["page-size"],
308
421
  listKey: "packages",
309
422
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/fulfillment/202309/packages/search", {
310
423
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -314,17 +427,20 @@ var COMMANDS = [
314
427
  {
315
428
  name: "packages get",
316
429
  summary: "\u5305\u88F9\u8BE6\u60C5\uFF08--package id\uFF09",
430
+ flags: ["package"],
317
431
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/fulfillment/202309/packages/${args2.package}`, {})
318
432
  },
319
433
  {
320
434
  name: "packages documents",
321
435
  summary: "\u5305\u88F9\u7269\u6D41\u9762\u5355\uFF08--package id\uFF09",
436
+ flags: ["package"],
322
437
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/fulfillment/202309/packages/${args2.package}/shipping_documents`, {})
323
438
  },
324
439
  // ── 售后审批 / 发货 写(不可逆,默认 dry-run,--apply 才提交)──
325
440
  {
326
441
  name: "orders cancel-approve",
327
442
  summary: "\u6279\u51C6\u53D6\u6D88\u5355\uFF08--id cancel_id\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u6279\u3002\u4E0D\u53EF\u9006",
443
+ flags: ["id"],
328
444
  write: true,
329
445
  preview: ({ args: args2 }) => [{
330
446
  step: "\u6279\u51C6\u53D6\u6D88\u5355",
@@ -337,6 +453,7 @@ var COMMANDS = [
337
453
  {
338
454
  name: "orders cancel-reject",
339
455
  summary: "\u9A73\u56DE\u53D6\u6D88\u5355\uFF08--id --reason\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u9A73\u3002\u4E0D\u53EF\u9006",
456
+ flags: ["id", "reason"],
340
457
  write: true,
341
458
  preview: ({ args: args2 }) => [{
342
459
  step: "\u9A73\u56DE\u53D6\u6D88\u5355",
@@ -354,6 +471,7 @@ var COMMANDS = [
354
471
  {
355
472
  name: "orders return-approve",
356
473
  summary: "\u6279\u51C6\u9000\u8D27\u5355\uFF08--id return_id\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u6279\u3002\u4E0D\u53EF\u9006",
474
+ flags: ["id"],
357
475
  write: true,
358
476
  preview: ({ args: args2 }) => [{
359
477
  step: "\u6279\u51C6\u9000\u8D27\u5355",
@@ -366,6 +484,7 @@ var COMMANDS = [
366
484
  {
367
485
  name: "orders return-reject",
368
486
  summary: "\u9A73\u56DE\u9000\u8D27\u5355\uFF08--id --reason\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u9A73\u3002\u4E0D\u53EF\u9006",
487
+ flags: ["id", "reason"],
369
488
  write: true,
370
489
  preview: ({ args: args2 }) => [{
371
490
  step: "\u9A73\u56DE\u9000\u8D27\u5355",
@@ -383,6 +502,7 @@ var COMMANDS = [
383
502
  {
384
503
  name: "orders create-package",
385
504
  summary: "\u4E3A\u8BA2\u5355\u5EFA\u5305\u88F9\uFF08--order id --body-file pkg.json\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u5EFA\u3002\u4EC5\u76F4\u53D1\u5546\u5BB6\uFF08\u7269\u6D41\u9ED8\u8BA4 ERP \u7BA1\uFF09",
505
+ flags: ["body-file", "order"],
386
506
  write: true,
387
507
  preview: ({ args: args2 }) => [{
388
508
  step: "\u5EFA\u5305\u88F9",
@@ -400,6 +520,7 @@ var COMMANDS = [
400
520
  {
401
521
  name: "packages ship",
402
522
  summary: "\u53D1\u8D27\uFF08--package id --body-file ship.json\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1\u3002\u4E0D\u53EF\u9006\uFF0C\u4EC5\u76F4\u53D1\u5546\u5BB6",
523
+ flags: ["body-file", "package"],
403
524
  write: true,
404
525
  preview: ({ args: args2 }) => [{
405
526
  step: "\u53D1\u8D27",
@@ -418,6 +539,7 @@ var COMMANDS = [
418
539
  {
419
540
  name: "events",
420
541
  summary: "\u67E5\u5E97\u94FA webhook \u4E8B\u4EF6\uFF08--type \u9017\u53F7\u5206\u9694 --since 1d/unix --unprocessed --limit\uFF09",
542
+ flags: ["limit", "since", "type", "unprocessed"],
421
543
  run: ({ shop, args: args2 }) => {
422
544
  const q = {};
423
545
  if (args2.type) q.type = args2.type;
@@ -430,6 +552,7 @@ var COMMANDS = [
430
552
  {
431
553
  name: "events ack",
432
554
  summary: "\u6807\u4E8B\u4EF6\u5DF2\u5904\u7406\uFF08--ids id1,id2 \u6216 --before <ts>\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u6807",
555
+ flags: ["before", "ids"],
433
556
  write: true,
434
557
  preview: ({ args: args2 }) => [{
435
558
  step: "\u6807\u4E8B\u4EF6\u5DF2\u5904\u7406",
@@ -442,6 +565,7 @@ var COMMANDS = [
442
565
  {
443
566
  name: "analytics shop",
444
567
  summary: "\u5E97\u94FA\u7ECF\u8425\u6570\u636E\uFF08--from --to\uFF0C\u9ED8\u8BA4\u8FD130\u5929\uFF09",
568
+ flags: ["from", "granularity", "to"],
445
569
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop/performance", {
446
570
  query: {
447
571
  start_date_ge: args2.from ?? daysAgo(30),
@@ -453,6 +577,7 @@ var COMMANDS = [
453
577
  {
454
578
  name: "analytics products",
455
579
  summary: "\u5546\u54C1\u7EF4\u5EA6\u7ECF\u8425\u6570\u636E\uFF08\u6309 GMV \u964D\u5E8F\uFF09",
580
+ flags: ["from", "page-size", "to"],
456
581
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_products/performance", {
457
582
  query: {
458
583
  start_date_ge: args2.from ?? daysAgo(30),
@@ -466,6 +591,7 @@ var COMMANDS = [
466
591
  {
467
592
  name: "analytics videos",
468
593
  summary: "\u5185\u5BB9\u89C6\u9891\u7EF4\u5EA6\u7ECF\u8425\u6570\u636E\uFF08shop_videos\uFF0C--from --to\uFF0C\u9ED8\u8BA4\u8FD130\u5929\uFF0C\u6309 GMV \u964D\u5E8F\uFF1B--sort-field --sort-order --page-size --page-token\uFF09",
594
+ flags: ["from", "page-size", "page-token", "sort-field", "sort-order", "to"],
469
595
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_videos/performance", {
470
596
  query: {
471
597
  start_date_ge: args2.from ?? daysAgo(30),
@@ -480,6 +606,7 @@ var COMMANDS = [
480
606
  {
481
607
  name: "analytics videos-overview",
482
608
  summary: "\u5185\u5BB9\u89C6\u9891\u6574\u4F53\u6982\u89C8\uFF08shop_videos overview\uFF1B--from --to --granularity\uFF09",
609
+ flags: ["from", "granularity", "to"],
483
610
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_videos/overview_performance", {
484
611
  query: {
485
612
  start_date_ge: args2.from ?? daysAgo(30),
@@ -491,6 +618,7 @@ var COMMANDS = [
491
618
  {
492
619
  name: "analytics video",
493
620
  summary: "\u5355\u6761\u89C6\u9891\u7ECF\u8425\u660E\u7EC6\uFF08--video video_id \u5FC5\u586B\uFF1B--from --to\uFF09",
621
+ flags: ["from", "to", "video"],
494
622
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_videos/${args2.video}/performance`, {
495
623
  query: {
496
624
  start_date_ge: args2.from ?? daysAgo(30),
@@ -501,6 +629,7 @@ var COMMANDS = [
501
629
  {
502
630
  name: "analytics video-products",
503
631
  summary: "\u5355\u6761\u89C6\u9891\u5E26\u8D27\u5546\u54C1\u8868\u73B0\uFF08--video video_id \u5FC5\u586B\uFF1B--from --to --page-size\uFF09",
632
+ flags: ["from", "page-size", "to", "video"],
504
633
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_videos/${args2.video}/products/performance`, {
505
634
  query: {
506
635
  start_date_ge: args2.from ?? daysAgo(30),
@@ -512,6 +641,7 @@ var COMMANDS = [
512
641
  {
513
642
  name: "analytics lives",
514
643
  summary: "\u76F4\u64AD\u7EF4\u5EA6\u7ECF\u8425\u6570\u636E\uFF08shop_lives\uFF0C--from --to\uFF0C\u9ED8\u8BA4\u8FD130\u5929\uFF0C\u6309 GMV \u964D\u5E8F\uFF1B--sort-field --sort-order --page-size --page-token\uFF09",
644
+ flags: ["from", "page-size", "page-token", "sort-field", "sort-order", "to"],
515
645
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_lives/performance", {
516
646
  query: {
517
647
  start_date_ge: args2.from ?? daysAgo(30),
@@ -526,6 +656,7 @@ var COMMANDS = [
526
656
  {
527
657
  name: "analytics lives-overview",
528
658
  summary: "\u76F4\u64AD\u6574\u4F53\u6982\u89C8\uFF08shop_lives overview\uFF1B--from --to --granularity\uFF09",
659
+ flags: ["from", "granularity", "to"],
529
660
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_lives/overview_performance", {
530
661
  query: {
531
662
  start_date_ge: args2.from ?? daysAgo(30),
@@ -537,6 +668,7 @@ var COMMANDS = [
537
668
  {
538
669
  name: "analytics skus",
539
670
  summary: "SKU \u7EF4\u5EA6\u7ECF\u8425\u6570\u636E\uFF08shop_skus\uFF0C--from --to\uFF0C\u9ED8\u8BA4\u8FD130\u5929\uFF0C\u6309 GMV \u964D\u5E8F\uFF1B--sort-field --sort-order --page-size --page-token\uFF09",
671
+ flags: ["from", "page-size", "page-token", "sort-field", "sort-order", "to"],
540
672
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_skus/performance", {
541
673
  query: {
542
674
  start_date_ge: args2.from ?? daysAgo(30),
@@ -551,6 +683,7 @@ var COMMANDS = [
551
683
  {
552
684
  name: "analytics sku",
553
685
  summary: "\u5355\u4E2A SKU \u7ECF\u8425\u660E\u7EC6\uFF08--sku sku_id \u5FC5\u586B\uFF1B--from --to --granularity\uFF09",
686
+ flags: ["from", "granularity", "sku", "to"],
554
687
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_skus/${args2.sku}/performance`, {
555
688
  query: {
556
689
  start_date_ge: args2.from ?? daysAgo(30),
@@ -562,6 +695,7 @@ var COMMANDS = [
562
695
  {
563
696
  name: "analytics product",
564
697
  summary: "\u5355\u4E2A\u5546\u54C1\u7ECF\u8425\u660E\u7EC6\uFF08--product product_id \u5FC5\u586B\uFF1B--from --to --granularity\uFF09",
698
+ flags: ["from", "granularity", "product", "to"],
565
699
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_products/${args2.product}/performance`, {
566
700
  query: {
567
701
  start_date_ge: args2.from ?? daysAgo(30),
@@ -573,11 +707,13 @@ var COMMANDS = [
573
707
  {
574
708
  name: "analytics shop-hourly",
575
709
  summary: "\u5E97\u94FA\u67D0\u5929\u5206\u65F6\uFF08\u9010\u5C0F\u65F6\uFF09\u7ECF\u8425\u6570\u636E\uFF08--date YYYY-MM-DD\uFF0C\u9ED8\u8BA4\u6628\u5929\uFF09",
710
+ flags: ["date"],
576
711
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202510/shop/performance/${args2.date ?? daysAgo(1)}/performance_per_hour`, {})
577
712
  },
578
713
  {
579
714
  name: "products list",
580
715
  summary: "\u5546\u54C1\u5217\u8868\uFF08--status ACTIVATE \u53EA\u53D6\u5728\u552E\uFF1B\u4E0D\u4F20\u8FD4\u6240\u6709\u542B DELETED\uFF0C\u5199\u64CD\u4F5C\u524D\u52A1\u5FC5\u7B5B ACTIVATE\uFF09",
716
+ flags: ["page-size", "status"],
581
717
  listKey: "products",
582
718
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/products/search", {
583
719
  query: { page_size: args2["page-size"] ?? "20" },
@@ -587,11 +723,13 @@ var COMMANDS = [
587
723
  {
588
724
  name: "products get",
589
725
  summary: "\u5355\u4E2A\u5546\u54C1\u5B8C\u6574\u8BE6\u60C5\uFF08--product id\uFF1B\u9010\u5B57\u6BB5\u56DE\u8BFB\u7528\uFF1A\u6807\u9898/\u63CF\u8FF0/\u56FE/SKU/\u4EF7/\u5E93\u5B58/\u91CD\u91CF/\u5C3A\u5BF8/\u5C5E\u6027\uFF09",
726
+ flags: ["product"],
590
727
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/product/202309/products/${args2.product}`, {})
591
728
  },
592
729
  {
593
730
  name: "products diagnose",
594
731
  summary: "\u5546\u54C1\u5B98\u65B9\u95EE\u9898\u8BCA\u65AD\uFF08--product \u9017\u53F7\u5206\u9694\u672C\u5E97\u81EA\u6709\u5546\u54C1 id\uFF09",
732
+ flags: ["product"],
595
733
  listKey: "products",
596
734
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/product/202405/products/diagnoses", {
597
735
  query: { product_ids: String(args2.product ?? "") }
@@ -600,6 +738,7 @@ var COMMANDS = [
600
738
  {
601
739
  name: "promotions list",
602
740
  summary: "\u4FC3\u9500\u6D3B\u52A8\u5217\u8868\uFF08--status \u7701\u7565=\u5168\u90E8\u72B6\u6001\uFF5CONGOING/NOT_START/EXPIRED/DEACTIVATED\uFF0C\u53D6\u503C\u533A\u5206\u5927\u5C0F\u5199\u4E14\u4E0D trim\uFF0C\u672A\u77E5\u503C\u672C\u5730\u62D2\u3001\u5E73\u53F0\u4F1A\u9759\u9ED8\u8FD4\u56DE\u5168\u90E8\uFF1B--force-status \u8D8A\u8FC7\u672C\u5730\u6821\u9A8C\uFF1B--page-size \u9ED8\u8BA420\uFF1B--page-token \u7FFB\u9875\uFF0C\u672B\u9875\u65E0 next_page_token\uFF09",
741
+ flags: ["force-status", "page-size", "page-token", "status"],
603
742
  listKey: "activities",
604
743
  run: ({ shop, args: args2 }) => {
605
744
  const st = args2.status ? String(args2.status) : "";
@@ -631,6 +770,7 @@ var COMMANDS = [
631
770
  {
632
771
  name: "finance statements",
633
772
  summary: "\u7ED3\u7B97\u5355\u5217\u8868",
773
+ flags: ["page-size", "sort-field", "sort-order"],
634
774
  listKey: "statements",
635
775
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/finance/202309/statements", {
636
776
  query: {
@@ -644,22 +784,26 @@ var COMMANDS = [
644
784
  {
645
785
  name: "seller status",
646
786
  summary: "\u8D26\u53F7/\u5E97\u94FA\u72B6\u6001\uFF08seller_status/shop_statuses/tax_form_status\uFF09",
787
+ flags: [],
647
788
  run: ({ shop }) => proxy("GET", shop, "/seller/202508/status", {})
648
789
  },
649
790
  {
650
791
  name: "seller permissions",
651
792
  summary: "\u5DF2\u6388\u6743\u6743\u9650\u5217\u8868",
793
+ flags: [],
652
794
  run: ({ shop }) => proxy("GET", shop, "/seller/202309/permissions", {})
653
795
  },
654
796
  // ===== 建品前置只读辅助命令 =====
655
797
  {
656
798
  name: "products prerequisites",
657
799
  summary: "\u5EFA\u54C1\u524D\u7F6E\u68C0\u67E5",
800
+ flags: [],
658
801
  run: ({ shop }) => proxy("GET", shop, "/product/202312/prerequisites", {})
659
802
  },
660
803
  {
661
804
  name: "categories recommend",
662
805
  summary: "\u7C7B\u76EE\u63A8\u8350\uFF08--title [--desc]\uFF09",
806
+ flags: ["desc", "title"],
663
807
  listKey: "categories",
664
808
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/categories/recommend", {
665
809
  body: {
@@ -671,6 +815,7 @@ var COMMANDS = [
671
815
  {
672
816
  name: "categories tree",
673
817
  summary: "\u7C7B\u76EE\u6811\uFF08--version v2\uFF09",
818
+ flags: ["version"],
674
819
  listKey: "categories",
675
820
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/product/202309/categories", {
676
821
  query: { category_version: args2.version ?? "v2" }
@@ -679,6 +824,7 @@ var COMMANDS = [
679
824
  {
680
825
  name: "categories attributes",
681
826
  summary: "\u7C7B\u76EE\u5C5E\u6027\uFF08--category\uFF09",
827
+ flags: ["category", "version"],
682
828
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/product/202309/categories/${args2.category}/attributes`, {
683
829
  query: { category_version: args2.version ?? "v2" }
684
830
  })
@@ -686,6 +832,7 @@ var COMMANDS = [
686
832
  {
687
833
  name: "categories rules",
688
834
  summary: "\u7C7B\u76EE\u89C4\u5219\uFF08--category\uFF09",
835
+ flags: ["category", "version"],
689
836
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/product/202309/categories/${args2.category}/rules`, {
690
837
  query: { category_version: args2.version ?? "v2" }
691
838
  })
@@ -693,6 +840,7 @@ var COMMANDS = [
693
840
  {
694
841
  name: "brands list",
695
842
  summary: "\u54C1\u724C\u5217\u8868\uFF08[--category] [--name]\uFF09",
843
+ flags: ["category", "name", "page-size"],
696
844
  listKey: "brands",
697
845
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/product/202309/brands", {
698
846
  query: {
@@ -705,6 +853,7 @@ var COMMANDS = [
705
853
  {
706
854
  name: "brands recommend",
707
855
  summary: "\u54C1\u724C\u63A8\u8350\uFF08--title\uFF09",
856
+ flags: ["title"],
708
857
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/brands/recommend", {
709
858
  body: { product_title: args2.title }
710
859
  })
@@ -712,18 +861,21 @@ var COMMANDS = [
712
861
  {
713
862
  name: "warehouses list",
714
863
  summary: "\u4ED3\u5E93\u5217\u8868\uFF08\u8DE8\u5883\u8BA4 sub_type CB_*\uFF09",
864
+ flags: [],
715
865
  listKey: "warehouses",
716
866
  run: ({ shop }) => proxy("GET", shop, "/logistics/202309/warehouses", {})
717
867
  },
718
868
  {
719
869
  name: "shipping-templates list",
720
870
  summary: "\u8FD0\u8D39\u6A21\u677F\u5217\u8868",
871
+ flags: [],
721
872
  listKey: "templates",
722
873
  run: ({ shop }) => proxy("GET", shop, "/logistics/202510/seller_templates", {})
723
874
  },
724
875
  {
725
876
  name: "delivery-options",
726
877
  summary: "\u4ED3\u5E93\u4EA4\u4ED8\u65B9\u5F0F\uFF08--warehouse\uFF09",
878
+ flags: ["warehouse"],
727
879
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/logistics/202309/warehouses/${args2.warehouse}/delivery_options`, {
728
880
  query: { scope: "PRODUCT" }
729
881
  })
@@ -732,6 +884,7 @@ var COMMANDS = [
732
884
  {
733
885
  name: "products set-price",
734
886
  summary: "\u6539 SKU \u552E\u4EF7\uFF08--product --sku --price [--currency MYR]\uFF09\u3002\u5199\u64CD\u4F5C,\u9ED8\u8BA4 dry-run",
887
+ flags: ["currency", "price", "product", "sku"],
735
888
  write: true,
736
889
  preview: ({ args: args2 }) => [{
737
890
  step: "\u6539\u4EF7",
@@ -745,6 +898,7 @@ var COMMANDS = [
745
898
  {
746
899
  name: "promotions create-flashsale",
747
900
  summary: "\u5EFA\u79D2\u6740\u6D3B\u52A8\uFF08--title --product --sku --price [--limit] [--hours 48]\uFF09\u3002\u4E24\u6B65:\u5EFA\u58F3+\u52A0\u54C1",
901
+ flags: ["hours", "limit", "per-user", "price", "product", "sku", "start-in-min", "title"],
748
902
  write: true,
749
903
  preview: ({ args: args2 }) => {
750
904
  const { begin, end } = flashWindow(args2);
@@ -766,9 +920,77 @@ var COMMANDS = [
766
920
  },
767
921
  readback: ({ shop, args: args2 }) => proxy("POST", shop, "/promotion/202309/activities/search", { body: { status: "NOT_START", page_size: 20 } })
768
922
  },
923
+ {
924
+ name: "promotions create-discount",
925
+ summary: "\u5EFA\u5546\u54C1\u6298\u6263\u6D3B\u52A8 FIXED_PRICE\uFF08--title [--product] --price <skuId>:<\u4E00\u53E3\u4EF7>|<productId>/<skuId>:<\u4E00\u53E3\u4EF7>[,...] [--days 180]\uFF09\u3002\u4E24\u6B65:\u5EFA\u58F3+\u52A0\u54C1\uFF0C\u53EF\u591A\u5546\u54C1",
926
+ flags: ["days", "price", "product", "title"],
927
+ write: true,
928
+ validate: validateCreateDiscount,
929
+ preview: ({ args: args2 }) => {
930
+ const { begin, end } = discountWindow(args2);
931
+ return [
932
+ { step: "1/2 \u5EFA\u6D3B\u52A8\u58F3", method: "POST", path: "/promotion/202309/activities", body: discountActivityBody(args2, begin, end) },
933
+ { step: "2/2 \u52A0\u5546\u54C1+\u4E00\u53E3\u4EF7", method: "PUT", path: "/promotion/202309/activities/{activity_id}/products", body: discountProductsBody(args2), human: discountHuman(args2) }
934
+ ];
935
+ },
936
+ run: async ({ shop, args: args2 }) => {
937
+ const { begin, end } = discountWindow(args2);
938
+ const submitted = submittedSkus(args2);
939
+ const created = await proxy("POST", shop, "/promotion/202309/activities", { body: discountActivityBody(args2, begin, end) });
940
+ if (!created.ok) {
941
+ const err = created.error;
942
+ switch (createFailureVerdict(created)) {
943
+ case "maybe_landed":
944
+ return { ...created, error: { ...err, message: `${err.message} ${DISCOUNT_TEXT.maybeLanded}` } };
945
+ case "dup":
946
+ return { ...created, error: { ...err, message: `${err.message} ${await dupSelfCheck(shop, args2.title, submitted)}` } };
947
+ case "rejected":
948
+ return created;
949
+ }
950
+ }
951
+ const activityId = created.data?.activity_id;
952
+ if (!activityId) {
953
+ return {
954
+ ok: false,
955
+ error: { code: "TIKTOK_ERROR", message: DISCOUNT_TEXT.noCreateId(args2.title), retryable: false },
956
+ meta: created.meta
957
+ };
958
+ }
959
+ const withProducts = await proxy("PUT", shop, `/promotion/202309/activities/${activityId}/products`, {
960
+ body: { ...discountProductsBody(args2), activity_id: activityId }
961
+ // F5:body 里要重复传
962
+ });
963
+ const rb = await proxy("GET", shop, `/promotion/202309/activities/${activityId}`, {});
964
+ const readback = rb.ok ? rb.data : { error: rb.error };
965
+ if (!withProducts.ok) {
966
+ const { retry_after_s: _dropped, ...err } = withProducts.error;
967
+ let note;
968
+ const c = rb.ok ? compareReadback(submitted, rb.data) : null;
969
+ if (!c) note = DISCOUNT_TEXT.addFailReadbackFailed(shop, activityId);
970
+ else {
971
+ if (c.missing.length === 0 && c.mismatched.length === 0) note = DISCOUNT_TEXT.addFailAllAttached(submitted.length, activityId);
972
+ else if (c.missing.length === submitted.length && c.productCount === 0) note = DISCOUNT_TEXT.addFailNoneAttached(shop, activityId);
973
+ else note = DISCOUNT_TEXT.addFailPartial(problemsText(c), shop, activityId);
974
+ }
975
+ return { ok: false, error: { ...err, retryable: false }, data: { activity_id: activityId, readback, note }, meta: withProducts.meta };
976
+ }
977
+ const ours = [];
978
+ const diff = rb.ok ? compareReadback(submitted, rb.data) : null;
979
+ if (!diff) ours.push(DISCOUNT_TEXT.readbackFailed(activityId));
980
+ else {
981
+ const problems = problemsText(diff);
982
+ if (problems) ours.push(DISCOUNT_TEXT.readbackDiffers(activityId, problems));
983
+ }
984
+ const putData = withProducts.data ?? {};
985
+ const data = { ...putData, activity_id: activityId, readback };
986
+ if (ours.length > 0) data.warnings = mergeWarnings(putData.warnings, ours);
987
+ return { ok: true, data, meta: withProducts.meta };
988
+ }
989
+ },
769
990
  {
770
991
  name: "promotions deactivate",
771
992
  summary: "\u7EC8\u6B62/\u505C\u7528\u4FC3\u9500\u6D3B\u52A8\uFF08--id \u5FC5\u586B\uFF09\u3002\u4E0D\u53EF\u9006",
993
+ flags: ["id"],
772
994
  write: true,
773
995
  preview: ({ args: args2 }) => [{ step: "\u7EC8\u6B62\u6D3B\u52A8", method: "POST", path: `/promotion/202309/activities/${args2.id}/deactivate`, body: {} }],
774
996
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/promotion/202309/activities/${args2.id}/deactivate`, { body: {} })
@@ -776,6 +998,7 @@ var COMMANDS = [
776
998
  {
777
999
  name: "products set-inventory",
778
1000
  summary: "\u6539 SKU \u5E93\u5B58\uFF08--product --sku --warehouse --quantity\uFF09\u3002\u5199\u64CD\u4F5C,\u9ED8\u8BA4 dry-run",
1001
+ flags: ["product", "quantity", "sku", "warehouse"],
779
1002
  write: true,
780
1003
  preview: ({ args: args2 }) => [{
781
1004
  step: "\u6539\u5E93\u5B58",
@@ -789,6 +1012,7 @@ var COMMANDS = [
789
1012
  {
790
1013
  name: "products create",
791
1014
  summary: "\u5EFA\u54C1\uFF08--body-file [--apply]\uFF09\u3002dry-run \u8DD1 listing_check \u9884\u68C0\uFF1B\u5199\u64CD\u4F5C\u3002\u26A0\uFE0F --apply \u5373\u5EFA\u5B8C\u76F4\u63A5\u4E0A\u67B6\u9001\u5BA1\uFF1B\u7528\u6237\u8981\u5B58\u8349\u7A3F\u5C31\u505C\u624B\u95EE\u7528\u6237\uFF08#1958\uFF09",
1015
+ flags: ["body-file"],
792
1016
  write: true,
793
1017
  preview: () => [{ step: "listing_check \u9884\u68C0 \u2192 \u5EFA\u54C1", method: "POST", path: "/product/202309/products/listing_check", body: void 0 }],
794
1018
  precheck: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/products/listing_check", { body: readBodyFile(args2["body-file"]) }),
@@ -803,6 +1027,7 @@ var COMMANDS = [
803
1027
  {
804
1028
  name: "products add-variant",
805
1029
  summary: "\u7ED9\u5546\u54C1\u52A0\u89C4\u683C SKU\uFF08--product --body-file skus-delta.json [--apply]\uFF09\u3002\u26A0\uFE0F --apply \u5373\u9001\u5BA1\u4E0A\u67B6\uFF1B\u7528\u6237\u8981\u5B58\u8349\u7A3F\u5C31\u505C\u624B\u95EE\u7528\u6237\uFF08#1958\uFF09",
1030
+ flags: ["body-file", "product"],
806
1031
  write: true,
807
1032
  preview: () => [{ step: "\u8BFB\u73B0\u6709 skus \u2192 append \u2192 partial_edit\u3002\u26A0\uFE0F \u9ED8\u8BA4 save_mode=LISTING\uFF1A--apply \u5373\u9001\u5BA1\u4E0A\u67B6\uFF08DRAFT \u5546\u54C1\u4F1A\u88AB\u63D0\u4EA4\uFF09\uFF1B\u672C\u547D\u4EE4\u81EA\u6784 body\uFF0Cbody-file \u91CC\u7684 save_mode \u4E0D\u900F\u4F20\u3001\u5199\u4E86\u4E5F\u65E0\u6548\u3002\u82E5\u7528\u6237\u8981\u6C42\u5B58\u8349\u7A3F/\u522B\u63D0\u4EA4 \u2192 \u4E0D\u8981 --apply\uFF0C\u505C\u624B\u95EE\u7528\u6237\uFF08#1958\uFF09", method: "POST", path: "/product/202309/products/{id}/partial_edit" }],
808
1033
  run: async ({ shop, args: args2 }) => {
@@ -816,6 +1041,7 @@ var COMMANDS = [
816
1041
  {
817
1042
  name: "products replace-images",
818
1043
  summary: "\u6574\u7EC4\u6362\u4E3B\u56FE\uFF08--product --body-file images.json [--apply]\uFF09\u3002\u26A0\uFE0F --apply \u5373\u9001\u5BA1\u4E0A\u67B6\uFF1B\u7528\u6237\u8981\u5B58\u8349\u7A3F\u5C31\u505C\u624B\u95EE\u7528\u6237\uFF08#1958\uFF09",
1044
+ flags: ["body-file", "product"],
819
1045
  write: true,
820
1046
  preview: () => [{ step: "main_images \u6574\u7EC4\u66FF\u6362 \u2192 partial_edit\u3002\u26A0\uFE0F \u9ED8\u8BA4 save_mode=LISTING\uFF1A--apply \u5373\u9001\u5BA1\u4E0A\u67B6\uFF08DRAFT \u5546\u54C1\u4F1A\u88AB\u63D0\u4EA4\uFF09\uFF1B\u672C\u547D\u4EE4\u81EA\u6784 body\uFF0Cbody-file \u91CC\u7684 save_mode \u4E0D\u900F\u4F20\u3001\u5199\u4E86\u4E5F\u65E0\u6548\u3002\u82E5\u7528\u6237\u8981\u6C42\u5B58\u8349\u7A3F/\u522B\u63D0\u4EA4 \u2192 \u4E0D\u8981 --apply\uFF0C\u505C\u624B\u95EE\u7528\u6237\uFF08#1958\uFF09", method: "POST", path: "/product/202309/products/{id}/partial_edit" }],
821
1047
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/product/202309/products/${args2.product}/partial_edit`, { body: { main_images: readBodyFile(args2["body-file"]).main_images } }),
@@ -824,6 +1050,7 @@ var COMMANDS = [
824
1050
  {
825
1051
  name: "products partial-edit",
826
1052
  summary: "\u6539\u5546\u54C1\u5B57\u6BB5\uFF08--product --body-file patch.json [--apply]\uFF09\u3002\u26A0\uFE0F --apply \u5373\u9001\u5BA1\u4E0A\u67B6\uFF1B\u7528\u6237\u8981\u5B58\u8349\u7A3F\u5C31\u505C\u624B\u95EE\u7528\u6237\uFF08#1958\uFF09",
1053
+ flags: ["body-file", "product"],
827
1054
  write: true,
828
1055
  preview: ({ args: args2 }) => [{ step: "partial_edit\uFF08\u5D4C\u5957\u5B57\u6BB5\u672A\u5217\u5B50\u9879\u4F1A\u88AB\u6E05\u7A7A\uFF09\u3002\u26A0\uFE0F \u9ED8\u8BA4 save_mode=LISTING\uFF1A--apply \u5373\u9001\u5BA1\u4E0A\u67B6\uFF08DRAFT \u5546\u54C1\u4F1A\u88AB\u63D0\u4EA4\uFF09\u3002\u82E5\u7528\u6237\u8981\u6C42\u5B58\u8349\u7A3F/\u522B\u63D0\u4EA4 \u2192 \u4E0D\u8981 --apply\uFF0C\u505C\u624B\u95EE\u7528\u6237\uFF08#1958\uFF09", method: "POST", path: `/product/202309/products/${args2.product}/partial_edit`, body: readBodyFile(args2["body-file"]) }],
829
1056
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/product/202309/products/${args2.product}/partial_edit`, { body: readBodyFile(args2["body-file"]) }),
@@ -833,6 +1060,7 @@ var COMMANDS = [
833
1060
  {
834
1061
  name: "affiliate creators search",
835
1062
  summary: "\u8054\u76DF\u5E7F\u573A\u627E\u8FBE\u4EBA\uFF08--keyword\uFF0C--page-size 12|20\uFF0C\u9ED8\u8BA4 12\uFF09",
1063
+ flags: ["keyword", "page-size"],
836
1064
  listKey: "creators",
837
1065
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202508/marketplace_creators/search", {
838
1066
  query: { page_size: String(args2["page-size"] ?? 12) },
@@ -842,11 +1070,13 @@ var COMMANDS = [
842
1070
  {
843
1071
  name: "affiliate creators perf",
844
1072
  summary: "\u8FBE\u4EBA\u5E26\u8D27\u8868\u73B0\u8BE6\u60C5\uFF08--creator \u5FC5\u586B\uFF0Ccreator_open_id\uFF09",
1073
+ flags: ["creator"],
845
1074
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/affiliate_seller/202508/marketplace_creators/${args2.creator}`, {})
846
1075
  },
847
1076
  {
848
1077
  name: "affiliate target-collab search",
849
1078
  summary: "\u5B9A\u5411\u9080\u7EA6\u5217\u8868\uFF08--status collaboration_status\uFF0C\u5982 ONGOING/EXPIRING/VALID/CANCELING/COMPLETED\uFF1B--page-size\uFF09",
1079
+ flags: ["page-size", "status"],
850
1080
  listKey: "target_collaborations",
851
1081
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202508/target_collaborations/search", {
852
1082
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -856,11 +1086,13 @@ var COMMANDS = [
856
1086
  {
857
1087
  name: "affiliate target-collab query",
858
1088
  summary: "\u5B9A\u5411\u9080\u7EA6\u8BE6\u60C5\uFF08--id \u5FC5\u586B\uFF09",
1089
+ flags: ["id"],
859
1090
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/affiliate_seller/202508/target_collaborations/${args2.id}`, {})
860
1091
  },
861
1092
  {
862
1093
  name: "affiliate samples search",
863
1094
  summary: "\u6837\u54C1\u7533\u8BF7\u5217\u8868\uFF08--page-size\uFF09",
1095
+ flags: ["page-size"],
864
1096
  listKey: "sample_applications",
865
1097
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202508/sample_applications/search", {
866
1098
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -870,6 +1102,7 @@ var COMMANDS = [
870
1102
  {
871
1103
  name: "affiliate dm list",
872
1104
  summary: "\u79C1\u4FE1\u4F1A\u8BDD\u5217\u8868\uFF08--page-size\uFF0C--status conversation_status \u53EF\u9009\uFF09\u3002\u26A0\uFE0F\u7248\u672C\u56FA\u5B9A 202412",
1105
+ flags: ["page-size", "status"],
873
1106
  listKey: "conversations",
874
1107
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/affiliate_seller/202412/conversations", {
875
1108
  query: {
@@ -881,11 +1114,13 @@ var COMMANDS = [
881
1114
  {
882
1115
  name: "affiliate dm messages",
883
1116
  summary: "\u4F1A\u8BDD\u6D88\u606F\u8BB0\u5F55\uFF08--conversation \u5FC5\u586B\uFF0Cconversation_id\uFF09",
1117
+ flags: ["conversation"],
884
1118
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/affiliate_seller/202412/conversation/${args2.conversation}/messages`, {})
885
1119
  },
886
1120
  {
887
1121
  name: "affiliate orders search",
888
1122
  summary: "\u8054\u76DF\u8BA2\u5355\u5217\u8868\uFF08\u4F63\u91D1\u660E\u7EC6\uFF0C--page-size\uFF09",
1123
+ flags: ["page-size"],
889
1124
  listKey: "orders",
890
1125
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202410/orders/search", {
891
1126
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -895,6 +1130,7 @@ var COMMANDS = [
895
1130
  {
896
1131
  name: "affiliate open-collab products-search",
897
1132
  summary: "\u516C\u5F00\u5408\u4F5C\u53EF\u8BBE\u4F63\u54C1\u5217\u8868\uFF08--page-size\uFF09",
1133
+ flags: ["page-size"],
898
1134
  listKey: "products",
899
1135
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202405/open_collaborations/products/search", {
900
1136
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -904,6 +1140,7 @@ var COMMANDS = [
904
1140
  {
905
1141
  name: "affiliate open-collab search",
906
1142
  summary: "\u516C\u5F00\u5408\u4F5C\u751F\u6548\u5217\u8868\uFF08\u542B current_commission\uFF0C--page-size\uFF09",
1143
+ flags: ["page-size"],
907
1144
  listKey: "open_collaborations",
908
1145
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202412/open_collaborations/search", {
909
1146
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -913,12 +1150,14 @@ var COMMANDS = [
913
1150
  {
914
1151
  name: "affiliate open-collab settings get",
915
1152
  summary: "\u516C\u5F00\u5408\u4F5C\u5168\u5C40\u8BBE\u7F6E\uFF08\u81EA\u52A8\u65B0\u589E\u54C1\u9ED8\u8BA4\u4F63\u91D1\uFF09",
1153
+ flags: [],
916
1154
  run: ({ shop }) => proxy("GET", shop, "/affiliate_seller/202409/open_collaboration_settings", {})
917
1155
  },
918
1156
  // ===== 联盟写命令(managing-affiliate-program,三道阀:dry-run 预览 → --apply 提交 → 回读) =====
919
1157
  {
920
1158
  name: "affiliate target-collab create",
921
1159
  summary: "\u53D1\u5B9A\u5411\u9080\u7EA6\uFF08--body-file collab.json\uFF0C\u542B creator_user_open_ids[]/products[]/free_sample_rule\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1160
+ flags: ["body-file"],
922
1161
  write: true,
923
1162
  preview: ({ args: args2 }) => [{
924
1163
  step: "\u53D1\u5B9A\u5411\u9080\u7EA6",
@@ -937,6 +1176,7 @@ var COMMANDS = [
937
1176
  {
938
1177
  name: "affiliate target-collab update",
939
1178
  summary: "\u6539\u5B9A\u5411\u9080\u7EA6\uFF08--id --body-file collab.json\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u63D0\u4EA4",
1179
+ flags: ["body-file", "id"],
940
1180
  write: true,
941
1181
  preview: ({ args: args2 }) => [{
942
1182
  step: "\u6539\u5B9A\u5411\u9080\u7EA6",
@@ -952,6 +1192,7 @@ var COMMANDS = [
952
1192
  {
953
1193
  name: "affiliate target-collab remove",
954
1194
  summary: "\u64A4\u56DE\u5B9A\u5411\u9080\u7EA6\uFF08--id \u5FC5\u586B\uFF0C\u64A4\u56DE\u5B89\u5168\u7F51\uFF09\u3002\u4E0D\u53EF\u9006\uFF0C\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1195
+ flags: ["id"],
955
1196
  write: true,
956
1197
  preview: ({ args: args2 }) => [{
957
1198
  step: "\u64A4\u56DE\u5B9A\u5411\u9080\u7EA6",
@@ -964,6 +1205,7 @@ var COMMANDS = [
964
1205
  {
965
1206
  name: "affiliate target-collab link",
966
1207
  summary: "\u751F\u6210\u5B9A\u5411\u9080\u7EA6\u5206\u4EAB\u94FE\u63A5\uFF08--id \u5FC5\u586B\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1208
+ flags: ["id"],
967
1209
  write: true,
968
1210
  preview: ({ args: args2 }) => [{
969
1211
  step: "\u751F\u6210\u9080\u7EA6\u94FE\u63A5",
@@ -976,6 +1218,7 @@ var COMMANDS = [
976
1218
  {
977
1219
  name: "affiliate dm open",
978
1220
  summary: "\u5F00\u8FBE\u4EBA\u79C1\u4FE1\u4F1A\u8BDD\uFF08--creator \u5FC5\u586B\uFF0Ccreator_open_id\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1221
+ flags: ["creator"],
979
1222
  write: true,
980
1223
  preview: ({ args: args2 }) => [{
981
1224
  step: "\u5F00\u79C1\u4FE1\u4F1A\u8BDD",
@@ -990,6 +1233,7 @@ var COMMANDS = [
990
1233
  {
991
1234
  name: "affiliate dm send",
992
1235
  summary: "\u7ED9\u8FBE\u4EBA\u53D1\u79C1\u4FE1\uFF08--conversation --content [--msg-type TEXT]\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1236
+ flags: ["content", "conversation", "msg-type"],
993
1237
  write: true,
994
1238
  preview: ({ args: args2 }) => [{
995
1239
  step: "\u53D1\u79C1\u4FE1",
@@ -1004,6 +1248,7 @@ var COMMANDS = [
1004
1248
  {
1005
1249
  name: "affiliate samples review",
1006
1250
  summary: "\u5BA1\u6279\u6837\u54C1\u7533\u8BF7\uFF08--id application_id --result review_result [--reject-reason]\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1251
+ flags: ["id", "reject-reason", "result"],
1007
1252
  listKey: "sample_applications",
1008
1253
  write: true,
1009
1254
  preview: ({ args: args2 }) => [{
@@ -1024,6 +1269,7 @@ var COMMANDS = [
1024
1269
  {
1025
1270
  name: "affiliate open-collab set-commission",
1026
1271
  summary: "\u6309\u54C1\u8BBE\u516C\u5F00\u5408\u4F5C\u4F63\u91D1\u7387\uFF08--product product_id --rate\uFF0C\u5355\u4F4D=\u767E\u5206\u6BD4\xD7100\uFF0C\u5982 1350=13.5%\uFF0C\u8303\u56F4 800-8000\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u5B58",
1272
+ flags: ["product", "rate"],
1027
1273
  write: true,
1028
1274
  preview: ({ args: args2 }) => [{
1029
1275
  step: "\u8BBE\u6309\u54C1\u516C\u5F00\u5408\u4F5C\u4F63\u91D1",
@@ -1042,6 +1288,7 @@ var COMMANDS = [
1042
1288
  {
1043
1289
  name: "affiliate open-collab remove",
1044
1290
  summary: "\u79FB\u9664\u54C1\u7684\u516C\u5F00\u5408\u4F5C\uFF08--product product_id\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1291
+ flags: ["product"],
1045
1292
  write: true,
1046
1293
  preview: ({ args: args2 }) => [{
1047
1294
  step: "\u79FB\u9664\u516C\u5F00\u5408\u4F5C",
@@ -1054,6 +1301,7 @@ var COMMANDS = [
1054
1301
  {
1055
1302
  name: "affiliate open-collab settings edit",
1056
1303
  summary: "\u6539\u516C\u5F00\u5408\u4F5C\u5168\u5C40\u9ED8\u8BA4\uFF08\u81EA\u52A8\u65B0\u589E\u54C1\u4F63\u91D1\uFF0C--rate --enable true|false\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u5B58",
1304
+ flags: ["enable", "rate"],
1057
1305
  write: true,
1058
1306
  preview: ({ args: args2 }) => [{
1059
1307
  step: "\u6539\u5168\u5C40\u9ED8\u8BA4",
@@ -1070,6 +1318,7 @@ var COMMANDS = [
1070
1318
  {
1071
1319
  name: "apps add",
1072
1320
  summary: "\u767B\u8BB0\u4E00\u4E2A TikTok App\uFF08--service-id --app-key --app-secret --type --label\uFF0C[--markets a,b] [--redirect] [--owner-entity] [--default]\uFF09",
1321
+ flags: ["app-key", "app-secret", "default", "label", "markets", "owner-entity", "redirect", "service-id", "type"],
1073
1322
  run: ({ args: args2 }) => adminApps("POST", {
1074
1323
  body: {
1075
1324
  serviceId: args2["service-id"],
@@ -1087,6 +1336,7 @@ var COMMANDS = [
1087
1336
  {
1088
1337
  name: "apps set-shop",
1089
1338
  summary: "\u9884\u7F6E shop\u2192app \u6620\u5C04\uFF08--shop shopId --app serviceId [--note]\uFF09\u2014\u2014auth link \u65F6\u6309\u5E97\u89E3\u6790\u7528\u54EA\u4E2A app",
1339
+ flags: ["app", "note"],
1090
1340
  run: ({ args: args2 }) => {
1091
1341
  if (!args2.shop || !args2.app) return Promise.resolve({ ok: false, error: { code: "VALIDATION", message: "--shop \u548C --app\uFF08serviceId\uFF09\u5FC5\u586B", retryable: false } });
1092
1342
  return setShopApp(args2.shop, args2.app, args2.note);
@@ -1095,16 +1345,19 @@ var COMMANDS = [
1095
1345
  {
1096
1346
  name: "apps list",
1097
1347
  summary: "\u5217\u51FA\u5DF2\u767B\u8BB0\u7684 TikTok App\uFF08admin\uFF0C\u542B appKey/status\uFF09",
1348
+ flags: [],
1098
1349
  run: () => adminApps("GET")
1099
1350
  },
1100
1351
  {
1101
1352
  name: "apps available",
1102
1353
  summary: "\u5217\u51FA\u53EF\u9009 App\uFF08\u7528\u6237\u9274\u6743\uFF0C\u4EC5 active\uFF0C\u65E0 secret\uFF09\u2014\u2014\u9009 serviceId \u4F20\u7ED9 auth link --app",
1354
+ flags: [],
1103
1355
  run: () => availableApps()
1104
1356
  },
1105
1357
  {
1106
1358
  name: "apps disable",
1107
1359
  summary: "\u505C\u7528\u4E00\u4E2A TikTok App\uFF08--id\uFF09",
1360
+ flags: ["id"],
1108
1361
  run: ({ args: args2 }) => adminApps("PATCH", { id: args2.id, body: { status: "disabled" } })
1109
1362
  },
1110
1363
  // ── auth link:skill 只传 --shop,backend 按 shop 解析用哪个 app(custom 映射→默认 public)。
@@ -1112,6 +1365,7 @@ var COMMANDS = [
1112
1365
  {
1113
1366
  name: "auth link",
1114
1367
  summary: "\u751F\u6210\u5E97\u94FA\u7ED1\u5B9A\u6388\u6743\u94FE\u63A5\uFF08--shop shopId\uFF0Cbackend \u6309\u5E97\u5B9A app\uFF1B--app serviceId \u53EF\u9009\u8986\u76D6\uFF09",
1368
+ flags: ["app"],
1115
1369
  run: async ({ args: args2 }) => {
1116
1370
  if (!args2.shop && !args2.app) {
1117
1371
  return { ok: false, error: { code: "VALIDATION", message: "--shop\uFF08shopId\uFF09\u5FC5\u586B\uFF08\u6216 --app \u8986\u76D6\uFF09", retryable: false } };
@@ -1181,9 +1435,254 @@ function findCommand(tokens) {
1181
1435
  }
1182
1436
  return null;
1183
1437
  }
1438
+ var DISCOUNT_FLAGS = ["shop", "apply", "actor", "title", "product", "price", "days"];
1439
+ var PRICE_ITEM = /^(?:([^:,\s/]+)\/)?([^:,\s/]+):(0|[1-9]\d*)(?:\.(\d{1,2}))?$/;
1440
+ function parsePriceItems(raw, defaultProduct) {
1441
+ const items = [];
1442
+ const seen = /* @__PURE__ */ new Set();
1443
+ for (const part of raw.split(",")) {
1444
+ const m = PRICE_ITEM.exec(part);
1445
+ if (!m) {
1446
+ return { ok: false, message: `--price \u6BCF\u4E00\u9879\u5FC5\u987B\u662F <skuId>:<\u4E00\u53E3\u4EF7> \u6216 <productId>/<skuId>:<\u4E00\u53E3\u4EF7>\uFF08\u5341\u8FDB\u5236\u6570\u5B57\uFF0C\u6700\u591A\u4E24\u4F4D\u5C0F\u6570\u3001\u4E0D\u8981\u524D\u5BFC\u96F6\uFF0C\u5982 25.42\uFF09\uFF0C\u591A\u9879\u7528\u9017\u53F7\u8FDE\u63A5\u3001\u5404\u9879\u4E4B\u95F4\u4E0D\u8981\u6709\u7A7A\u683C\uFF0C\u5982 --price 173707:29.90,173708:32.00\uFF1B\u6536\u5230\u300C${part}\u300D` };
1447
+ }
1448
+ const [, explicitPid, sid, intPart, frac] = m;
1449
+ if (explicitPid && defaultProduct && explicitPid !== defaultProduct) {
1450
+ return { ok: false, message: `--price \u7684\u300C${part}\u300D\u6307\u5B9A\u4E86\u5546\u54C1 ${explicitPid}\uFF0C\u4E0E --product ${defaultProduct} \u4E0D\u4E00\u81F4\uFF1B\u591A\u5546\u54C1\u8BF7\u628A --product \u53BB\u6389\u3001\u6BCF\u9879\u90FD\u5199\u6210 <productId>/<skuId>:<\u4EF7>` };
1451
+ }
1452
+ const pid = explicitPid ?? defaultProduct;
1453
+ if (!pid) {
1454
+ return { ok: false, message: `--price \u7684\u300C${part}\u300D\u6CA1\u5E26 productId\uFF0C\u800C\u4F60\u4E5F\u6CA1\u4F20 --product\uFF1B\u8981\u4E48\u52A0 --product\uFF0C\u8981\u4E48\u5199\u6210 <productId>/<skuId>:<\u4EF7>` };
1455
+ }
1456
+ const amount = frac === void 0 ? intPart : `${intPart}.${frac}`;
1457
+ if (!(Number(amount) > 0)) return { ok: false, message: `--price \u7684\u4E00\u53E3\u4EF7\u5FC5\u987B\u5927\u4E8E 0\uFF1B\u6536\u5230\u300C${part}\u300D` };
1458
+ if (seen.has(sid)) return { ok: false, message: `--price \u91CC\u540C\u4E00\u4E2A skuId \u91CD\u590D\u51FA\u73B0\uFF1A${sid}` };
1459
+ seen.add(sid);
1460
+ items.push({ pid, sid, amount });
1461
+ }
1462
+ return { ok: true, items };
1463
+ }
1464
+ function discountWindow(args2) {
1465
+ const now = Math.floor(Date.now() / 1e3);
1466
+ const begin = now + 900;
1467
+ const end = begin + Number(args2.days ?? 180) * 86400;
1468
+ return { begin, end };
1469
+ }
1470
+ function discountActivityBody(args2, begin, end) {
1471
+ return {
1472
+ title: args2.title,
1473
+ activity_type: "FIXED_PRICE",
1474
+ product_level: "VARIATION",
1475
+ begin_time: begin,
1476
+ end_time: end
1477
+ };
1478
+ }
1479
+ function discountProductsBody(args2) {
1480
+ const parsed = parsePriceItems(args2.price ?? "", args2.product);
1481
+ const items = parsed.ok ? parsed.items : [];
1482
+ const order = [];
1483
+ const byPid = /* @__PURE__ */ new Map();
1484
+ for (const it of items) {
1485
+ if (!byPid.has(it.pid)) {
1486
+ byPid.set(it.pid, []);
1487
+ order.push(it.pid);
1488
+ }
1489
+ byPid.get(it.pid).push({ sid: it.sid, amount: it.amount });
1490
+ }
1491
+ return {
1492
+ products: order.map((pid) => ({
1493
+ id: pid,
1494
+ quantity_limit: -1,
1495
+ quantity_per_user: -1,
1496
+ skus: byPid.get(pid).map((it) => ({
1497
+ id: it.sid,
1498
+ activity_price_amount: it.amount,
1499
+ // 🔴 写侧裸串;读回叫 activity_price.amount(F4)
1500
+ quantity_limit: -1,
1501
+ quantity_per_user: -1
1502
+ }))
1503
+ }))
1504
+ };
1505
+ }
1506
+ function discountHuman(args2) {
1507
+ const parsed = parsePriceItems(args2.price ?? "", args2.product);
1508
+ const days = Number(args2.days ?? 180);
1509
+ const list = parsed.ok ? parsed.items.map((it) => `\u5546\u54C1 ${it.pid} \u7684 SKU ${it.sid} \u552E\u4EF7\u5B9A\u4E3A ${it.amount}`).join("\uFF1B") : "\uFF08--price \u89E3\u6790\u5931\u8D25\uFF09";
1510
+ return `${list}\u3002\u539F\u4EF7\u5C06\u663E\u793A\u4E3A\u5212\u7EBF\u4EF7\u3002\u6D3B\u52A8\u5171 ${days} \u5929\uFF0C\u4ECE 15 \u5206\u949F\u540E\u5F00\u59CB \u2014\u2014 \u521A\u5EFA\u5B8C\u53BB\u5546\u54C1\u9875\u770B\u4EF7\u683C\u6CA1\u53D8\u662F\u6B63\u5E38\u7684\uFF0C\u90A3\u65F6\u6D3B\u52A8\u8FD8\u662F NOT_START\u3002`;
1511
+ }
1512
+ function validateCreateDiscount(ctx2) {
1513
+ const { args: args2, rest } = ctx2;
1514
+ for (const k of Object.keys(args2)) {
1515
+ if (DISCOUNT_FLAGS.includes(k)) continue;
1516
+ if (k === "percent") {
1517
+ return { ok: false, message: "\u672C\u7248\u4E0D\u652F\u6301\u767E\u5206\u6BD4\u6298\u6263\uFF08--percent\uFF09\u3002\u8BF7\u5148\u7528 tiktok products get --shop <id> --product <pid> \u53D6 SKU \u6302\u724C\u4EF7\uFF0C\u81EA\u884C\u6298\u7B97\uFF08\u4FDD\u7559\u4E24\u4F4D\u5C0F\u6570\uFF09\u540E\u8D70 --price <skuId>:<\u6298\u540E\u4EF7>" };
1518
+ }
1519
+ if (k === "sku") {
1520
+ return { ok: false, message: "\u672C\u547D\u4EE4\u6CA1\u6709 --sku\uFF1AskuId \u5199\u5728 --price \u91CC\uFF0C\u5982 --price 173707:29.90\uFF08\u6CA1\u4F20 --product \u65F6\u6BCF\u9879\u90FD\u8981\u5E26 productId\uFF0C\u5982 --price 1737048/173707:29.90\uFF09" };
1521
+ }
1522
+ const own = DISCOUNT_FLAGS.filter((f) => !GLOBAL_FLAGS.includes(f));
1523
+ const hint = suggestFlag(k, own, args2[k], Date.now(), "promotions create-discount");
1524
+ const tip = !hint ? "" : hint.startsWith("--") ? `\uFF08\u4F60\u662F\u60F3\u7528 ${hint} \u5417\uFF1F\uFF09` : `\uFF08${hint}\uFF09`;
1525
+ return { ok: false, message: `\u4E0D\u652F\u6301\u7684\u53C2\u6570 --${k}${tip}\uFF1B\u672C\u547D\u4EE4\u53EA\u8BA4 ${DISCOUNT_FLAGS.map((f) => "--" + f).join(" ")}` };
1526
+ }
1527
+ if (rest && rest.length > 0) {
1528
+ return { ok: false, message: `\u591A\u4F59\u7684\u4F4D\u7F6E\u53C2\u6570\u300C${rest.join(" ")}\u300D\u2014\u2014\u6BCF\u4E2A\u53C2\u6570\u53EA\u80FD\u5E26\u4E00\u4E2A\u503C\u3002\u591A\u4E2A SKU \u8981\u7528\u9017\u53F7\u8FDE\u6210\u4E00\u4E2A --price\uFF0C\u5982 --price A:12,B:15\uFF08\u4E0D\u80FD\u7528\u7A7A\u683C\u5206\u9694\uFF09\uFF1B\u82E5\u662F\u6807\u9898\u7B49\u503C\u91CC\u542B\u7A7A\u683C\uFF0C\u7ED9\u6574\u4E2A\u503C\u52A0\u5F15\u53F7\uFF0C\u5982 --title "Momo Sale"` };
1529
+ }
1530
+ for (const k of ["title", "price"]) {
1531
+ const v = args2[k];
1532
+ if (!v || v === "true" || !v.trim()) {
1533
+ return { ok: false, message: `--${k} \u5FC5\u586B\uFF0C\u4E14\u5FC5\u987B\u5E26\u503C\uFF08\u53EA\u5199 --${k} \u4E0D\u7ED9\u503C\u4F1A\u88AB\u89E3\u6790\u6210\u5B57\u7B26\u4E32 "true"\uFF0C\u5199\u6210 --${k}= \u5219\u662F\u7A7A\u503C\uFF0C\u4E24\u79CD\u90FD\u4E0D\u7B97\uFF09` };
1534
+ }
1535
+ }
1536
+ if (args2.title !== args2.title.trim()) {
1537
+ return { ok: false, message: `--title \u9996\u5C3E\u4E0D\u80FD\u6709\u7A7A\u767D\uFF08\u6536\u5230\u300C${args2.title}\u300D\uFF09\uFF1A\u649E\u91CD\u540D\u65F6 CLI \u6309\u6807\u9898\u4E25\u683C\u76F8\u7B49\u67E5\u540C\u540D\u6D3B\u52A8\uFF0C\u5E73\u53F0\u82E5\u53BB\u6389\u9996\u5C3E\u7A7A\u767D\u5C31\u4F1A\u5BF9\u4E0D\u4E0A\u3002\u8BF7\u53BB\u6389\u9996\u5C3E\u7A7A\u767D\u540E\u91CD\u8DD1` };
1538
+ }
1539
+ if ("product" in args2 && (!args2.product || args2.product === "true")) {
1540
+ return { ok: false, message: `--product \u5FC5\u586B\uFF0C\u4E14\u5FC5\u987B\u5E26\u503C\uFF08\u53EA\u5199 --product \u4E0D\u7ED9\u503C\u4F1A\u88AB\u89E3\u6790\u6210\u5B57\u7B26\u4E32 "true"\uFF0C\u5199\u6210 --product= \u5219\u662F\u7A7A\u503C\uFF0C\u4E24\u79CD\u90FD\u4E0D\u7B97\uFF09` };
1541
+ }
1542
+ if (args2.product?.includes(",")) {
1543
+ return { ok: false, message: "--product \u53EA\u6536\u4E00\u4E2A\u5546\u54C1 id\uFF1B\u591A\u5546\u54C1\u8BF7\u5728 --price \u91CC\u5199 <productId>/<skuId>:<\u4EF7>" };
1544
+ }
1545
+ const parsed = parsePriceItems(args2.price, args2.product);
1546
+ if (!parsed.ok) return parsed;
1547
+ if ("days" in args2) {
1548
+ const raw = args2.days;
1549
+ const n = Number(raw);
1550
+ if (!/^\d+$/.test(raw) || !(n >= 1 && n <= 180)) {
1551
+ return { ok: false, message: `--days \u5FC5\u987B\u662F 1\u2013180 \u7684\u6574\u6570\uFF08\u6536\u5230\u300C${raw}\u300D\uFF09` };
1552
+ }
1553
+ }
1554
+ return { ok: true };
1555
+ }
1556
+ var deactivateCmd = (shop, id) => `\`tiktok promotions deactivate --shop ${shop} --id ${id} --apply\``;
1557
+ var DISCOUNT_TEXT = {
1558
+ // ⑤ 建壳可能已落。标题 = 幂等键:原标题重跑,最坏撞一次重名,不会多出一条活动。
1559
+ maybeLanded: "\u26A0\uFE0F \u5EFA\u58F3\u8BF7\u6C42\u53EF\u80FD\u5DF2\u7ECF\u843D\u5230\u5E73\u53F0\u2014\u2014\u522B\u6362\u6807\u9898\uFF0C\u76F4\u63A5\u7528\u539F\u6807\u9898\u91CD\u8DD1\u539F\u547D\u4EE4\uFF1A\u6CA1\u843D\u5C31\u4F1A\u6B63\u5E38\u5EFA\u6210\uFF1B\u82E5\u5DF2\u843D\uFF0C\u91CD\u8DD1\u4F1A\u649E\u91CD\u540D\uFF0C\u90A3\u6761\u63D0\u793A\u4F1A\u544A\u8BC9\u4F60\u4E0B\u4E00\u6B65\u3002",
1560
+ // ⑥
1561
+ noCreateId: (title) => `\u5EFA\u58F3\u8FD4\u56DE\u6210\u529F\u4F46\u6CA1\u62FF\u5230 activity_id \u2014\u2014 \u26A0\uFE0F \u5E73\u53F0\u4E0A\u5F88\u53EF\u80FD\u5DF2\u7ECF\u6709\u8FD9\u6761\u58F3\u4E86\uFF08\u6807\u9898\u300C${title}\u300D\uFF09\u3002\u522B\u6362\u6807\u9898\uFF0C\u76F4\u63A5\u7528\u539F\u6807\u9898\u91CD\u8DD1\u539F\u547D\u4EE4\uFF1A\u91CD\u8DD1\u4F1A\u649E\u91CD\u540D\uFF0C\u90A3\u6761\u63D0\u793A\u4F1A\u544A\u8BC9\u4F60\u4E0B\u4E00\u6B65\u3002`,
1562
+ // ⑦ 撞重名后自查的六种结论(前面由调用方拼上平台原文 + 一个半角空格)
1563
+ dupSame: (title, aid, status) => `\u26A0\uFE0F \u540C\u540D\u6D3B\u52A8\u300C${title}\u300D\u5DF2\u5B58\u5728\uFF08\u6D3B\u52A8 ${aid}\uFF0C${status}\uFF09\uFF0C\u6302\u7684\u5546\u54C1\u4E0E\u672C\u6B21\u63D0\u4EA4\u5B8C\u5168\u4E00\u81F4\u2014\u2014\u8FD9\u5C31\u662F\u4F60\u8981\u7684\u6D3B\u52A8\uFF0C\u5DF2\u7ECF\u5EFA\u597D\u4E86\uFF1A\u522B\u91CD\u5EFA\u3002`,
1564
+ dupEmpty: (title, aid, status, shop) => `\u26A0\uFE0F \u540C\u540D\u6D3B\u52A8\u300C${title}\u300D\u5DF2\u5B58\u5728\uFF08\u6D3B\u52A8 ${aid}\uFF0C${status}\uFF09\uFF0C\u4F46\u5B83\u662F\u4E00\u6761\u7A7A\u58F3\uFF08\u6CA1\u6709\u4EFB\u4F55\u5546\u54C1\uFF09\u2014\u2014\u5148\u6E05\u7406\uFF1A${deactivateCmd(shop, aid)}\uFF0C\u518D\u7528\u539F\u6807\u9898\u91CD\u8DD1\u539F\u547D\u4EE4\uFF1B\u91CD\u8DD1\u82E5\u53C8\u649E\u91CD\u540D\uFF0C\u6362\u4E2A\u6807\u9898\u3002`,
1565
+ dupOwner: (title, aid, status, problems) => `\u26A0\uFE0F \u540C\u540D\u6D3B\u52A8\u300C${title}\u300D\u5DF2\u5B58\u5728\uFF08\u6D3B\u52A8 ${aid}\uFF0C${status}\uFF09\uFF0C\u6302\u7684\u5546\u54C1\u4E0E\u672C\u6B21\u63D0\u4EA4\u4E0D\u4E00\u81F4\uFF1A${problems}\u2014\u2014\u522B\u52A8\u5B83\uFF0C\u4E5F\u5148\u522B\u91CD\u5EFA\uFF1A\u628A\u5B83\u544A\u8BC9\u5E97\u4E3B\uFF0C\u95EE\u662F\u5C31\u7528\u8FD9\u6761\uFF0C\u8FD8\u662F\u6362\u4E2A\u6807\u9898\u53E6\u5EFA\u3002`,
1566
+ dupRetitle: (title) => `\u26A0\uFE0F \u672A\u5F00\u59CB / \u8FDB\u884C\u4E2D\u7684\u6D3B\u52A8\u91CC\u6CA1\u6709\u300C${title}\u300D\u2014\u2014\u8FD9\u4E2A\u6807\u9898\u88AB\u4E00\u6761\u5DF2\u7EC8\u6B62\u6216\u5DF2\u8FC7\u671F\u7684\u6D3B\u52A8\u5360\u7740\uFF0C\u6362\u4E2A\u6807\u9898\u91CD\u8DD1\u539F\u547D\u4EE4\u3002`,
1567
+ dupUnclear: (title, queryError) => `\u26A0\uFE0F \u540C\u540D\u6D3B\u52A8\u300C${title}\u300D\u5DF2\u5B58\u5728\uFF0C\u4F46\u6CA1\u80FD\u67E5\u6E05\u5B83\u7684\u72B6\u6001\uFF08${queryError}\uFF09\u2014\u2014\u522B\u6362\u6807\u9898\u3001\u522B\u91CD\u5EFA\uFF0C\u7A0D\u540E\u7528\u539F\u6807\u9898\u91CD\u8DD1\u539F\u547D\u4EE4\u518D\u770B\uFF1B\u8FDE\u7EED\u4E24\u6B21\u90FD\u662F\u8FD9\u53E5\uFF0C\u5C31\u5982\u5B9E\u544A\u8BC9\u5E97\u4E3B\u300C\u6D3B\u52A8\u53EF\u80FD\u5DF2\u5EFA\uFF0C\u4F46\u67E5\u4E0D\u6E05\u300D\u3002`,
1568
+ dupMulti: (title, found) => `\u26A0\uFE0F \u672A\u5F00\u59CB / \u8FDB\u884C\u4E2D\u7684\u6D3B\u52A8\u91CC\u6709 ${found.length} \u6761\u53EB\u300C${title}\u300D\uFF08${found.map((a) => `${a.id}\uFF1A${a.status}`).join("\uFF1B")}\uFF09\u2014\u2014\u522B\u52A8\u5B83\u4EEC\uFF0C\u4E5F\u5148\u522B\u91CD\u5EFA\uFF1A\u628A\u5B83\u4EEC\u544A\u8BC9\u5E97\u4E3B\uFF0C\u95EE\u600E\u4E48\u5904\u7406\u3002`,
1569
+ // ⑧ 加品失败后按回读下结论
1570
+ addFailAllAttached: (n, id) => `\u52A0\u5546\u54C1\u62A5\u4E86\u9519\uFF0C\u4F46\u56DE\u8BFB\u663E\u793A\u63D0\u4EA4\u7684 ${n} \u4E2A SKU \u90FD\u5DF2\u6309\u4EF7\u6302\u4E0A\uFF08\u6D3B\u52A8 ${id} \u7EA6 15 \u5206\u949F\u540E\u5F00\u59CB\uFF09\u2014\u2014\u522B\u6E05\u7406\uFF0C\u4E5F\u522B\u91CD\u8DD1\u672C\u547D\u4EE4\u3002`,
1571
+ addFailNoneAttached: (shop, id) => `\u52A0\u5546\u54C1\u5931\u8D25\uFF0C\u56DE\u8BFB\u663E\u793A\u6CA1\u6709\u4EFB\u4F55\u5546\u54C1\u6302\u4E0A\u2014\u2014\u6D3B\u52A8 ${id} \u662F\u4E00\u6761\u7A7A\u58F3\u3002\u5148\u6E05\u7406\uFF1A${deactivateCmd(shop, id)}\uFF1B\u518D\u6309\u4E0A\u9762\u7684\u5E73\u53F0\u62A5\u9519\u6539\u6B63\u53C2\u6570\u540E\u91CD\u8DD1\u539F\u547D\u4EE4\uFF08\u53EA\u6709\u9650\u6D41\u3001\u7F51\u7EDC\u7C7B\u62A5\u9519\u624D\u539F\u6837\u91CD\u8DD1\uFF09\u3002\u522B\u76F4\u63A5\u91CD\u8DD1\uFF1A\u4F1A\u649E\u91CD\u540D\u6216\u518D\u5EFA\u4E00\u4E2A\u58F3\u3002`,
1572
+ addFailPartial: (problems, shop, id) => `\u52A0\u5546\u54C1\u62A5\u4E86\u9519\uFF0C\u56DE\u8BFB\u663E\u793A\u6CA1\u6709\u5168\u90E8\u6302\u4E0A\uFF1A${problems}\u3002\u672C\u547D\u4EE4\u4E0D\u80FD\u5F80\u5DF2\u6709\u6D3B\u52A8\u8865\u5546\u54C1\uFF0C\u8BF7\u5E97\u4E3B\u4E8C\u9009\u4E00\uFF1A\u4FDD\u7559\u6D3B\u52A8 ${id}\uFF08\u7F3A\u7684 SKU \u4E0D\u6253\u6298\uFF0C\u4EF7\u683C\u4E0D\u7B26\u7684\u6309\u8BFB\u56DE\u7684\u4EF7\u683C\u751F\u6548\uFF09\uFF0C\u6216\u6E05\u7406\u540E\u6574\u6761\u91CD\u5EFA\uFF1A${deactivateCmd(shop, id)}\uFF0C\u6E05\u7406\u540E\u6309\u4E0A\u9762\u7684\u5E73\u53F0\u62A5\u9519\u6539\u6B63\u53C2\u6570\u518D\u91CD\u8DD1\u539F\u547D\u4EE4\u3002\u522B\u76F4\u63A5\u91CD\u8DD1\u672C\u547D\u4EE4\u3002`,
1573
+ addFailReadbackFailed: (shop, id) => `\u52A0\u5546\u54C1\u5931\u8D25\uFF0C\u56DE\u8BFB\u4E5F\u5931\u8D25\u4E86\uFF0C\u67E5\u4E0D\u5230\u6D3B\u52A8 ${id} \u91CC\u5546\u54C1\u6709\u6CA1\u6709\u6302\u4E0A\u3002\u522B\u91CD\u8DD1\u672C\u547D\u4EE4\uFF08\u4F1A\u518D\u5EFA\u4E00\u4E2A\u58F3\uFF09\uFF0C\u4E5F\u5148\u522B\u6E05\u7406\uFF1A\u5982\u5B9E\u544A\u8BC9\u5E97\u4E3B\u300C\u6D3B\u52A8\u5DF2\u5EFA\uFF0C\u5546\u54C1\u662F\u5426\u6302\u4E0A\u672A\u80FD\u786E\u8BA4\u300D\uFF0C\u8BF7\u5E97\u4E3B\u5728\u5356\u5BB6\u540E\u53F0\u770B\u8FD9\u6761\u6D3B\u52A8\u91CC\u6709\u6CA1\u6709\u5546\u54C1\uFF1B\u786E\u8BA4\u6CA1\u6302\u4E0A\u518D\u6E05\u7406\uFF1A${deactivateCmd(shop, id)}\u3002`,
1574
+ // ⑨ 成功路径的 warnings
1575
+ readbackFailed: (id) => `\u6D3B\u52A8 ${id} \u5DF2\u5EFA\u6210\u3001\u5546\u54C1\u5DF2\u63D0\u4EA4\uFF0C\u4F46\u56DE\u8BFB\u5931\u8D25\uFF0C\u672A\u80FD\u9A8C\u8BC1\u5546\u54C1\u662F\u5426\u6302\u4E0A\u2014\u2014\u4E0D\u8981\u91CD\u5EFA\u6D3B\u52A8\uFF1B\u5982\u5B9E\u544A\u8BC9\u5E97\u4E3B\u300C\u5DF2\u5EFA\uFF0C\u5546\u54C1\u662F\u5426\u6302\u4E0A\u672A\u80FD\u786E\u8BA4\u300D\u3002`,
1576
+ readbackDiffers: (id, problems) => `\u6D3B\u52A8 ${id} \u5DF2\u5EFA\u6210\uFF0C\u4F46\u56DE\u8BFB\u4E0E\u63D0\u4EA4\u4E0D\u4E00\u81F4\uFF1A${problems}\u2014\u2014\u4E0D\u8981\u91CD\u5EFA\u6D3B\u52A8\uFF1B\u5982\u5B9E\u544A\u8BC9\u5E97\u4E3B\u54EA\u4E9B SKU \u672A\u786E\u8BA4\u6302\u4E0A\u3002`
1577
+ };
1578
+ var OBSERVED_REJECT_CODES = /* @__PURE__ */ new Set([
1579
+ 36009004,
1580
+ 36009009,
1581
+ 36009010,
1582
+ 17029001,
1583
+ 17029004,
1584
+ 17029010,
1585
+ 17029021,
1586
+ 17029022,
1587
+ 17029043,
1588
+ 17029044,
1589
+ 17029063,
1590
+ 105002,
1591
+ 106001,
1592
+ 105005
1593
+ ]);
1594
+ var DUP_NAME_CODE = 17029004;
1595
+ function createFailureVerdict(env2) {
1596
+ const err = env2.error;
1597
+ const status = env2.meta?.http_status;
1598
+ if (status === void 0) return "maybe_landed";
1599
+ if (err.code === "RATE_LIMITED") return "maybe_landed";
1600
+ if (status >= 500) return "maybe_landed";
1601
+ const m = err.code === "TIKTOK_ERROR" ? /^\[(\d+)\]/.exec(err.message) : null;
1602
+ const code = m ? Number(m[1]) : void 0;
1603
+ if (status >= 400) return code === DUP_NAME_CODE ? "dup" : "rejected";
1604
+ if (err.code === "AUTH_EXPIRED") return "rejected";
1605
+ if (code !== void 0 && OBSERVED_REJECT_CODES.has(code)) return code === DUP_NAME_CODE ? "dup" : "rejected";
1606
+ return "maybe_landed";
1607
+ }
1608
+ function submittedSkus(args2) {
1609
+ return discountProductsBody(args2).products.flatMap((p) => p.skus.map((s) => ({ sid: s.id, amount: s.activity_price_amount })));
1610
+ }
1611
+ function compareReadback(submitted, readback) {
1612
+ const products = readback?.products;
1613
+ if (!Array.isArray(products)) return null;
1614
+ const skus = products.flatMap((p) => Array.isArray(p?.skus) ? p.skus : []);
1615
+ const mine = new Set(submitted.map((s) => s.sid));
1616
+ const byId = /* @__PURE__ */ new Map();
1617
+ const extra = [];
1618
+ for (const s of skus) {
1619
+ const id = String(s?.id);
1620
+ if ((byId.has(id) || !mine.has(id)) && !extra.includes(id)) extra.push(id);
1621
+ if (!byId.has(id)) byId.set(id, s);
1622
+ }
1623
+ const diff = { missing: [], mismatched: [], extra, total: skus.length, productCount: products.length };
1624
+ for (const { sid, amount } of submitted) {
1625
+ const hit = byId.get(sid);
1626
+ if (!hit) {
1627
+ diff.missing.push(sid);
1628
+ continue;
1629
+ }
1630
+ const raw = hit.activity_price?.amount;
1631
+ const got = raw === void 0 || raw === null || raw === "" ? void 0 : String(raw);
1632
+ if (got === void 0 || Number(got) !== Number(amount)) diff.mismatched.push({ sid, submitted: amount, readback: got });
1633
+ }
1634
+ return diff;
1635
+ }
1636
+ function problemsText(d) {
1637
+ const parts = [];
1638
+ if (d.missing.length) parts.push(`\u7F3A ${d.missing.join("\u3001")}`);
1639
+ if (d.mismatched.length) {
1640
+ parts.push(`\u91D1\u989D\u4E0D\u7B26 ${d.mismatched.map((m) => `${m.sid}\uFF08\u63D0\u4EA4 ${m.submitted}\uFF0C\u8BFB\u56DE ${m.readback ?? "\u65E0\u91D1\u989D"}\uFF09`).join("\u3001")}`);
1641
+ }
1642
+ return parts.join("\uFF1B");
1643
+ }
1644
+ function mergeWarnings(existing, ours) {
1645
+ if (existing === null || existing === void 0) return ours;
1646
+ if (Array.isArray(existing)) return [...existing, ...ours];
1647
+ return [existing, ...ours];
1648
+ }
1649
+ var DUP_SEARCH_MAX_PAGES = 10;
1650
+ async function dupSelfCheck(shop, title, submitted) {
1651
+ const found = /* @__PURE__ */ new Map();
1652
+ for (const status of ["NOT_START", "ONGOING"]) {
1653
+ let token;
1654
+ for (let page = 1; ; page++) {
1655
+ const r = await proxy("POST", shop, "/promotion/202309/activities/search", {
1656
+ body: { status, page_size: 50, ...token ? { page_token: token } : {} }
1657
+ });
1658
+ if (!r.ok) return DISCOUNT_TEXT.dupUnclear(title, r.error.message);
1659
+ const d2 = r.data ?? {};
1660
+ for (const a of Array.isArray(d2.activities) ? d2.activities : []) {
1661
+ if (a?.title === title) found.set(String(a.id), { id: String(a.id), status: String(a.status) });
1662
+ }
1663
+ token = d2.next_page_token ? String(d2.next_page_token) : void 0;
1664
+ if (!token) break;
1665
+ if (page === DUP_SEARCH_MAX_PAGES) return DISCOUNT_TEXT.dupUnclear(title, `\u540C\u540D\u81EA\u67E5\u7FFB\u4E86 ${DUP_SEARCH_MAX_PAGES} \u9875\u4ECD\u672A\u7FFB\u5B8C`);
1666
+ }
1667
+ }
1668
+ if (found.size === 0) return DISCOUNT_TEXT.dupRetitle(title);
1669
+ if (found.size > 1) return DISCOUNT_TEXT.dupMulti(title, [...found.values()].sort((a, b) => Number(a.status !== "NOT_START") - Number(b.status !== "NOT_START")));
1670
+ const [hit] = found.values();
1671
+ const g = await proxy("GET", shop, `/promotion/202309/activities/${hit.id}`, {});
1672
+ if (!g.ok) return DISCOUNT_TEXT.dupUnclear(title, g.error.message);
1673
+ const d = compareReadback(submitted, g.data);
1674
+ if (!d) return DISCOUNT_TEXT.dupUnclear(title, "\u56DE\u8BFB\u7F3A products \u5B57\u6BB5");
1675
+ if (d.missing.length === 0 && d.mismatched.length === 0 && d.total === submitted.length) {
1676
+ return DISCOUNT_TEXT.dupSame(title, hit.id, hit.status);
1677
+ }
1678
+ if (d.productCount === 0) return DISCOUNT_TEXT.dupEmpty(title, hit.id, hit.status, shop);
1679
+ return DISCOUNT_TEXT.dupOwner(title, hit.id, hit.status, problemsText(d) || `\u591A\u51FA ${d.extra.join("\u3001")}`);
1680
+ }
1184
1681
 
1185
1682
  // src/index.ts
1186
1683
  var NO_SHOP_COMMANDS = /* @__PURE__ */ new Set(["auth link", "apps add", "apps list", "apps disable", "apps available", "apps set-shop"]);
1684
+ var SHOP_OPTIONAL_BUT_READ = /* @__PURE__ */ new Set(["auth link", "apps set-shop"]);
1685
+ var mentionsShop = (name) => !NO_SHOP_COMMANDS.has(name) || SHOP_OPTIONAL_BUT_READ.has(name);
1187
1686
  function parseFlags(tokens) {
1188
1687
  const args2 = {};
1189
1688
  const positional2 = [];
@@ -1241,6 +1740,7 @@ function usage() {
1241
1740
  for (const c of COMMANDS) lines.push(` ${c.name.padEnd(22)} ${c.summary}`);
1242
1741
  lines.push(` ${"shops".padEnd(22)} \u67E5\u5F53\u524D\u767B\u5F55\u7528\u6237\u5DF2\u6388\u6743\u5E97\u94FA`);
1243
1742
  lines.push(` ${"images upload".padEnd(22)} \u4E0A\u4F20\u672C\u5730\u56FE\u7247\uFF08--file --use-case [--out]\uFF09`);
1743
+ lines.push("", "\u6BCF\u6761\u547D\u4EE4\u53EA\u8BA4\u5B83\u81EA\u5DF1\u7684\u53C2\u6570\uFF08`tiktok <\u547D\u4EE4> --help` \u770B\uFF09\uFF0C\u4E0D\u8BA4\u7684\u4F1A\u5F53\u573A\u62A5 VALIDATION \u2014\u2014 \u4E0D\u4F1A\u518D\u88AB\u9759\u9ED8\u4E22\u6389\u3002");
1244
1744
  lines.push("", "\u901A\u7528\uFF1A--shop \u5FC5\u586B\uFF08\u9664 shops / auth link / apps add|list|disable\uFF09\uFF1B\u9ED8\u8BA4 JSON \u8F93\u51FA\uFF1BTIKTOK_BACKEND_URL \u663E\u5F0F\u6307\u5B9A backend\uFF08\u672A\u8BBE\u5219\u6309 OPTIMA_ENV \u6620\u5C04 cn-stage/cn-prod\uFF0C\u5426\u5219 localhost\uFF09\u3002");
1245
1745
  process.stderr.write(lines.join("\n") + "\n");
1246
1746
  }
@@ -1250,11 +1750,40 @@ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
1250
1750
  process.exit(argv.length === 0 ? 1 : 0);
1251
1751
  }
1252
1752
  var { args, positional } = parseFlags(argv);
1753
+ var wantsHelp = argv.includes("--help") || argv.includes("-h") || "help" in args;
1754
+ function rejectUnknownFlags(cmdName, allowed, rest, noShop = false) {
1755
+ const v = checkFlags(cmdName, args, allowed, rest, Date.now(), noShop);
1756
+ if (!v) return;
1757
+ out({
1758
+ ok: false,
1759
+ error: {
1760
+ code: "VALIDATION",
1761
+ message: v.message,
1762
+ retryable: false,
1763
+ unknown_flags: v.unknown_flags,
1764
+ extra_positional: v.extra_positional,
1765
+ allowed_flags: v.allowed_flags
1766
+ }
1767
+ });
1768
+ process.exit(1);
1769
+ }
1770
+ var IMAGES_UPLOAD = { name: "images upload", summary: "\u4E0A\u4F20\u672C\u5730\u56FE\u7247\uFF08--file <\u8DEF\u5F84> [--use-case MAIN_IMAGE] [--out <\u8FFD\u52A0\u5199\u5165\u7684 jsonl>]\uFF09", flags: ["file", "use-case", "out"] };
1771
+ var SHOPS = { name: "shops", summary: "\u67E5\u5F53\u524D\u767B\u5F55\u7528\u6237\u5DF2\u6388\u6743\u5E97\u94FA\uFF08\u4E0D\u9700\u8981 --shop\uFF09", flags: [], noShop: true };
1253
1772
  if (positional[0] === "shops") {
1773
+ if (wantsHelp) {
1774
+ process.stderr.write(commandUsage(SHOPS));
1775
+ process.exit(0);
1776
+ }
1777
+ rejectUnknownFlags(SHOPS.name, SHOPS.flags, positional.slice(1), true);
1254
1778
  out(await listShops());
1255
1779
  process.exit(0);
1256
1780
  }
1257
1781
  if (positional[0] === "images" && positional[1] === "upload") {
1782
+ if (wantsHelp) {
1783
+ process.stderr.write(commandUsage(IMAGES_UPLOAD));
1784
+ process.exit(0);
1785
+ }
1786
+ rejectUnknownFlags(IMAGES_UPLOAD.name, IMAGES_UPLOAD.flags, positional.slice(2));
1258
1787
  if (!args.file) {
1259
1788
  out({ ok: false, error: { code: "VALIDATION", message: "\u7F3A\u5C11 --file", retryable: false } });
1260
1789
  process.exit(1);
@@ -1271,24 +1800,29 @@ if (positional[0] === "images" && positional[1] === "upload") {
1271
1800
  out(r);
1272
1801
  process.exit(0);
1273
1802
  }
1274
- var wantsHelp = argv.includes("--help") || argv.includes("-h");
1275
1803
  var match = findCommand(positional);
1276
1804
  if (wantsHelp) {
1277
- if (match) process.stderr.write(commandUsage(match.cmd));
1805
+ if (match) process.stderr.write(commandUsage({ ...match.cmd, noShop: !mentionsShop(match.cmd.name) }));
1278
1806
  else usage();
1279
1807
  process.exit(0);
1280
1808
  }
1281
- normalizeBooleanFlags(args);
1282
1809
  if (!match) {
1283
1810
  out({ ok: false, error: { code: "VALIDATION", message: `\u672A\u77E5\u547D\u4EE4: ${positional.join(" ")}`, retryable: false } });
1284
1811
  usage();
1285
1812
  process.exit(1);
1286
1813
  }
1814
+ var ctx = { shop: args.shop ?? "", args, actor: args.actor, rest: match.rest };
1815
+ var verdict = match.cmd.validate?.(ctx);
1816
+ if (verdict && !verdict.ok) {
1817
+ out({ ok: false, error: { code: "VALIDATION", message: verdict.message, retryable: false } });
1818
+ process.exit(1);
1819
+ }
1820
+ rejectUnknownFlags(match.cmd.name, match.cmd.flags, match.rest, !mentionsShop(match.cmd.name));
1821
+ normalizeBooleanFlags(args);
1287
1822
  if (!args.shop && !NO_SHOP_COMMANDS.has(match.cmd.name)) {
1288
1823
  out({ ok: false, error: { code: "VALIDATION", message: "\u7F3A\u5C11 --shop\uFF08\u591A\u5E97\u663E\u5F0F\u8DEF\u7531\uFF09", retryable: false } });
1289
1824
  process.exit(1);
1290
1825
  }
1291
- var ctx = { shop: args.shop ?? "", args, actor: args.actor };
1292
1826
  if (match.cmd.write && !("apply" in args)) {
1293
1827
  if (match.cmd.precheck) {
1294
1828
  const pc = await match.cmd.precheck(ctx);
@@ -1314,7 +1848,7 @@ fillEmptyList(env.data, match.cmd.listKey);
1314
1848
  if (match.cmd.write && env.ok) {
1315
1849
  const result = { ...env, applied: true };
1316
1850
  if (env.data?.warnings?.length) {
1317
- result.warnings_alert = "\u26A0\uFE0F \u6709\u5B57\u6BB5\u88AB\u9759\u9ED8\u5904\u7406\uFF0C\u89C1 warnings";
1851
+ result.warnings_alert = "\u26A0\uFE0F \u6709\u8B66\u544A\uFF0C\u89C1 data.warnings";
1318
1852
  }
1319
1853
  if (match.cmd.readback) {
1320
1854
  const rb = await match.cmd.readback(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/tiktok-cli",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "TikTok Shop CLI for Optima agents — 经 tiktok-backend 操作店铺",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,8 @@
30
30
  "directory": "tiktok-cli"
31
31
  },
32
32
  "publishConfig": {
33
- "access": "public"
33
+ "access": "public",
34
+ "registry": "https://registry.npmjs.org"
34
35
  },
35
36
  "engines": {
36
37
  "node": ">=20"