@optima-chat/tiktok-cli 0.17.0 → 0.18.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.
package/README.md CHANGED
@@ -13,6 +13,7 @@ tiktok <domain> <verb> --shop <shopId> [--flags]
13
13
  - `--shop` **必填**(多店显式路由,对应 A 轨「多店必须显式选店」教训;`auth link` 除外)
14
14
  - 默认 **JSON 输出**,统一信封 `{ ok, data?, error?, meta? }`
15
15
  - `TIKTOK_BACKEND_URL` 解析顺序:显式环境变量 > `OPTIMA_ENV` 映射(`cn-stage` → `https://tiktok-api.stage.optima.chat`;`cn-prod` → `https://tiktok-api.yzsgo.com`)> 本地默认 `http://127.0.0.1:3000`
16
+ - `OPTIMA_TRACE`(可选,COO 会话设为 `coo/<taskId>`):非空(trim 后)时每个打 backend 的请求都带 `X-Optima-Trace: <值>`,供用量按任务归因(Optima-Chat/optima-coo#645);没设 / 空白则不带。只发 backend,CLI 本就不直连第三方
16
17
 
17
18
  ## 命令(v1 只读)
18
19
 
package/dist/index.js CHANGED
@@ -129,6 +129,15 @@ function authHeaders() {
129
129
  }
130
130
  return {};
131
131
  }
