@optima-chat/tiktok-cli 0.7.2 → 0.8.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 (2) hide show
  1. package/dist/index.js +94 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -75,16 +75,60 @@ async function listShops() {
75
75
  return { ok: false, error: { code: "BACKEND_UNREACHABLE", message: e.message, retryable: true } };
76
76
  }
77
77
  }
78
- async function createAuthLink() {
78
+ async function availableApps() {
79
79
  try {
80
- const r = await fetch(`${BACKEND}/auth/link`, { method: "POST", headers: authHeaders() });
80
+ const r = await fetch(`${BACKEND}/auth/apps`, { headers: authHeaders() });
81
81
  if (r.status === 401) return { ok: false, error: { code: "AUTH_EXPIRED", message: "\u672A\u767B\u5F55\u6216 token \u5931\u6548", retryable: false } };
82
- const body = await r.json();
82
+ const body = await r.json().catch(() => ({}));
83
+ if (!r.ok || body?.error) {
84
+ return { ok: false, error: { code: "VALIDATION", message: body?.error ?? `HTTP ${r.status}`, retryable: false } };
85
+ }
86
+ return { ok: true, data: body };
87
+ } catch (e) {
88
+ return { ok: false, error: { code: "BACKEND_UNREACHABLE", message: e.message, retryable: true } };
89
+ }
90
+ }
91
+ async function authLink(app) {
92
+ try {
93
+ const r = await fetch(`${BACKEND}/auth/link`, {
94
+ method: "POST",
95
+ headers: { "Content-Type": "application/json", ...authHeaders() },
96
+ body: JSON.stringify({ app })
97
+ });
98
+ if (r.status === 401) return { ok: false, error: { code: "AUTH_EXPIRED", message: "\u672A\u767B\u5F55\u6216 token \u5931\u6548", retryable: false } };
99
+ const body = await r.json().catch(() => ({}));
100
+ if (!r.ok || body?.error) {
101
+ return { ok: false, error: { code: "VALIDATION", message: body?.error ?? body?.message ?? `HTTP ${r.status}`, retryable: false } };
102
+ }
83
103
  return { ok: true, data: { authorize_url: body.authorize_url, expires_in_minutes: 15 } };
84
104
  } catch (e) {
85
105
  return { ok: false, error: { code: "BACKEND_UNREACHABLE", message: e.message, retryable: true } };
86
106
  }
87
107
  }
108
+ async function adminApps(method, opts = {}) {
109
+ const url = `${BACKEND}/admin/apps${opts.id ? `/${opts.id}` : ""}`;
110
+ const token = process.env.TIKTOK_ADMIN_TOKEN;
111
+ try {
112
+ const r = await fetch(url, {
113
+ method,
114
+ headers: {
115
+ "Content-Type": "application/json",
116
+ ...token ? { "x-admin-token": token } : {}
117
+ },
118
+ body: method === "GET" ? void 0 : JSON.stringify(opts.body ?? {})
119
+ });
120
+ const body = await r.json().catch(() => ({}));
121
+ if (r.status === 401 || r.status === 403) {
122
+ return { ok: false, error: { code: "VALIDATION", message: body?.error ?? "admin token \u65E0\u6548\u6216\u7F3A\u5931", retryable: false } };
123
+ }
124
+ if (!r.ok) {
125
+ return { ok: false, error: { code: "VALIDATION", message: body?.error ?? body?.message ?? `HTTP ${r.status}`, retryable: false } };
126
+ }
127
+ return { ok: true, data: body };
128
+ } catch (e) {
129
+ return { ok: false, error: { code: "BACKEND_UNREACHABLE", message: e.message, retryable: true } };
130
+ }
131
+ }
88
132
  async function fetchEvents(shop, query) {
89
133
  try {
90
134
  const qs = new URLSearchParams(query).toString();
@@ -836,6 +880,49 @@ var COMMANDS = [
836
880
  body: { auto_add_product: { commission_rate: Number(args2.rate), enable: args2.enable === "true" } }
837
881
  }),
838
882
  readback: ({ shop }) => proxy("GET", shop, "/affiliate_seller/202409/open_collaboration_settings", {})
883
+ },
884
+ // ── 多 App 管理(admin 配置操作,鉴权走 x-admin-token,不走商家 --shop,也不走三道阀 dry-run/--apply)──
885
+ {
886
+ name: "apps add",
887
+ summary: "\u767B\u8BB0\u4E00\u4E2A TikTok App\uFF08--service-id --app-key --app-secret --type --label\uFF0C[--markets a,b] [--redirect] [--owner-entity]\uFF09",
888
+ run: ({ args: args2 }) => adminApps("POST", {
889
+ body: {
890
+ serviceId: args2["service-id"],
891
+ appKey: args2["app-key"],
892
+ appSecret: args2["app-secret"],
893
+ appType: args2.type,
894
+ label: args2.label,
895
+ ...args2.markets ? { markets: args2.markets.split(",") } : {},
896
+ redirectUri: args2.redirect,
897
+ ...args2["owner-entity"] ? { ownerEntity: args2["owner-entity"] } : {}
898
+ }
899
+ })
900
+ },
901
+ {
902
+ name: "apps list",
903
+ summary: "\u5217\u51FA\u5DF2\u767B\u8BB0\u7684 TikTok App\uFF08admin\uFF0C\u542B appKey/status\uFF09",
904
+ run: () => adminApps("GET")
905
+ },
906
+ {
907
+ name: "apps available",
908
+ 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",
909
+ run: () => availableApps()
910
+ },
911
+ {
912
+ name: "apps disable",
913
+ summary: "\u505C\u7528\u4E00\u4E2A TikTok App\uFF08--id\uFF09",
914
+ run: ({ args: args2 }) => adminApps("PATCH", { id: args2.id, body: { status: "disabled" } })
915
+ },
916
+ // ── auth link(多 app:--app 必填 serviceId,决定 backend 用哪个 app_key/secret 生成授权链接)──
917
+ {
918
+ name: "auth link",
919
+ summary: "\u751F\u6210\u5E97\u94FA\u7ED1\u5B9A\u6388\u6743\u94FE\u63A5\uFF08--app serviceId \u5FC5\u586B\uFF0C\u53D1\u7ED9\u5546\u5BB6\u70B9\u51FB\uFF09",
920
+ run: async ({ args: args2 }) => {
921
+ if (!args2.app) {
922
+ return { ok: false, error: { code: "VALIDATION", message: "--app \u5FC5\u586B\uFF08serviceId\uFF09", retryable: false } };
923
+ }
924
+ return authLink(args2.app);
925
+ }
839
926
  }
840
927
  ];
