@optima-chat/tiktok-cli 0.13.0 → 0.15.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 +15 -1
  2. package/dist/index.js +266 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,7 +23,7 @@ tiktok <domain> <verb> --shop <shopId> [--flags]
23
23
  | `orders list` | 订单列表(`--status --page-size --since`) |
24
24
  | `orders get --id <id>` | 订单详情 |
25
25
  | `analytics shop` | 店铺经营数据(`--from --to`,默认近30天) |
26
- | `analytics products` | 商品维度数据(GMV 降序) |
26
+ | `analytics products` | 商品维度数据(GMV 降序)。`--version` 默认 `202509`;`202605` 返回 `total_performance` + 分渠道 `*_performance` 组(**组是可选的、字段名三套变体、`overall_performance` 在新版不存在**,见 spec §3.0);未知版本本地拒,平台出新版可 `--force-version` 直接试;`--page-token` 翻页,**末页 `next_page_token` 为空串**(不是缺席) |
27
27
  | `products list` | 商品列表 |
28
28
  | `promotions list` | 促销活动。`--status` **省略 = 全部状态**(`ONGOING`/`NOT_START`/`EXPIRED`/`DEACTIVATED`;⚠️ 值写错平台**静默忽略并返回全部**,故 CLI **本地硬拒未知值**;平台将来新增值可用 `--force-status` 越过);`--page-size`(默认 20);`--page-token` 翻页,**末页无 `next_page_token`** |
29
29
  | `finance statements` | 结算单 |
@@ -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";
@@ -209,19 +306,26 @@ 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);
218
320
  var daysAgo = (n) => new Date(Date.now() - n * 864e5).toISOString().slice(0, 10);
219
321
  var toEpoch = (v) => /^\d+$/.test(v) ? Number(v) : Math.floor(Date.parse(v) / 1e3);
220
322
  var PROMOTION_STATUSES = /* @__PURE__ */ new Set(["ONGOING", "NOT_START", "EXPIRED", "DEACTIVATED"]);