132
+ function traceHeaders(env2 = process.env) {
133
+ const v = env2.OPTIMA_TRACE?.trim();
134
+ return v ? { "X-Optima-Trace": v } : {};
135
+ }
136
+ function backendFetch(url, init = {}) {
137
+ const headers = new Headers(init.headers);
138
+ for (const [k, v] of Object.entries(traceHeaders())) headers.set(k, v);
139
+ return fetch(url, { ...init, headers });
140
+ }
132
141
  function classify(status, body, shop) {
133
142
  const meta = { shop, tiktok_request_id: body?.request_id, http_status: status };
134
143
  if (status === 0 || body?.code === 0) {
@@ -148,7 +157,7 @@ async function proxy(method, shop, path, opts = {}) {
148
157
  const qs = opts.query ? "?" + new URLSearchParams(opts.query) : "";
149
158
  const url = `${BACKEND}/proxy/${shop}${path}${qs}`;
150
159
  try {
151
- const r = await fetch(url, {
160
+ const r = await backendFetch(url, {
152
161
  method,
153
162
  headers: {
154
163
  "Content-Type": "application/json",
@@ -165,7 +174,7 @@ async function proxy(method, shop, path, opts = {}) {
165
174
  }
166
175
  async function listShops() {
167
176
  try {
168
- const r = await fetch(`${BACKEND}/auth/shops`, { headers: authHeaders() });
177
+ const r = await backendFetch(`${BACKEND}/auth/shops`, { headers: authHeaders() });
169
178
  if (r.status === 401) return { ok: false, error: { code: "AUTH_EXPIRED", message: "\u672A\u767B\u5F55\u6216 token \u5931\u6548", retryable: false } };
170
179
  const body = await r.json();
171
180
  return { ok: true, data: body.shops };
@@ -176,7 +185,7 @@ async function listShops() {
176
185
  async function setShopApp(shopId, serviceId, note) {
177
186
  const token = process.env.TIKTOK_ADMIN_TOKEN;
178
187
  try {
179
- const r = await fetch(`${BACKEND}/admin/apps/shop-map`, {
188
+ const r = await backendFetch(`${BACKEND}/admin/apps/shop-map`, {
180
189
  method: "PUT",
181
190
  headers: { "Content-Type": "application/json", ...token ? { "x-admin-token": token } : {} },
182
191
  body: JSON.stringify({ shopId, serviceId, note })
@@ -190,7 +199,7 @@ async function setShopApp(shopId, serviceId, note) {
190
199
  }
191
200
  async function availableApps() {
192
201
  try {
193
- const r = await fetch(`${BACKEND}/auth/apps`, { headers: authHeaders() });
202
+ const r = await backendFetch(`${BACKEND}/auth/apps`, { headers: authHeaders() });
194
203
  if (r.status === 401) return { ok: false, error: { code: "AUTH_EXPIRED", message: "\u672A\u767B\u5F55\u6216 token \u5931\u6548", retryable: false } };
195
204
  const body = await r.json().catch(() => ({}));
196
205
  if (!r.ok || body?.error) {
@@ -203,7 +212,7 @@ async function availableApps() {
203
212
  }
204
213
  async function authLink(opts) {
205
214
  try {
206
- const r = await fetch(`${BACKEND}/auth/link`, {
215
+ const r = await backendFetch(`${BACKEND}/auth/link`, {
207
216
  method: "POST",
208
217
  headers: { "Content-Type": "application/json", ...authHeaders() },
209
218
  body: JSON.stringify({ shop: opts.shop, app: opts.app })
@@ -222,7 +231,7 @@ async function adminApps(method, opts = {}) {
222
231
  const url = `${BACKEND}/admin/apps${opts.id ? `/${opts.id}` : ""}`;
223
232
  const token = process.env.TIKTOK_ADMIN_TOKEN;
224
233
  try {
225
- const r = await fetch(url, {
234
+ const r = await backendFetch(url, {
226
235
  method,
227
236
  headers: {
228
237
  "Content-Type": "application/json",
@@ -245,7 +254,7 @@ async function adminApps(method, opts = {}) {
245
254
  async function fetchEvents(shop, query) {
246
255
  try {
247
256
  const qs = new URLSearchParams(query).toString();
248
- const r = await fetch(`${BACKEND}/events/${shop}${qs ? "?" + qs : ""}`, { headers: authHeaders() });
257
+ const r = await backendFetch(`${BACKEND}/events/${shop}${qs ? "?" + qs : ""}`, { headers: authHeaders() });
249
258
  if (r.status === 401) return { ok: false, error: { code: "AUTH_EXPIRED", message: "\u672A\u767B\u5F55\u6216 token \u5931\u6548", retryable: false } };
250
259
  if (r.status === 404) return { ok: false, error: { code: "SHOP_NOT_AUTHORIZED", message: "\u672A\u627E\u5230\u4F60\u540D\u4E0B\u7684\u5DF2\u6388\u6743\u5E97\u94FA", retryable: false }, meta: { shop } };
251
260
  const body = await r.json();
@@ -256,7 +265,7 @@ async function fetchEvents(shop, query) {
256
265
  }
257
266
  async function ackEvents(shop, body) {
258
267
  try {
259
- const r = await fetch(`${BACKEND}/events/${shop}/ack`, {
268
+ const r = await backendFetch(`${BACKEND}/events/${shop}/ack`, {
260
269
  method: "POST",
261
270
  headers: { "Content-Type": "application/json", ...authHeaders() },
262
271
  body: JSON.stringify(body)
@@ -275,7 +284,7 @@ async function uploadImage(shop, filePath, useCase) {
275
284
  const form = new FormData();
276
285
  form.append("data", new Blob([buf]), basename(filePath));
277
286
  form.append("use_case", useCase);
278
- const r = await fetch(`${BACKEND}/proxy-upload/${shop}/image`, { method: "POST", headers: authHeaders(), body: form });
287
+ const r = await backendFetch(`${BACKEND}/proxy-upload/${shop}/image`, { method: "POST", headers: authHeaders(), body: form });
279
288
  const body = await r.json().catch(() => ({}));
280
289
  if (body?.code === 0) return { ok: true, data: { uri: body.data?.uri, url: body.data?.url, use_case: useCase }, meta: { shop } };
281
290
  return { ok: false, error: { code: r.status === 401 ? "AUTH_EXPIRED" : "TIKTOK_ERROR", message: body?.message ?? body?.error ?? `HTTP ${r.status}`, retryable: false }, meta: { shop } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/tiktok-cli",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "TikTok Shop CLI for Optima agents — 经 tiktok-backend 操作店铺",
5
5
  "type": "module",
6
6
  "bin": {