841
928
  function sampleReviewBody(args2) {
@@ -901,6 +988,7 @@ function findCommand(tokens) {
901
988
  }
902
989
 
903
990
  // src/index.ts
991
+ var NO_SHOP_COMMANDS = /* @__PURE__ */ new Set(["auth link", "apps add", "apps list", "apps disable", "apps available"]);
904
992
  function parseFlags(tokens) {
905
993
  const args2 = {};
906
994
  const positional2 = [];
@@ -924,9 +1012,8 @@ function usage() {
924
1012
  const lines = ["tiktok <domain> <verb> --shop <shopId> [--flags]", "", "\u547D\u4EE4\uFF1A"];
925
1013
  for (const c of COMMANDS) lines.push(` ${c.name.padEnd(22)} ${c.summary}`);
926
1014
  lines.push(` ${"shops".padEnd(22)} \u67E5\u5F53\u524D\u767B\u5F55\u7528\u6237\u5DF2\u6388\u6743\u5E97\u94FA`);
927
- lines.push(` ${"auth link".padEnd(22)} \u751F\u6210\u5E97\u94FA\u7ED1\u5B9A\u6388\u6743\u94FE\u63A5\uFF08\u53D1\u7ED9\u5546\u5BB6\u70B9\u51FB\uFF09`);
928
1015
  lines.push(` ${"images upload".padEnd(22)} \u4E0A\u4F20\u672C\u5730\u56FE\u7247\uFF08--file --use-case [--out]\uFF09`);
929
- lines.push("", "\u901A\u7528\uFF1A--shop \u5FC5\u586B\uFF08\u9664 shops / auth link\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");
1016
+ 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");
930
1017
  process.stderr.write(lines.join("\n") + "\n");
931
1018
  }
932
1019
  var argv = process.argv.slice(2);
@@ -939,10 +1026,6 @@ if (positional[0] === "shops") {
939
1026
  out(await listShops());
940
1027
  process.exit(0);
941
1028
  }
942
- if (positional[0] === "auth" && positional[1] === "link") {
943
- out(await createAuthLink());
944
- process.exit(0);
945
- }
946
1029
  if (positional[0] === "images" && positional[1] === "upload") {
947
1030
  if (!args.file) {
948
1031
  out({ ok: false, error: { code: "VALIDATION", message: "\u7F3A\u5C11 --file", retryable: false } });
@@ -966,11 +1049,11 @@ if (!match) {
966
1049
  usage();
967
1050
  process.exit(1);
968
1051
  }
969
- if (!args.shop) {
1052
+ if (!args.shop && !NO_SHOP_COMMANDS.has(match.cmd.name)) {
970
1053
  out({ ok: false, error: { code: "VALIDATION", message: "\u7F3A\u5C11 --shop\uFF08\u591A\u5E97\u663E\u5F0F\u8DEF\u7531\uFF09", retryable: false } });
971
1054
  process.exit(1);
972
1055
  }
973
- var ctx = { shop: args.shop, args, actor: args.actor };
1056
+ var ctx = { shop: args.shop ?? "", args, actor: args.actor };
974
1057
  if (match.cmd.write && !("apply" in args)) {
975
1058
  if (match.cmd.precheck) {
976
1059
  const pc = await match.cmd.precheck(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/tiktok-cli",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "TikTok Shop CLI for Optima agents — 经 tiktok-backend 操作店铺",
5
5
  "type": "module",
6
6
  "bin": {