323
+ var ANALYTICS_PRODUCT_VERSIONS = /* @__PURE__ */ new Set(["202509", "202605"]);
221
324
  var COMMANDS = [
222
325
  {
223
326
  name: "orders list",
224
327
  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",
328
+ flags: ["from", "page-size", "page-token", "since", "sort-field", "sort-order", "status", "to"],
225
329
  listKey: "orders",
226
330
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/order/202309/orders/search", {
227
331
  query: {
@@ -243,12 +347,14 @@ var COMMANDS = [
243
347
  {
244
348
  name: "orders get",
245
349
  summary: "\u8BA2\u5355\u8BE6\u60C5\uFF08--id \u5FC5\u586B\uFF09",
350
+ flags: ["id"],
246
351
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/order/202309/orders/detail", { query: { ids: args2.id } })
247
352
  },
248
353
  // ── 售后(退货/取消)只读 ──
249
354
  {
250
355
  name: "orders cancellations",
251
356
  summary: "\u53D6\u6D88\u5355\u5217\u8868\uFF08--status \u53EF\u9009\uFF0C--page-size\uFF09",
357
+ flags: ["page-size", "status"],
252
358
  listKey: "cancellations",
253
359
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/return_refund/202309/cancellations/search", {
254
360
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -258,6 +364,7 @@ var COMMANDS = [
258
364
  {
259
365
  name: "orders returns",
260
366
  summary: "\u9000\u8D27\u5355\u5217\u8868\uFF08--status \u53EF\u9009\uFF0C--page-size\uFF09",
367
+ flags: ["page-size", "status"],
261
368
  listKey: "return_orders",
262
369
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/return_refund/202309/returns/search", {
263
370
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -267,16 +374,19 @@ var COMMANDS = [
267
374
  {
268
375
  name: "orders return-records",
269
376
  summary: "\u9000\u8D27\u5355\u5904\u7406\u8BB0\u5F55\uFF08--id return_id\uFF09",
377
+ flags: ["id"],
270
378
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/return_refund/202309/returns/${args2.id}/records`, {})
271
379
  },
272
380
  {
273
381
  name: "orders aftersale-eligibility",
274
382
  summary: "\u8BA2\u5355\u552E\u540E\u8D44\u683C\uFF08--order id\uFF09",
383
+ flags: ["order"],
275
384
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/return_refund/202512/orders/${args2.order}/aftersale_eligibility`, {})
276
385
  },
277
386
  {
278
387
  name: "orders reject-reasons",
279
388
  summary: "\u9A73\u56DE\u53D6\u6D88/\u9000\u8D27\u7684\u5408\u6CD5\u7406\u7531\uFF08--id return_or_cancel_id\uFF09",
389
+ flags: ["id"],
280
390
  listKey: "reasons",
281
391
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/return_refund/202309/reject_reasons", {
282
392
  query: { return_or_cancel_id: args2.id }
@@ -285,6 +395,7 @@ var COMMANDS = [
285
395
  {
286
396
  name: "orders refund-calculate",
287
397
  summary: "\u8BA1\u7B97\u9000\u6B3E\u989D\uFF08--body-file calc.json\uFF09",
398
+ flags: ["body-file"],
288
399
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/return_refund/202309/refunds/calculate", {
289
400
  body: readBodyFile(args2["body-file"])
290
401
  })
@@ -293,11 +404,13 @@ var COMMANDS = [
293
404
  {
294
405
  name: "orders tracking",
295
406
  summary: "\u8BA2\u5355\u7269\u6D41\u8DDF\u8E2A\uFF08--order id\uFF09",
407
+ flags: ["order"],
296
408
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/fulfillment/202309/orders/${args2.order}/tracking`, {})
297
409
  },
298
410
  {
299
411
  name: "orders shipping-services",
300
412
  summary: "\u8BA2\u5355\u53EF\u7528\u627F\u8FD0/\u914D\u9001\u670D\u52A1\uFF08--order id\uFF09",
413
+ flags: ["order"],
301
414
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/fulfillment/202309/orders/${args2.order}/shipping_services/query`, {
302
415
  body: {}
303
416
  })
@@ -305,6 +418,7 @@ var COMMANDS = [
305
418
  {
306
419
  name: "packages search",
307
420
  summary: "\u5305\u88F9\u5217\u8868\uFF08--page-size\uFF09",
421
+ flags: ["page-size"],
308
422
  listKey: "packages",
309
423
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/fulfillment/202309/packages/search", {
310
424
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -314,17 +428,20 @@ var COMMANDS = [
314
428
  {
315
429
  name: "packages get",
316
430
  summary: "\u5305\u88F9\u8BE6\u60C5\uFF08--package id\uFF09",
431
+ flags: ["package"],
317
432
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/fulfillment/202309/packages/${args2.package}`, {})
318
433
  },
319
434
  {
320
435
  name: "packages documents",
321
436
  summary: "\u5305\u88F9\u7269\u6D41\u9762\u5355\uFF08--package id\uFF09",
437
+ flags: ["package"],
322
438
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/fulfillment/202309/packages/${args2.package}/shipping_documents`, {})
323
439
  },
324
440
  // ── 售后审批 / 发货 写(不可逆,默认 dry-run,--apply 才提交)──
325
441
  {
326
442
  name: "orders cancel-approve",
327
443
  summary: "\u6279\u51C6\u53D6\u6D88\u5355\uFF08--id cancel_id\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u6279\u3002\u4E0D\u53EF\u9006",
444
+ flags: ["id"],
328
445
  write: true,
329
446
  preview: ({ args: args2 }) => [{
330
447
  step: "\u6279\u51C6\u53D6\u6D88\u5355",
@@ -337,6 +454,7 @@ var COMMANDS = [
337
454
  {
338
455
  name: "orders cancel-reject",
339
456
  summary: "\u9A73\u56DE\u53D6\u6D88\u5355\uFF08--id --reason\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u9A73\u3002\u4E0D\u53EF\u9006",
457
+ flags: ["id", "reason"],
340
458
  write: true,
341
459
  preview: ({ args: args2 }) => [{
342
460
  step: "\u9A73\u56DE\u53D6\u6D88\u5355",
@@ -354,6 +472,7 @@ var COMMANDS = [
354
472
  {
355
473
  name: "orders return-approve",
356
474
  summary: "\u6279\u51C6\u9000\u8D27\u5355\uFF08--id return_id\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u6279\u3002\u4E0D\u53EF\u9006",
475
+ flags: ["id"],
357
476
  write: true,
358
477
  preview: ({ args: args2 }) => [{
359
478
  step: "\u6279\u51C6\u9000\u8D27\u5355",
@@ -366,6 +485,7 @@ var COMMANDS = [
366
485
  {
367
486
  name: "orders return-reject",
368
487
  summary: "\u9A73\u56DE\u9000\u8D27\u5355\uFF08--id --reason\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u9A73\u3002\u4E0D\u53EF\u9006",
488
+ flags: ["id", "reason"],
369
489
  write: true,
370
490
  preview: ({ args: args2 }) => [{
371
491
  step: "\u9A73\u56DE\u9000\u8D27\u5355",
@@ -383,6 +503,7 @@ var COMMANDS = [
383
503
  {
384
504
  name: "orders create-package",
385
505
  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",
506
+ flags: ["body-file", "order"],
386
507
  write: true,
387
508
  preview: ({ args: args2 }) => [{
388
509
  step: "\u5EFA\u5305\u88F9",
@@ -400,6 +521,7 @@ var COMMANDS = [
400
521
  {
401
522
  name: "packages ship",
402
523
  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",
524
+ flags: ["body-file", "package"],
403
525
  write: true,
404
526
  preview: ({ args: args2 }) => [{
405
527
  step: "\u53D1\u8D27",
@@ -418,6 +540,7 @@ var COMMANDS = [
418
540
  {
419
541
  name: "events",
420
542
  summary: "\u67E5\u5E97\u94FA webhook \u4E8B\u4EF6\uFF08--type \u9017\u53F7\u5206\u9694 --since 1d/unix --unprocessed --limit\uFF09",
543
+ flags: ["limit", "since", "type", "unprocessed"],
421
544
  run: ({ shop, args: args2 }) => {
422
545
  const q = {};
423
546
  if (args2.type) q.type = args2.type;
@@ -430,6 +553,7 @@ var COMMANDS = [
430
553
  {
431
554
  name: "events ack",
432
555
  summary: "\u6807\u4E8B\u4EF6\u5DF2\u5904\u7406\uFF08--ids id1,id2 \u6216 --before <ts>\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u6807",
556
+ flags: ["before", "ids"],
433
557
  write: true,
434
558
  preview: ({ args: args2 }) => [{
435
559
  step: "\u6807\u4E8B\u4EF6\u5DF2\u5904\u7406",
@@ -442,6 +566,7 @@ var COMMANDS = [
442
566
  {
443
567
  name: "analytics shop",
444
568
  summary: "\u5E97\u94FA\u7ECF\u8425\u6570\u636E\uFF08--from --to\uFF0C\u9ED8\u8BA4\u8FD130\u5929\uFF09",
569
+ flags: ["from", "granularity", "to"],
445
570
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop/performance", {
446
571
  query: {
447
572
  start_date_ge: args2.from ?? daysAgo(30),
@@ -452,20 +577,49 @@ var COMMANDS = [
452
577
  },
453
578
  {
454
579
  name: "analytics products",
455
- summary: "\u5546\u54C1\u7EF4\u5EA6\u7ECF\u8425\u6570\u636E\uFF08\u6309 GMV \u964D\u5E8F\uFF09",
456
- run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_products/performance", {
457
- query: {
458
- start_date_ge: args2.from ?? daysAgo(30),
459
- end_date_lt: args2.to ?? today(),
460
- sort_field: "gmv",
461
- sort_order: "DESC",
462
- page_size: args2["page-size"] ?? "20"
580
+ summary: "\u5546\u54C1\u7EF4\u5EA6\u7ECF\u8425\u6570\u636E\uFF08\u6309 GMV \u964D\u5E8F\uFF1B--from --to \u9ED8\u8BA4\u8FD130\u5929\uFF1B--version 202509 \u9ED8\u8BA4\uFF5C202605 = \u5546\u54C1\u7EA7 key \u6362\u6210 total_performance + \u5206\u6E20\u9053 *_performance \u7EC4\uFF08\u7EC4\u53EF\u9009\u3001\u5B57\u6BB5\u540D\u6709\u4E09\u5957\u53D8\u4F53\uFF09\uFF0C\u672A\u77E5\u7248\u672C\u672C\u5730\u62D2\u3001\u5E73\u53F0\u5C06\u6765\u51FA\u65B0\u7248\u53EF\u7528 --force-version \u76F4\u63A5\u8BD5\uFF1B--page-size \u9ED8\u8BA420\uFF1B--page-token \u7FFB\u9875\uFF0C\u672B\u9875 next_page_token \u4E3A\u7A7A\u4E32\uFF09",
581
+ flags: ["force-version", "from", "page-size", "page-token", "to", "version"],
582
+ listKey: "products",
583
+ run: ({ shop, args: args2 }) => {
584
+ const ver = args2.version ? String(args2.version) : "202509";
585
+ if (!/^\d{6}$/.test(ver)) {
586
+ return Promise.resolve({
587
+ ok: false,
588
+ error: {
589
+ code: "VALIDATION",
590
+ message: `--version \u5FC5\u987B\u662F 6 \u4F4D\u6570\u5B57\uFF08\u5DF2\u77E5\u53D6\u503C\uFF1A${[...ANALYTICS_PRODUCT_VERSIONS].join(" / ")}\uFF09\uFF0C\u6536\u5230 ${JSON.stringify(ver)}\u3002\u26D4 \u683C\u5F0F\u4E0D\u5408\u6CD5\u65F6 --force-version \u4E5F\u4E0D\u653E\u884C\uFF0C\u4E0D\u53D1\u8BF7\u6C42\u3002`,
591
+ retryable: false
592
+ }
593
+ });
463
594
  }
464
- })
595
+ if (!ANALYTICS_PRODUCT_VERSIONS.has(ver) && !args2["force-version"]) {
596
+ return Promise.resolve({
597
+ ok: false,
598
+ error: {
599
+ code: "VALIDATION",
600
+ message: `\u672A\u77E5\u7684 --version ${JSON.stringify(ver)}\uFF1B\u5DF2\u77E5\u53D6\u503C\uFF1A${[...ANALYTICS_PRODUCT_VERSIONS].join(" / ")}\u3002\u5E73\u53F0\u82E5\u5DF2\u53D1\u5E03\u65B0\u7248\u672C\uFF0C\u53EF\u52A0 --force-version \u76F4\u63A5\u8BD5\uFF08\u7248\u672C\u4E0D\u5BF9\u5E73\u53F0\u4F1A\u660E\u786E\u56DE 36009004\uFF09\u3002\u672C\u5730\u62D2\uFF0C\u4E0D\u53D1\u8BF7\u6C42\u3002`,
601
+ retryable: false
602
+ }
603
+ });
604
+ }
605
+ return proxy("GET", shop, `/analytics/${ver}/shop_products/performance`, {
606
+ query: {
607
+ start_date_ge: args2.from ?? daysAgo(30),
608
+ end_date_lt: args2.to ?? today(),
609
+ sort_field: "gmv",
610
+ sort_order: "DESC",
611
+ page_size: args2["page-size"] ?? "20",
612
+ // 有值才带(照 analytics videos)。实测(spec §3.0 用例 07):第 1 页 token 翻到第 2 页零交集;
613
+ // 🔴 末页 next_page_token **键在、值为空串 ""**(不是缺席,与 promotions list 不同构)。
614
+ ...args2["page-token"] ? { page_token: args2["page-token"] } : {}
615
+ }
616
+ });
617
+ }
465
618
  },
466
619
  {
467
620
  name: "analytics videos",
468
621
  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",
622
+ flags: ["from", "page-size", "page-token", "sort-field", "sort-order", "to"],
469
623
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_videos/performance", {
470
624
  query: {
471
625
  start_date_ge: args2.from ?? daysAgo(30),
@@ -480,6 +634,7 @@ var COMMANDS = [
480
634
  {
481
635
  name: "analytics videos-overview",
482
636
  summary: "\u5185\u5BB9\u89C6\u9891\u6574\u4F53\u6982\u89C8\uFF08shop_videos overview\uFF1B--from --to --granularity\uFF09",
637
+ flags: ["from", "granularity", "to"],
483
638
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_videos/overview_performance", {
484
639
  query: {
485
640
  start_date_ge: args2.from ?? daysAgo(30),
@@ -491,6 +646,7 @@ var COMMANDS = [
491
646
  {
492
647
  name: "analytics video",
493
648
  summary: "\u5355\u6761\u89C6\u9891\u7ECF\u8425\u660E\u7EC6\uFF08--video video_id \u5FC5\u586B\uFF1B--from --to\uFF09",
649
+ flags: ["from", "to", "video"],
494
650
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_videos/${args2.video}/performance`, {
495
651
  query: {
496
652
  start_date_ge: args2.from ?? daysAgo(30),
@@ -501,6 +657,7 @@ var COMMANDS = [
501
657
  {
502
658
  name: "analytics video-products",
503
659
  summary: "\u5355\u6761\u89C6\u9891\u5E26\u8D27\u5546\u54C1\u8868\u73B0\uFF08--video video_id \u5FC5\u586B\uFF1B--from --to --page-size\uFF09",
660
+ flags: ["from", "page-size", "to", "video"],
504
661
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_videos/${args2.video}/products/performance`, {
505
662
  query: {
506
663
  start_date_ge: args2.from ?? daysAgo(30),
@@ -512,6 +669,7 @@ var COMMANDS = [
512
669
  {
513
670
  name: "analytics lives",
514
671
  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",
672
+ flags: ["from", "page-size", "page-token", "sort-field", "sort-order", "to"],
515
673
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_lives/performance", {
516
674
  query: {
517
675
  start_date_ge: args2.from ?? daysAgo(30),
@@ -526,6 +684,7 @@ var COMMANDS = [
526
684
  {
527
685
  name: "analytics lives-overview",
528
686
  summary: "\u76F4\u64AD\u6574\u4F53\u6982\u89C8\uFF08shop_lives overview\uFF1B--from --to --granularity\uFF09",
687
+ flags: ["from", "granularity", "to"],
529
688
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_lives/overview_performance", {
530
689
  query: {
531
690
  start_date_ge: args2.from ?? daysAgo(30),
@@ -537,6 +696,7 @@ var COMMANDS = [
537
696
  {
538
697
  name: "analytics skus",
539
698
  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",
699
+ flags: ["from", "page-size", "page-token", "sort-field", "sort-order", "to"],
540
700
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/analytics/202509/shop_skus/performance", {
541
701
  query: {
542
702
  start_date_ge: args2.from ?? daysAgo(30),
@@ -551,6 +711,7 @@ var COMMANDS = [
551
711
  {
552
712
  name: "analytics sku",
553
713
  summary: "\u5355\u4E2A SKU \u7ECF\u8425\u660E\u7EC6\uFF08--sku sku_id \u5FC5\u586B\uFF1B--from --to --granularity\uFF09",
714
+ flags: ["from", "granularity", "sku", "to"],
554
715
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_skus/${args2.sku}/performance`, {
555
716
  query: {
556
717
  start_date_ge: args2.from ?? daysAgo(30),
@@ -562,6 +723,7 @@ var COMMANDS = [
562
723
  {
563
724
  name: "analytics product",
564
725
  summary: "\u5355\u4E2A\u5546\u54C1\u7ECF\u8425\u660E\u7EC6\uFF08--product product_id \u5FC5\u586B\uFF1B--from --to --granularity\uFF09",
726
+ flags: ["from", "granularity", "product", "to"],
565
727
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202509/shop_products/${args2.product}/performance`, {
566
728
  query: {
567
729
  start_date_ge: args2.from ?? daysAgo(30),
@@ -573,11 +735,13 @@ var COMMANDS = [
573
735
  {
574
736
  name: "analytics shop-hourly",
575
737
  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",
738
+ flags: ["date"],
576
739
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/analytics/202510/shop/performance/${args2.date ?? daysAgo(1)}/performance_per_hour`, {})
577
740
  },
578
741
  {
579
742
  name: "products list",
580
743
  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",
744
+ flags: ["page-size", "status"],
581
745
  listKey: "products",
582
746
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/products/search", {
583
747
  query: { page_size: args2["page-size"] ?? "20" },
@@ -587,11 +751,13 @@ var COMMANDS = [
587
751
  {
588
752
  name: "products get",
589
753
  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",
754
+ flags: ["product"],
590
755
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/product/202309/products/${args2.product}`, {})
591
756
  },
592
757
  {
593
758
  name: "products diagnose",
594
759
  summary: "\u5546\u54C1\u5B98\u65B9\u95EE\u9898\u8BCA\u65AD\uFF08--product \u9017\u53F7\u5206\u9694\u672C\u5E97\u81EA\u6709\u5546\u54C1 id\uFF09",
760
+ flags: ["product"],
595
761
  listKey: "products",
596
762
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/product/202405/products/diagnoses", {
597
763
  query: { product_ids: String(args2.product ?? "") }
@@ -600,6 +766,7 @@ var COMMANDS = [
600
766
  {
601
767
  name: "promotions list",
602
768
  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",
769
+ flags: ["force-status", "page-size", "page-token", "status"],
603
770
  listKey: "activities",
604
771
  run: ({ shop, args: args2 }) => {
605
772
  const st = args2.status ? String(args2.status) : "";
@@ -631,6 +798,7 @@ var COMMANDS = [
631
798
  {
632
799
  name: "finance statements",
633
800
  summary: "\u7ED3\u7B97\u5355\u5217\u8868",
801
+ flags: ["page-size", "sort-field", "sort-order"],
634
802
  listKey: "statements",
635
803
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/finance/202309/statements", {
636
804
  query: {
@@ -644,22 +812,26 @@ var COMMANDS = [
644
812
  {
645
813
  name: "seller status",
646
814
  summary: "\u8D26\u53F7/\u5E97\u94FA\u72B6\u6001\uFF08seller_status/shop_statuses/tax_form_status\uFF09",
815
+ flags: [],
647
816
  run: ({ shop }) => proxy("GET", shop, "/seller/202508/status", {})
648
817
  },
649
818
  {
650
819
  name: "seller permissions",
651
820
  summary: "\u5DF2\u6388\u6743\u6743\u9650\u5217\u8868",
821
+ flags: [],
652
822
  run: ({ shop }) => proxy("GET", shop, "/seller/202309/permissions", {})
653
823
  },
654
824
  // ===== 建品前置只读辅助命令 =====
655
825
  {
656
826
  name: "products prerequisites",
657
827
  summary: "\u5EFA\u54C1\u524D\u7F6E\u68C0\u67E5",
828
+ flags: [],
658
829
  run: ({ shop }) => proxy("GET", shop, "/product/202312/prerequisites", {})
659
830
  },
660
831
  {
661
832
  name: "categories recommend",
662
833
  summary: "\u7C7B\u76EE\u63A8\u8350\uFF08--title [--desc]\uFF09",
834
+ flags: ["desc", "title"],
663
835
  listKey: "categories",
664
836
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/categories/recommend", {
665
837
  body: {
@@ -671,6 +843,7 @@ var COMMANDS = [
671
843
  {
672
844
  name: "categories tree",
673
845
  summary: "\u7C7B\u76EE\u6811\uFF08--version v2\uFF09",
846
+ flags: ["version"],
674
847
  listKey: "categories",
675
848
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/product/202309/categories", {
676
849
  query: { category_version: args2.version ?? "v2" }
@@ -679,6 +852,7 @@ var COMMANDS = [
679
852
  {
680
853
  name: "categories attributes",
681
854
  summary: "\u7C7B\u76EE\u5C5E\u6027\uFF08--category\uFF09",
855
+ flags: ["category", "version"],
682
856
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/product/202309/categories/${args2.category}/attributes`, {
683
857
  query: { category_version: args2.version ?? "v2" }
684
858
  })
@@ -686,6 +860,7 @@ var COMMANDS = [
686
860
  {
687
861
  name: "categories rules",
688
862
  summary: "\u7C7B\u76EE\u89C4\u5219\uFF08--category\uFF09",
863
+ flags: ["category", "version"],
689
864
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/product/202309/categories/${args2.category}/rules`, {
690
865
  query: { category_version: args2.version ?? "v2" }
691
866
  })
@@ -693,6 +868,7 @@ var COMMANDS = [
693
868
  {
694
869
  name: "brands list",
695
870
  summary: "\u54C1\u724C\u5217\u8868\uFF08[--category] [--name]\uFF09",
871
+ flags: ["category", "name", "page-size"],
696
872
  listKey: "brands",
697
873
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/product/202309/brands", {
698
874
  query: {
@@ -705,6 +881,7 @@ var COMMANDS = [
705
881
  {
706
882
  name: "brands recommend",
707
883
  summary: "\u54C1\u724C\u63A8\u8350\uFF08--title\uFF09",
884
+ flags: ["title"],
708
885
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/brands/recommend", {
709
886
  body: { product_title: args2.title }
710
887
  })
@@ -712,18 +889,21 @@ var COMMANDS = [
712
889
  {
713
890
  name: "warehouses list",
714
891
  summary: "\u4ED3\u5E93\u5217\u8868\uFF08\u8DE8\u5883\u8BA4 sub_type CB_*\uFF09",
892
+ flags: [],
715
893
  listKey: "warehouses",
716
894
  run: ({ shop }) => proxy("GET", shop, "/logistics/202309/warehouses", {})
717
895
  },
718
896
  {
719
897
  name: "shipping-templates list",
720
898
  summary: "\u8FD0\u8D39\u6A21\u677F\u5217\u8868",
899
+ flags: [],
721
900
  listKey: "templates",
722
901
  run: ({ shop }) => proxy("GET", shop, "/logistics/202510/seller_templates", {})
723
902
  },
724
903
  {
725
904
  name: "delivery-options",
726
905
  summary: "\u4ED3\u5E93\u4EA4\u4ED8\u65B9\u5F0F\uFF08--warehouse\uFF09",
906
+ flags: ["warehouse"],
727
907
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/logistics/202309/warehouses/${args2.warehouse}/delivery_options`, {
728
908
  query: { scope: "PRODUCT" }
729
909
  })
@@ -732,6 +912,7 @@ var COMMANDS = [
732
912
  {
733
913
  name: "products set-price",
734
914
  summary: "\u6539 SKU \u552E\u4EF7\uFF08--product --sku --price [--currency MYR]\uFF09\u3002\u5199\u64CD\u4F5C,\u9ED8\u8BA4 dry-run",
915
+ flags: ["currency", "price", "product", "sku"],
735
916
  write: true,
736
917
  preview: ({ args: args2 }) => [{
737
918
  step: "\u6539\u4EF7",
@@ -745,6 +926,7 @@ var COMMANDS = [
745
926
  {
746
927
  name: "promotions create-flashsale",
747
928
  summary: "\u5EFA\u79D2\u6740\u6D3B\u52A8\uFF08--title --product --sku --price [--limit] [--hours 48]\uFF09\u3002\u4E24\u6B65:\u5EFA\u58F3+\u52A0\u54C1",
929
+ flags: ["hours", "limit", "per-user", "price", "product", "sku", "start-in-min", "title"],
748
930
  write: true,
749
931
  preview: ({ args: args2 }) => {
750
932
  const { begin, end } = flashWindow(args2);
@@ -769,6 +951,7 @@ var COMMANDS = [
769
951
  {
770
952
  name: "promotions create-discount",
771
953
  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",
954
+ flags: ["days", "price", "product", "title"],
772
955
  write: true,
773
956
  validate: validateCreateDiscount,
774
957
  preview: ({ args: args2 }) => {
@@ -835,6 +1018,7 @@ var COMMANDS = [
835
1018
  {
836
1019
  name: "promotions deactivate",
837
1020
  summary: "\u7EC8\u6B62/\u505C\u7528\u4FC3\u9500\u6D3B\u52A8\uFF08--id \u5FC5\u586B\uFF09\u3002\u4E0D\u53EF\u9006",
1021
+ flags: ["id"],
838
1022
  write: true,
839
1023
  preview: ({ args: args2 }) => [{ step: "\u7EC8\u6B62\u6D3B\u52A8", method: "POST", path: `/promotion/202309/activities/${args2.id}/deactivate`, body: {} }],
840
1024
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/promotion/202309/activities/${args2.id}/deactivate`, { body: {} })
@@ -842,6 +1026,7 @@ var COMMANDS = [
842
1026
  {
843
1027
  name: "products set-inventory",
844
1028
  summary: "\u6539 SKU \u5E93\u5B58\uFF08--product --sku --warehouse --quantity\uFF09\u3002\u5199\u64CD\u4F5C,\u9ED8\u8BA4 dry-run",
1029
+ flags: ["product", "quantity", "sku", "warehouse"],
845
1030
  write: true,
846
1031
  preview: ({ args: args2 }) => [{
847
1032
  step: "\u6539\u5E93\u5B58",
@@ -855,6 +1040,7 @@ var COMMANDS = [
855
1040
  {
856
1041
  name: "products create",
857
1042
  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",
1043
+ flags: ["body-file"],
858
1044
  write: true,
859
1045
  preview: () => [{ step: "listing_check \u9884\u68C0 \u2192 \u5EFA\u54C1", method: "POST", path: "/product/202309/products/listing_check", body: void 0 }],
860
1046
  precheck: ({ shop, args: args2 }) => proxy("POST", shop, "/product/202309/products/listing_check", { body: readBodyFile(args2["body-file"]) }),
@@ -869,6 +1055,7 @@ var COMMANDS = [
869
1055
  {
870
1056
  name: "products add-variant",
871
1057
  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",
1058
+ flags: ["body-file", "product"],
872
1059
  write: true,
873
1060
  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" }],
874
1061
  run: async ({ shop, args: args2 }) => {
@@ -882,6 +1069,7 @@ var COMMANDS = [
882
1069
  {
883
1070
  name: "products replace-images",
884
1071
  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",
1072
+ flags: ["body-file", "product"],
885
1073
  write: true,
886
1074
  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" }],
887
1075
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/product/202309/products/${args2.product}/partial_edit`, { body: { main_images: readBodyFile(args2["body-file"]).main_images } }),
@@ -890,6 +1078,7 @@ var COMMANDS = [
890
1078
  {
891
1079
  name: "products partial-edit",
892
1080
  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",
1081
+ flags: ["body-file", "product"],
893
1082
  write: true,
894
1083
  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"]) }],
895
1084
  run: ({ shop, args: args2 }) => proxy("POST", shop, `/product/202309/products/${args2.product}/partial_edit`, { body: readBodyFile(args2["body-file"]) }),
@@ -899,6 +1088,7 @@ var COMMANDS = [
899
1088
  {
900
1089
  name: "affiliate creators search",
901
1090
  summary: "\u8054\u76DF\u5E7F\u573A\u627E\u8FBE\u4EBA\uFF08--keyword\uFF0C--page-size 12|20\uFF0C\u9ED8\u8BA4 12\uFF09",
1091
+ flags: ["keyword", "page-size"],
902
1092
  listKey: "creators",
903
1093
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202508/marketplace_creators/search", {
904
1094
  query: { page_size: String(args2["page-size"] ?? 12) },
@@ -908,11 +1098,13 @@ var COMMANDS = [
908
1098
  {
909
1099
  name: "affiliate creators perf",
910
1100
  summary: "\u8FBE\u4EBA\u5E26\u8D27\u8868\u73B0\u8BE6\u60C5\uFF08--creator \u5FC5\u586B\uFF0Ccreator_open_id\uFF09",
1101
+ flags: ["creator"],
911
1102
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/affiliate_seller/202508/marketplace_creators/${args2.creator}`, {})
912
1103
  },
913
1104
  {
914
1105
  name: "affiliate target-collab search",
915
1106
  summary: "\u5B9A\u5411\u9080\u7EA6\u5217\u8868\uFF08--status collaboration_status\uFF0C\u5982 ONGOING/EXPIRING/VALID/CANCELING/COMPLETED\uFF1B--page-size\uFF09",
1107
+ flags: ["page-size", "status"],
916
1108
  listKey: "target_collaborations",
917
1109
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202508/target_collaborations/search", {
918
1110
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -922,11 +1114,13 @@ var COMMANDS = [
922
1114
  {
923
1115
  name: "affiliate target-collab query",
924
1116
  summary: "\u5B9A\u5411\u9080\u7EA6\u8BE6\u60C5\uFF08--id \u5FC5\u586B\uFF09",
1117
+ flags: ["id"],
925
1118
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/affiliate_seller/202508/target_collaborations/${args2.id}`, {})
926
1119
  },
927
1120
  {
928
1121
  name: "affiliate samples search",
929
1122
  summary: "\u6837\u54C1\u7533\u8BF7\u5217\u8868\uFF08--page-size\uFF09",
1123
+ flags: ["page-size"],
930
1124
  listKey: "sample_applications",
931
1125
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202508/sample_applications/search", {
932
1126
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -936,6 +1130,7 @@ var COMMANDS = [
936
1130
  {
937
1131
  name: "affiliate dm list",
938
1132
  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",
1133
+ flags: ["page-size", "status"],
939
1134
  listKey: "conversations",
940
1135
  run: ({ shop, args: args2 }) => proxy("GET", shop, "/affiliate_seller/202412/conversations", {
941
1136
  query: {
@@ -947,11 +1142,13 @@ var COMMANDS = [
947
1142
  {
948
1143
  name: "affiliate dm messages",
949
1144
  summary: "\u4F1A\u8BDD\u6D88\u606F\u8BB0\u5F55\uFF08--conversation \u5FC5\u586B\uFF0Cconversation_id\uFF09",
1145
+ flags: ["conversation"],
950
1146
  run: ({ shop, args: args2 }) => proxy("GET", shop, `/affiliate_seller/202412/conversation/${args2.conversation}/messages`, {})
951
1147
  },
952
1148
  {
953
1149
  name: "affiliate orders search",
954
1150
  summary: "\u8054\u76DF\u8BA2\u5355\u5217\u8868\uFF08\u4F63\u91D1\u660E\u7EC6\uFF0C--page-size\uFF09",
1151
+ flags: ["page-size"],
955
1152
  listKey: "orders",
956
1153
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202410/orders/search", {
957
1154
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -961,6 +1158,7 @@ var COMMANDS = [
961
1158
  {
962
1159
  name: "affiliate open-collab products-search",
963
1160
  summary: "\u516C\u5F00\u5408\u4F5C\u53EF\u8BBE\u4F63\u54C1\u5217\u8868\uFF08--page-size\uFF09",
1161
+ flags: ["page-size"],
964
1162
  listKey: "products",
965
1163
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202405/open_collaborations/products/search", {
966
1164
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -970,6 +1168,7 @@ var COMMANDS = [
970
1168
  {
971
1169
  name: "affiliate open-collab search",
972
1170
  summary: "\u516C\u5F00\u5408\u4F5C\u751F\u6548\u5217\u8868\uFF08\u542B current_commission\uFF0C--page-size\uFF09",
1171
+ flags: ["page-size"],
973
1172
  listKey: "open_collaborations",
974
1173
  run: ({ shop, args: args2 }) => proxy("POST", shop, "/affiliate_seller/202412/open_collaborations/search", {
975
1174
  query: { page_size: String(args2["page-size"] ?? 20) },
@@ -979,12 +1178,14 @@ var COMMANDS = [
979
1178
  {
980
1179
  name: "affiliate open-collab settings get",
981
1180
  summary: "\u516C\u5F00\u5408\u4F5C\u5168\u5C40\u8BBE\u7F6E\uFF08\u81EA\u52A8\u65B0\u589E\u54C1\u9ED8\u8BA4\u4F63\u91D1\uFF09",
1181
+ flags: [],
982
1182
  run: ({ shop }) => proxy("GET", shop, "/affiliate_seller/202409/open_collaboration_settings", {})
983
1183
  },
984
1184
  // ===== 联盟写命令(managing-affiliate-program,三道阀:dry-run 预览 → --apply 提交 → 回读) =====
985
1185
  {
986
1186
  name: "affiliate target-collab create",
987
1187
  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",
1188
+ flags: ["body-file"],
988
1189
  write: true,
989
1190
  preview: ({ args: args2 }) => [{
990
1191
  step: "\u53D1\u5B9A\u5411\u9080\u7EA6",
@@ -1003,6 +1204,7 @@ var COMMANDS = [
1003
1204
  {
1004
1205
  name: "affiliate target-collab update",
1005
1206
  summary: "\u6539\u5B9A\u5411\u9080\u7EA6\uFF08--id --body-file collab.json\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u63D0\u4EA4",
1207
+ flags: ["body-file", "id"],
1006
1208
  write: true,
1007
1209
  preview: ({ args: args2 }) => [{
1008
1210
  step: "\u6539\u5B9A\u5411\u9080\u7EA6",
@@ -1018,6 +1220,7 @@ var COMMANDS = [
1018
1220
  {
1019
1221
  name: "affiliate target-collab remove",
1020
1222
  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",
1223
+ flags: ["id"],
1021
1224
  write: true,
1022
1225
  preview: ({ args: args2 }) => [{
1023
1226
  step: "\u64A4\u56DE\u5B9A\u5411\u9080\u7EA6",
@@ -1030,6 +1233,7 @@ var COMMANDS = [
1030
1233
  {
1031
1234
  name: "affiliate target-collab link",
1032
1235
  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",
1236
+ flags: ["id"],
1033
1237
  write: true,
1034
1238
  preview: ({ args: args2 }) => [{
1035
1239
  step: "\u751F\u6210\u9080\u7EA6\u94FE\u63A5",
@@ -1042,6 +1246,7 @@ var COMMANDS = [
1042
1246
  {
1043
1247
  name: "affiliate dm open",
1044
1248
  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",
1249
+ flags: ["creator"],
1045
1250
  write: true,
1046
1251
  preview: ({ args: args2 }) => [{
1047
1252
  step: "\u5F00\u79C1\u4FE1\u4F1A\u8BDD",
@@ -1056,6 +1261,7 @@ var COMMANDS = [
1056
1261
  {
1057
1262
  name: "affiliate dm send",
1058
1263
  summary: "\u7ED9\u8FBE\u4EBA\u53D1\u79C1\u4FE1\uFF08--conversation --content [--msg-type TEXT]\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1264
+ flags: ["content", "conversation", "msg-type"],
1059
1265
  write: true,
1060
1266
  preview: ({ args: args2 }) => [{
1061
1267
  step: "\u53D1\u79C1\u4FE1",
@@ -1070,6 +1276,7 @@ var COMMANDS = [
1070
1276
  {
1071
1277
  name: "affiliate samples review",
1072
1278
  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",
1279
+ flags: ["id", "reject-reason", "result"],
1073
1280
  listKey: "sample_applications",
1074
1281
  write: true,
1075
1282
  preview: ({ args: args2 }) => [{
@@ -1090,6 +1297,7 @@ var COMMANDS = [
1090
1297
  {
1091
1298
  name: "affiliate open-collab set-commission",
1092
1299
  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",
1300
+ flags: ["product", "rate"],
1093
1301
  write: true,
1094
1302
  preview: ({ args: args2 }) => [{
1095
1303
  step: "\u8BBE\u6309\u54C1\u516C\u5F00\u5408\u4F5C\u4F63\u91D1",
@@ -1108,6 +1316,7 @@ var COMMANDS = [
1108
1316
  {
1109
1317
  name: "affiliate open-collab remove",
1110
1318
  summary: "\u79FB\u9664\u54C1\u7684\u516C\u5F00\u5408\u4F5C\uFF08--product product_id\uFF09\u3002\u9ED8\u8BA4 dry-run\uFF0C--apply \u624D\u53D1",
1319
+ flags: ["product"],
1111
1320
  write: true,
1112
1321
  preview: ({ args: args2 }) => [{
1113
1322
  step: "\u79FB\u9664\u516C\u5F00\u5408\u4F5C",
@@ -1120,6 +1329,7 @@ var COMMANDS = [
1120
1329
  {
1121
1330
  name: "affiliate open-collab settings edit",
1122
1331
  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",
1332
+ flags: ["enable", "rate"],
1123
1333
  write: true,
1124
1334
  preview: ({ args: args2 }) => [{
1125
1335
  step: "\u6539\u5168\u5C40\u9ED8\u8BA4",
@@ -1136,6 +1346,7 @@ var COMMANDS = [
1136
1346
  {
1137
1347
  name: "apps add",
1138
1348
  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",
1349
+ flags: ["app-key", "app-secret", "default", "label", "markets", "owner-entity", "redirect", "service-id", "type"],
1139
1350
  run: ({ args: args2 }) => adminApps("POST", {
1140
1351
  body: {
1141
1352
  serviceId: args2["service-id"],
@@ -1153,6 +1364,7 @@ var COMMANDS = [
1153
1364
  {
1154
1365
  name: "apps set-shop",
1155
1366
  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",
1367
+ flags: ["app", "note"],
1156
1368
  run: ({ args: args2 }) => {
1157
1369
  if (!args2.shop || !args2.app) return Promise.resolve({ ok: false, error: { code: "VALIDATION", message: "--shop \u548C --app\uFF08serviceId\uFF09\u5FC5\u586B", retryable: false } });
1158
1370
  return setShopApp(args2.shop, args2.app, args2.note);
@@ -1161,16 +1373,19 @@ var COMMANDS = [
1161
1373
  {
1162
1374
  name: "apps list",
1163
1375
  summary: "\u5217\u51FA\u5DF2\u767B\u8BB0\u7684 TikTok App\uFF08admin\uFF0C\u542B appKey/status\uFF09",
1376
+ flags: [],
1164
1377
  run: () => adminApps("GET")
1165
1378
  },
1166
1379
  {
1167
1380
  name: "apps available",
1168
1381
  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",
1382
+ flags: [],
1169
1383
  run: () => availableApps()
1170
1384
  },
1171
1385
  {
1172
1386
  name: "apps disable",
1173
1387
  summary: "\u505C\u7528\u4E00\u4E2A TikTok App\uFF08--id\uFF09",
1388
+ flags: ["id"],
1174
1389
  run: ({ args: args2 }) => adminApps("PATCH", { id: args2.id, body: { status: "disabled" } })
1175
1390
  },
1176
1391
  // ── auth link:skill 只传 --shop,backend 按 shop 解析用哪个 app(custom 映射→默认 public)。
@@ -1178,6 +1393,7 @@ var COMMANDS = [
1178
1393
  {
1179
1394
  name: "auth link",
1180
1395
  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",
1396
+ flags: ["app"],
1181
1397
  run: async ({ args: args2 }) => {
1182
1398
  if (!args2.shop && !args2.app) {
1183
1399
  return { ok: false, error: { code: "VALIDATION", message: "--shop\uFF08shopId\uFF09\u5FC5\u586B\uFF08\u6216 --app \u8986\u76D6\uFF09", retryable: false } };
@@ -1331,7 +1547,10 @@ function validateCreateDiscount(ctx2) {
1331
1547
  if (k === "sku") {
1332
1548
  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" };
1333
1549
  }
1334
- return { ok: false, message: `\u4E0D\u652F\u6301\u7684\u53C2\u6570 --${k}\uFF1B\u672C\u547D\u4EE4\u53EA\u8BA4 ${DISCOUNT_FLAGS.map((f) => "--" + f).join(" ")}` };
1550
+ const own = DISCOUNT_FLAGS.filter((f) => !GLOBAL_FLAGS.includes(f));
1551
+ const hint = suggestFlag(k, own, args2[k], Date.now(), "promotions create-discount");
1552
+ const tip = !hint ? "" : hint.startsWith("--") ? `\uFF08\u4F60\u662F\u60F3\u7528 ${hint} \u5417\uFF1F\uFF09` : `\uFF08${hint}\uFF09`;
1553
+ return { ok: false, message: `\u4E0D\u652F\u6301\u7684\u53C2\u6570 --${k}${tip}\uFF1B\u672C\u547D\u4EE4\u53EA\u8BA4 ${DISCOUNT_FLAGS.map((f) => "--" + f).join(" ")}` };
1335
1554
  }
1336
1555
  if (rest && rest.length > 0) {
1337
1556
  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"` };
@@ -1490,6 +1709,8 @@ async function dupSelfCheck(shop, title, submitted) {
1490
1709
 
1491
1710
  // src/index.ts
1492
1711
  var NO_SHOP_COMMANDS = /* @__PURE__ */ new Set(["auth link", "apps add", "apps list", "apps disable", "apps available", "apps set-shop"]);
1712
+ var SHOP_OPTIONAL_BUT_READ = /* @__PURE__ */ new Set(["auth link", "apps set-shop"]);
1713
+ var mentionsShop = (name) => !NO_SHOP_COMMANDS.has(name) || SHOP_OPTIONAL_BUT_READ.has(name);
1493
1714
  function parseFlags(tokens) {
1494
1715
  const args2 = {};
1495
1716
  const positional2 = [];
@@ -1512,7 +1733,7 @@ function parseFlags(tokens) {
1512
1733
  }
1513
1734
  return { args: args2, positional: positional2 };
1514
1735
  }
1515
- var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["apply", "default", "unprocessed", "enable", "force-status"]);
1736
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["apply", "default", "unprocessed", "enable", "force-status", "force-version"]);
1516
1737
  var FLAG_TRUE = /* @__PURE__ */ new Set(["true", "1", "yes", "on"]);
1517
1738
  var FLAG_FALSE = /* @__PURE__ */ new Set(["false", "0", "no", "off", ""]);
1518
1739
  function normalizeBooleanFlags(args2) {
@@ -1547,6 +1768,7 @@ function usage() {
1547
1768
  for (const c of COMMANDS) lines.push(` ${c.name.padEnd(22)} ${c.summary}`);
1548
1769
  lines.push(` ${"shops".padEnd(22)} \u67E5\u5F53\u524D\u767B\u5F55\u7528\u6237\u5DF2\u6388\u6743\u5E97\u94FA`);
1549
1770
  lines.push(` ${"images upload".padEnd(22)} \u4E0A\u4F20\u672C\u5730\u56FE\u7247\uFF08--file --use-case [--out]\uFF09`);
1771
+ 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");
1550
1772
  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");
1551
1773
  process.stderr.write(lines.join("\n") + "\n");
1552
1774
  }
@@ -1556,11 +1778,40 @@ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
1556
1778
  process.exit(argv.length === 0 ? 1 : 0);
1557
1779
  }
1558
1780
  var { args, positional } = parseFlags(argv);
1781
+ var wantsHelp = argv.includes("--help") || argv.includes("-h") || "help" in args;
1782
+ function rejectUnknownFlags(cmdName, allowed, rest, noShop = false) {
1783
+ const v = checkFlags(cmdName, args, allowed, rest, Date.now(), noShop);
1784
+ if (!v) return;
1785
+ out({
1786
+ ok: false,
1787
+ error: {
1788
+ code: "VALIDATION",
1789
+ message: v.message,
1790
+ retryable: false,
1791
+ unknown_flags: v.unknown_flags,
1792
+ extra_positional: v.extra_positional,
1793
+ allowed_flags: v.allowed_flags
1794
+ }
1795
+ });
1796
+ process.exit(1);
1797
+ }
1798
+ 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"] };
1799
+ 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 };
1559
1800
  if (positional[0] === "shops") {
1801
+ if (wantsHelp) {
1802
+ process.stderr.write(commandUsage(SHOPS));
1803
+ process.exit(0);
1804
+ }
1805
+ rejectUnknownFlags(SHOPS.name, SHOPS.flags, positional.slice(1), true);
1560
1806
  out(await listShops());
1561
1807
  process.exit(0);
1562
1808
  }
1563
1809
  if (positional[0] === "images" && positional[1] === "upload") {
1810
+ if (wantsHelp) {
1811
+ process.stderr.write(commandUsage(IMAGES_UPLOAD));
1812
+ process.exit(0);
1813
+ }
1814
+ rejectUnknownFlags(IMAGES_UPLOAD.name, IMAGES_UPLOAD.flags, positional.slice(2));
1564
1815
  if (!args.file) {
1565
1816
  out({ ok: false, error: { code: "VALIDATION", message: "\u7F3A\u5C11 --file", retryable: false } });
1566
1817
  process.exit(1);
@@ -1577,14 +1828,12 @@ if (positional[0] === "images" && positional[1] === "upload") {
1577
1828
  out(r);
1578
1829
  process.exit(0);
1579
1830
  }
1580
- var wantsHelp = argv.includes("--help") || argv.includes("-h");
1581
1831
  var match = findCommand(positional);
1582
1832
  if (wantsHelp) {
1583
- if (match) process.stderr.write(commandUsage(match.cmd));
1833
+ if (match) process.stderr.write(commandUsage({ ...match.cmd, noShop: !mentionsShop(match.cmd.name) }));
1584
1834
  else usage();
1585
1835
  process.exit(0);
1586
1836
  }
1587
- normalizeBooleanFlags(args);
1588
1837
  if (!match) {
1589
1838
  out({ ok: false, error: { code: "VALIDATION", message: `\u672A\u77E5\u547D\u4EE4: ${positional.join(" ")}`, retryable: false } });
1590
1839
  usage();
@@ -1596,6 +1845,8 @@ if (verdict && !verdict.ok) {
1596
1845
  out({ ok: false, error: { code: "VALIDATION", message: verdict.message, retryable: false } });
1597
1846
  process.exit(1);
1598
1847
  }
1848
+ rejectUnknownFlags(match.cmd.name, match.cmd.flags, match.rest, !mentionsShop(match.cmd.name));
1849
+ normalizeBooleanFlags(args);
1599
1850
  if (!args.shop && !NO_SHOP_COMMANDS.has(match.cmd.name)) {
1600
1851
  out({ ok: false, error: { code: "VALIDATION", message: "\u7F3A\u5C11 --shop\uFF08\u591A\u5E97\u663E\u5F0F\u8DEF\u7531\uFF09", retryable: false } });
1601
1852
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/tiktok-cli",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "TikTok Shop CLI for Optima agents — 经 tiktok-backend 操作店铺",
5
5
  "type": "module",
6
6
  "bin": {