siluzan-tso-cli 1.1.46 → 1.1.47-beta.2

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 (27) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +103 -46
  3. package/dist/skill/SKILL.md +1 -1
  4. package/dist/skill/_meta.json +2 -2
  5. package/dist/skill/references/accounts/accounts-balance-stats.md +1 -1
  6. package/dist/skill/references/accounts/accounts-list.md +1 -1
  7. package/dist/skill/references/accounts/accounts-permissions.md +4 -2
  8. package/dist/skill/references/accounts/accounts.md +1 -1
  9. package/dist/skill/references/accounts/finance.md +1 -1
  10. package/dist/skill/references/accounts/open-account-by-media.md +1 -1
  11. package/dist/skill/references/accounts/open-account-google-ui.md +1 -1
  12. package/dist/skill/references/analytics/account-analytics.md +8 -7
  13. package/dist/skill/references/analytics/rag.md +1 -1
  14. package/dist/skill/references/analytics/reporting.md +5 -5
  15. package/dist/skill/references/core/agent-conventions.md +4 -4
  16. package/dist/skill/references/core/setup.md +5 -5
  17. package/dist/skill/references/misc/tso-home.md +2 -2
  18. package/dist/skill/references/operations/hosted-automation-bing.md +1 -1
  19. package/dist/skill/references/operations/hosted-automation-tiktok.md +2 -2
  20. package/dist/skill/references/report-templates/bing-period-report.md +4 -4
  21. package/dist/skill/report-templates/bing-period-report.md +4 -4
  22. package/dist/skill/report-templates/report-template-dark.html +1 -1
  23. package/dist/skill/report-templates/report-template-mobile.html +1 -1
  24. package/dist/skill/report-templates/report-template-print.html +1 -1
  25. package/dist/skill/scripts/install.ps1 +3 -3
  26. package/dist/skill/scripts/install.sh +3 -3
  27. package/package.json +1 -1
package/README.md CHANGED
@@ -43,7 +43,7 @@ HTML 报告模板外部资源:**ECharts** 统一使用 `https://staticpn.siluz
43
43
  在**用户的目标项目根目录**执行(根据用户使用的助手选择 `--ai`):
44
44
 
45
45
  ```bash
46
- npm install -g siluzan-tso-cli
46
+ npm install -g siluzan-tso-cli@beta
47
47
  siluzan-tso init --ai cursor # 写入 Cursor(默认)
48
48
  siluzan-tso init --ai cursor,claude # 同时写入多个平台
49
49
  siluzan-tso init --ai all # 写入所有支持的平台
@@ -51,6 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
+ > **注意**:当前为测试版(1.1.47-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
55
 
55
56
  | 助手 | 建议 `--ai` |
56
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -4026,7 +4026,7 @@ var DEFAULT_API_BASE;
4026
4026
  var init_defaults = __esm({
4027
4027
  "src/config/defaults.ts"() {
4028
4028
  "use strict";
4029
- DEFAULT_API_BASE = "https://tso-api.siluzan.com";
4029
+ DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
4030
4030
  }
4031
4031
  });
4032
4032
 
@@ -132184,23 +132184,6 @@ function formatLocalYmd(d) {
132184
132184
  const day = String(d.getDate()).padStart(2, "0");
132185
132185
  return `${y}-${m}-${day}`;
132186
132186
  }
132187
- function bingRangeTouchesTodayOrYesterday(startDate, endDate) {
132188
- const s = parseLocalYmd(startDate);
132189
- const e = parseLocalYmd(endDate);
132190
- if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime()) || s > e) {
132191
- return true;
132192
- }
132193
- const today = stripLocalDate(/* @__PURE__ */ new Date());
132194
- const yesterday = new Date(today);
132195
- yesterday.setDate(yesterday.getDate() - 1);
132196
- for (let cur = new Date(s); cur <= e; cur.setDate(cur.getDate() + 1)) {
132197
- const c = stripLocalDate(cur);
132198
- if (c.getTime() === today.getTime() || c.getTime() === yesterday.getTime()) {
132199
- return true;
132200
- }
132201
- }
132202
- return false;
132203
- }
132204
132187
  function resolveBingDateRange(start, end, allowToday = false) {
132205
132188
  if (start && end) {
132206
132189
  const s = parseLocalYmd(start);
@@ -132213,12 +132196,6 @@ function resolveBingDateRange(start, end, allowToday = false) {
132213
132196
  console.error("\n\u274C \u5F00\u59CB\u65E5\u671F\u4E0D\u80FD\u665A\u4E8E\u7ED3\u675F\u65E5\u671F\u3002\n");
132214
132197
  process.exit(1);
132215
132198
  }
132216
- if (!allowToday && bingRangeTouchesTodayOrYesterday(start, end)) {
132217
- console.error(
132218
- "\n\u274C Bing \u62A5\u8868\u65F6\u95F4\u8303\u56F4\u4E0D\u80FD\u5305\u542B\u300C\u4ECA\u5929\u300D\u6216\u300C\u6628\u5929\u300D\uFF08\u63A5\u53E3\u9650\u5236\uFF09\u3002\u8BF7\u6539\u7528\u66F4\u65E9\u7684\u7ED3\u675F\u65E5\u671F\uFF0C\u4F8B\u5982\u524D\u5929\u53CA\u4EE5\u524D\uFF1B\u82E5\u9700\u67E5\u4ECA\u5929/\u8FD1\u5B9E\u65F6\u6570\u636E\uFF0C\u5BF9 campaigns/ad-groups/ads \u4F20 --aggregation \u6216 --return-only-complete-data\u3002\n"
132219
- );
132220
- process.exit(1);
132221
- }
132222
132199
  return { startDate: start, endDate: end };
132223
132200
  }
132224
132201
  if (!start && !end) {
@@ -132227,13 +132204,13 @@ function resolveBingDateRange(start, end, allowToday = false) {
132227
132204
  return { startDate: today, endDate: today };
132228
132205
  }
132229
132206
  const endD = stripLocalDate(/* @__PURE__ */ new Date());
132230
- endD.setDate(endD.getDate() - 2);
132207
+ endD.setDate(endD.getDate() - 1);
132231
132208
  const startD = new Date(endD);
132232
132209
  startD.setDate(startD.getDate() - 6);
132233
132210
  return { startDate: formatLocalYmd(startD), endDate: formatLocalYmd(endD) };
132234
132211
  }
132235
132212
  console.error(
132236
- "\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u533A\u95F4\uFF08\u622A\u81F3\u524D\u5929\u5171 7 \u5929\uFF09\u3002\n"
132213
+ "\n\u274C --start \u4E0E --end \u987B\u540C\u65F6\u4F20\u5165\uFF0C\u6216\u540C\u65F6\u7701\u7565\u4EE5\u4F7F\u7528\u9ED8\u8BA4\u533A\u95F4\uFF08\u622A\u81F3\u6628\u5929\u5171 7 \u5929\uFF09\u3002\n"
132237
132214
  );
132238
132215
  process.exit(1);
132239
132216
  }
@@ -132304,9 +132281,6 @@ async function enrichBingOverviewPreviousPeriod(config, accountId, record2, star
132304
132281
  }
132305
132282
  const prevRange = calcBingPreviousPeriodRange(startDate, endDate);
132306
132283
  if (!prevRange) return record2;
132307
- if (bingRangeTouchesTodayOrYesterday(prevRange.startDate, prevRange.endDate)) {
132308
- return record2;
132309
- }
132310
132284
  try {
132311
132285
  const params = new URLSearchParams({
132312
132286
  startDate: prevRange.startDate,
@@ -133340,7 +133314,7 @@ function registerBatchCommand2(sectionHelp, aliasHelp) {
133340
133314
  "\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6 `<section>-<accountId>.json` + `report-manifest-<accountId>.json`"
133341
133315
  ).option("-t, --token <token>", "Auth Token").option(
133342
133316
  "--start <date>",
133343
- "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF1B\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929\uFF0C\u9664\u975E\u4F20\u4E86 --aggregation \u6216 --return-only-complete-data\uFF09"
133317
+ "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\u7565\uFF1B\u53EF\u542B\u6628\u5929/\u4ECA\u5929\uFF0C\u6570\u636E\u53EF\u80FD\u4E0D\u5B8C\u6574\uFF1B\u7701\u7565=\u622A\u81F3\u6628\u5929\u8FD1 7 \u5929\uFF09"
133344
133318
  ).option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
133345
133319
  "--sections <list>",
133346
133320
  `\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,campaigns,keywords\uFF1B\u7701\u7565=\u5168\u90E8 11 \u4E2A\u3002\u53EF\u9009\uFF1A${sectionHelp}\uFF1B\u522B\u540D\uFF1A${aliasHelp}`
@@ -133350,10 +133324,10 @@ function registerBatchCommand2(sectionHelp, aliasHelp) {
133350
133324
  (v) => parseInt(v, 10)
133351
133325
  ).option("--concurrency <n>", "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF0C\u4E0A\u9650 16", (v) => parseInt(v, 10)).option(
133352
133326
  "--aggregation <value>",
133353
- "\u4EC5 campaigns/ad-groups/ads\uFF1A\u5B98\u65B9 ReportAggregation\uFF08Summary \u9ED8\u8BA4/Daily/Hourly/Weekly \u7B49\uFF09\uFF1B\u4F20\u5165\u540E\u81EA\u52A8\u5141\u8BB8\u65E5\u671F\u542B\u4ECA\u5929/\u6628\u5929\uFF0C\u7528\u4E8E\u8FD1\u5B9E\u65F6\u76D1\u63A7\u800C\u975Eperiodic\u62A5\u544A"
133327
+ "\u4EC5 campaigns/ad-groups/ads\uFF1A\u5B98\u65B9 ReportAggregation\uFF08Summary \u9ED8\u8BA4/Daily/Hourly/Weekly \u7B49\uFF09\uFF1B\u4F20\u5165\u4E14\u7701\u7565\u65E5\u671F\u65F6\u9ED8\u8BA4\u4ECA\u5929\u5F53\u5929\uFF0C\u7528\u4E8E\u8FD1\u5B9E\u65F6\u76D1\u63A7"
133354
133328
  ).option(
133355
133329
  "--return-only-complete-data <bool>",
133356
- "\u4EC5 campaigns/ad-groups/ads\uFF1Atrue/false\uFF0C\u663E\u5F0F\u8986\u76D6\u7F51\u5173\u9ED8\u8BA4\u7684\u6309\u65E5\u671F\u81EA\u52A8\u5224\u65AD\uFF1B\u4F20\u5165\u540E\u81EA\u52A8\u5141\u8BB8\u65E5\u671F\u542B\u4ECA\u5929/\u6628\u5929"
133330
+ "\u4EC5 campaigns/ad-groups/ads\uFF1Atrue/false\uFF0C\u663E\u5F0F\u8986\u76D6\u7F51\u5173\u6309\u65E5\u671F\u81EA\u52A8\u5224\u65AD\uFF1B\u542B\u6628\u5929/\u4ECA\u5929\u65F6\u4E0D\u8981\u4F20 true"
133357
133331
  ).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
133358
133332
  async (opts) => {
133359
133333
  await runAllBingSections({
@@ -133367,7 +133341,7 @@ function registerBingAnalysisCommands(program2) {
133367
133341
  const sectionHelp = BING_SECTION_NAMES.join(", ");
133368
133342
  const aliasHelp = Object.keys(BING_SECTION_ALIASES).join(", ");
133369
133343
  const root = program2.command("bing-analysis").description(
133370
- "Bing\uFF08BingV2\uFF09\u8D26\u6237\u5206\u6790\uFF1A\u6279\u91CF\u62C9\u53D6 Section \u62A5\u8868\u6570\u636E\uFF08\u9ED8\u8BA4\uFF0C\u65E5\u671F\u4E0D\u53EF\u542B\u4ECA\u5929/\u6628\u5929\uFF0C\u4F20 --aggregation \u53EF\u89E3\u9501\uFF09\u3001\u7531 render \u751F\u6210 bing-period-report.html \u7EC8\u7A3F\uFF0C\u6216\u67E5\u8BE2\u8D26\u6237\u72B6\u6001/\u7CFB\u5217\u4E0E\u5E7F\u544A\u539F\u59CB\u5B9E\u4F53/\u62D2\u5BA1 Bulk \u5DE1\u68C0/IO/\u6708\u6D88\u8017"
133344
+ "Bing\uFF08BingV2\uFF09\u8D26\u6237\u5206\u6790\uFF1A\u6279\u91CF\u62C9\u53D6 Section \u62A5\u8868\u6570\u636E\uFF08\u9ED8\u8BA4\u622A\u81F3\u6628\u5929\u8FD1 7 \u5929\uFF0C\u53EF\u542B\u6628\u5929/\u4ECA\u5929\uFF09\u3001\u7531 render \u751F\u6210 bing-period-report.html \u7EC8\u7A3F\uFF0C\u6216\u67E5\u8BE2\u8D26\u6237\u72B6\u6001/\u7CFB\u5217\u4E0E\u5E7F\u544A\u539F\u59CB\u5B9E\u4F53/\u62D2\u5BA1 Bulk \u5DE1\u68C0/IO/\u6708\u6D88\u8017"
133371
133345
  );
133372
133346
  root.addCommand(registerBatchCommand2(sectionHelp, aliasHelp), { isDefault: true });
133373
133347
  root.command("render").description(
@@ -134256,6 +134230,87 @@ function parseTikTokNumber(value) {
134256
134230
  const n = Number(value);
134257
134231
  return Number.isFinite(n) ? n : void 0;
134258
134232
  }
134233
+ var TIKTOK_VALUE_METRICS = ["total_purchase_value"];
134234
+ var TIKTOK_OFFICIAL_ROAS_METRICS = [
134235
+ "complete_payment_roas",
134236
+ "total_active_pay_roas"
134237
+ ];
134238
+ var TIKTOK_ROAS_COMPANION_METRICS = [
134239
+ "total_purchase_value",
134240
+ "complete_payment_roas",
134241
+ "total_active_pay_roas"
134242
+ ];
134243
+ function splitTikTokCsv(raw) {
134244
+ return raw.split(",").map((x) => x.trim()).filter(Boolean);
134245
+ }
134246
+ function pickFirstMetric(row, keys) {
134247
+ let fallback;
134248
+ for (const key of keys) {
134249
+ const value = parseTikTokNumber(row[key]);
134250
+ if (value === void 0) continue;
134251
+ if (value > 0) return { key, value };
134252
+ fallback ??= { key, value };
134253
+ }
134254
+ return fallback;
134255
+ }
134256
+ function expandTikTokOfficialReportMetrics(metrics) {
134257
+ const parts = splitTikTokCsv(metrics);
134258
+ const needCompanions = parts.includes("complete_payment") || TIKTOK_ROAS_COMPANION_METRICS.some((k) => parts.includes(k));
134259
+ if (!needCompanions) return parts.join(",");
134260
+ for (const extra of TIKTOK_ROAS_COMPANION_METRICS) {
134261
+ if (!parts.includes(extra)) parts.push(extra);
134262
+ }
134263
+ return parts.join(",");
134264
+ }
134265
+ function pickTikTokOfficialRoas(row) {
134266
+ return pickFirstMetric(row, TIKTOK_OFFICIAL_ROAS_METRICS);
134267
+ }
134268
+ function pickTikTokRevenue(row) {
134269
+ const picked = pickFirstMetric(row, TIKTOK_VALUE_METRICS);
134270
+ if (!picked) return { revenue: void 0, revenueSource: void 0 };
134271
+ return { revenue: picked.value, revenueSource: picked.key };
134272
+ }
134273
+ function attachTikTokRoasFields(row) {
134274
+ const spend = parseTikTokNumber(row.spend);
134275
+ const official = pickTikTokOfficialRoas(row);
134276
+ const purchase = pickTikTokRevenue(row);
134277
+ if (purchase.revenue !== void 0) {
134278
+ row.revenue = purchase.revenue;
134279
+ row.revenueSource = purchase.revenueSource;
134280
+ } else if (official && official.value > 0 && spend !== void 0 && spend > 0) {
134281
+ row.revenue = official.value * spend;
134282
+ row.revenueSource = `${official.key}*spend`;
134283
+ }
134284
+ const revenue = parseTikTokNumber(row.revenue);
134285
+ if (official && official.value > 0) {
134286
+ row.roas = official.value;
134287
+ row.roasSource = official.key;
134288
+ } else if (spend !== void 0 && spend > 0 && revenue !== void 0) {
134289
+ row.roas = revenue / spend;
134290
+ row.roasSource = "computed";
134291
+ } else if (official) {
134292
+ row.roas = official.value;
134293
+ row.roasSource = official.key;
134294
+ }
134295
+ }
134296
+ function summarizeTikTokOfficialReport(items2) {
134297
+ let spend = 0;
134298
+ let revenue = 0;
134299
+ let hasRevenue = false;
134300
+ for (const row of items2) {
134301
+ spend += parseTikTokNumber(row.spend) ?? 0;
134302
+ const rowRevenue = parseTikTokNumber(row.revenue);
134303
+ if (rowRevenue === void 0) continue;
134304
+ revenue += rowRevenue;
134305
+ hasRevenue = true;
134306
+ }
134307
+ return {
134308
+ spend,
134309
+ revenue: hasRevenue ? revenue : 0,
134310
+ roas: spend > 0 && hasRevenue ? revenue / spend : void 0,
134311
+ itemCount: items2.length
134312
+ };
134313
+ }
134259
134314
  function flattenTikTokReportRow(row) {
134260
134315
  const dims = row.dimensions && typeof row.dimensions === "object" && !Array.isArray(row.dimensions) ? row.dimensions : {};
134261
134316
  const metrics = row.metrics && typeof row.metrics === "object" && !Array.isArray(row.metrics) ? row.metrics : {};
@@ -134264,11 +134319,7 @@ function flattenTikTokReportRow(row) {
134264
134319
  const n = parseTikTokNumber(v);
134265
134320
  out[k] = n === void 0 ? v : n;
134266
134321
  }
134267
- const spend = parseTikTokNumber(out.spend);
134268
- const payment = parseTikTokNumber(out.complete_payment);
134269
- if (spend !== void 0 && spend > 0 && payment !== void 0) {
134270
- out.roas = payment / spend;
134271
- }
134322
+ attachTikTokRoasFields(out);
134272
134323
  return out;
134273
134324
  }
134274
134325
  function pickAdGroupBidField(bidType) {
@@ -134476,6 +134527,7 @@ async function fetchAdEntities(config, opts) {
134476
134527
  );
134477
134528
  }
134478
134529
  async function fetchOfficialReport(config, opts) {
134530
+ const metrics = expandTikTokOfficialReportMetrics(opts.metrics);
134479
134531
  const base = requireTikTokApiUrl(config);
134480
134532
  const raw = await fetchTikTokPagedList(
134481
134533
  config,
@@ -134487,7 +134539,7 @@ async function fetchOfficialReport(config, opts) {
134487
134539
  endDate: opts.endDate,
134488
134540
  dataLevel: opts.dataLevel,
134489
134541
  dimensions: opts.dimensions,
134490
- metrics: opts.metrics,
134542
+ metrics,
134491
134543
  serviceType: opts.serviceType,
134492
134544
  pageNum,
134493
134545
  pageSize
@@ -134497,7 +134549,8 @@ async function fetchOfficialReport(config, opts) {
134497
134549
  );
134498
134550
  return {
134499
134551
  items: raw.items.map(flattenTikTokReportRow),
134500
- totalNumber: raw.totalNumber
134552
+ totalNumber: raw.totalNumber,
134553
+ metrics
134501
134554
  };
134502
134555
  }
134503
134556
 
@@ -134751,7 +134804,7 @@ async function runTikTokOfficialReport(opts) {
134751
134804
  const id = assertTikTokAccountId(opts.account);
134752
134805
  const dataLevel = opts.dataLevel.trim();
134753
134806
  const dimensions = opts.dimensions.trim();
134754
- const metrics = opts.metrics.trim();
134807
+ const metrics = expandTikTokOfficialReportMetrics(opts.metrics.trim());
134755
134808
  if (!dataLevel || !dimensions || !metrics) {
134756
134809
  console.error("\n\u274C --data-level / --dimensions / --metrics \u5747\u4E3A\u5FC5\u586B\u3002\n");
134757
134810
  process.exit(1);
@@ -134776,6 +134829,7 @@ async function runTikTokOfficialReport(opts) {
134776
134829
  } catch (err) {
134777
134830
  fail("\u67E5\u8BE2 TikTok \u5B98\u65B9\u62A5\u8868\u5931\u8D25", err);
134778
134831
  }
134832
+ const totals = summarizeTikTokOfficialReport(result.items);
134779
134833
  const payload = {
134780
134834
  account: id,
134781
134835
  dataLevel,
@@ -134784,6 +134838,7 @@ async function runTikTokOfficialReport(opts) {
134784
134838
  metrics,
134785
134839
  startDate,
134786
134840
  endDate,
134841
+ totals,
134787
134842
  ...wrapListJson({
134788
134843
  page: 1,
134789
134844
  pageSize,
@@ -134809,9 +134864,11 @@ TikTok \u5B98\u65B9\u62A5\u8868 ${dataLevel} ${startDate}~${endDate}\uFF08${id}\
134809
134864
  console.log(" \u65E0\u62A5\u8868\u884C\u3002\n");
134810
134865
  return;
134811
134866
  }
134812
- const dimKeys = dimensions.split(",").map((x) => x.trim()).filter(Boolean);
134813
- const metricKeys = metrics.split(",").map((x) => x.trim()).filter(Boolean);
134814
- const keys = [...dimKeys, ...metricKeys, "roas"].filter((k, i, a) => a.indexOf(k) === i);
134867
+ const dimKeys = splitTikTokCsv(dimensions);
134868
+ const metricKeys = splitTikTokCsv(metrics);
134869
+ const keys = [...dimKeys, ...metricKeys, "revenue", "revenueSource", "roas", "roasSource"].filter(
134870
+ (k, i, a) => a.indexOf(k) === i
134871
+ );
134815
134872
  printCliTable(
134816
134873
  result.items.map((r) => {
134817
134874
  const row = {};
@@ -134826,7 +134883,7 @@ TikTok \u5B98\u65B9\u62A5\u8868 ${dataLevel} ${startDate}~${endDate}\uFF08${id}\
134826
134883
  const sampleSpend = parseTikTokNumber(result.items[0]?.spend);
134827
134884
  if (sampleSpend !== void 0) {
134828
134885
  console.log(
134829
- " spend \u5DF2\u8F6C\u6570\u5B57\uFF1BROAS = complete_payment / spend\uFF08spend=0 \u4E0D\u9664\uFF09\u3002\u52FF\u6DF7\u52A0\u4E0D\u540C DataLevel\u3002\n"
134886
+ " spend \u5DF2\u8F6C\u6570\u5B57\u3002ROAS \u4F18\u5148\u5B98\u65B9\u5217 complete_payment_roas / total_active_pay_roas\uFF0C\u5426\u5219\u7528 total_purchase_value/spend\uFF08\u89C1 roasSource\uFF09\u3002complete_payment \u662F\u7F51\u9875\u5B8C\u4ED8\u6B21\u6570\uFF0C\u4E0D\u662F\u91D1\u989D\u3002\u591A\u884C\u6C47\u603B\u89C1 totals\uFF08\u5148\u52A0\u603B\u518D\u9664\uFF0C\u52FF\u5E73\u5747\u5404\u7EC4 roas\uFF09\u3002\u52FF\u6DF7\u52A0\u4E0D\u540C DataLevel\u3002\n"
134830
134887
  );
134831
134888
  } else {
134832
134889
  console.log();
@@ -134979,7 +135036,7 @@ function registerTikTokAnalysisCommands(program2) {
134979
135036
  "TikTokAPI \u5B98\u65B9\u62A5\u8868\uFF08GET \u2026/query/report/Tiktok/search\uFF1B\u53EF\u542B\u4ECA\u5929/\u5C0F\u65F6\uFF1B\u4E0E `run` \u7684 TSO \u770B\u677F\u5468\u671F\u62A5\u8868\u4E0D\u540C\u3002ServiceType/DataLevel/Dimensions/Metrics/\u65E5\u671F/PageSize \u5FC5\u586B\uFF09"
134980
135037
  ).requiredOption(...ACCOUNT_OPTION2).requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u6309\u5E7F\u544A\u4E3B timezone \u81EA\u7136\u65E5\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").requiredOption("--data-level <level>", "AUCTION_CAMPAIGN / AUCTION_ADGROUP / AUCTION_AD").requiredOption("--dimensions <list>", "\u9017\u53F7\u5206\u9694\uFF0C\u5982 campaign_id \u6216 adgroup_id,stat_time_hour").requiredOption(
134981
135038
  "--metrics <list>",
134982
- "\u9017\u53F7\u5206\u9694\uFF0C\u82B1\u8D39\u987B\u7528 spend\uFF08\u4E0D\u8981 stat_cost\uFF09\uFF1B\u53EF\u542B conversion,cost_per_conversion,ctr,complete_payment"
135039
+ "\u9017\u53F7\u5206\u9694\uFF0C\u82B1\u8D39\u987B\u7528 spend\uFF08\u4E0D\u8981 stat_cost\uFF09\u3002complete_payment \u662F\u7F51\u9875\u5B8C\u4ED8\u6B21\u6570\u3002\u542B\u5B8C\u4ED8/ROAS \u65F6\u81EA\u52A8\u9644\u5E26 total_purchase_value,complete_payment_roas,total_active_pay_roas"
134983
135040
  ).option("--service-type <type>", "AUCTION\uFF08\u9ED8\u8BA4\uFF09\u6216 RESERVATION", "AUCTION").option("--page-size <n>", "\u6BCF\u9875\u6761\u6570\uFF0C\u9ED8\u8BA4 200\uFF0C\u4E0A\u9650 1000", (v) => parseInt(v, 10)).option("-t, --token <token>", "Auth Token").option("--json-out <path>", JSON_OUT_OPTION_DESC2).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
134984
135041
  async (opts) => {
134985
135042
  await runTikTokOfficialReport(opts);
@@ -40,7 +40,7 @@ siluzan-tso -h
40
40
 
41
41
  - `entityId`(UUID)≠ `mediaCustomerId`(`list-accounts` 的 `ma.mediaCustomerId`:Google/TikTok/Bing 多为数字;**Yandex=`porg-…`**;Meta 常带 `act_`)。`stats`/`balance`/`accounts-digest`/`ad *` 的 `-a` **只传 mediaCustomerId**;空结果或 verbose 打出 403 时**先核验 ID**,禁止把 UUID/`entityId` 传给 `-a`,也**禁止**据此直接 `reauth`。
42
42
  - **Google CID 特殊性**:UI 常写 `123-456-7890`(带连字符),平台 `mediaCustomerId` 为纯数字 `1234567890`。CLI 会自动去连字符;**勿**把带横杠 403 误判为 OAuth 失效。优先用 `list-accounts` 的纯数字 ID。
43
- - **禁止臆测授权过期**:见 403/`HTTP 403`/拉数失败/「可能 OAuth 失效」时,**禁止**口头推断授权过期或直接 `reauth`。须先核验 ID(及 Google 连字符),再跑 `account check-access -a <mediaCustomerId>`,以返回的 `accessible` / `reauth_required` / `no_permission` 为准;仅当 `list-accounts` 输出含激活字段且可判定未激活时才跳过用其判断授权过期;细则见 `accounts-permissions.md`。
43
+ - **禁止臆测授权过期**:见 403/`HTTP 403`/拉数失败/「可能 OAuth 失效」时,**禁止**口头推断授权过期或直接 `reauth`。须先核验 ID(及 Google 连字符)。**`account check-access` 仅支持 Google**(无 `-m`,禁止对 TikTok/Meta/Bing/Yandex/Kwai 调用):Google 跑 `account check-access -a <mediaCustomerId>`,以 `accessible` / `reauth_required` / `no_permission` 为准;非 Google 看 `list-accounts` 的 `invalidOAuthToken`。仅当 `list-accounts` 输出含激活字段且可判定未激活时才跳过用其判断授权过期;细则见 `accounts-permissions.md`。
44
44
  - **Google 广告命令名**:列表是 `ad campaigns`(不是 `ad-campaigns` / `campaign list`);`balance`/`stats` 须带 `-m Google`;单国地域用 `ad geo search … --json-out`(或 `geo resolve`)。
45
45
  - **Google 广告类型边界**:CLI/Skill **仅支持** Google **搜索广告(Search)** 与 **Performance Max(PMax)** 的方案、创建与精细管理;**不支持展示广告(Display)**(含自适应展示广告 RDA、独立 Display 系列)。用户要做展示广告时须明确告知不支持,可改推 Search / PMax;**禁止**用 `campaign-create` / `ad-create` 伪装创建 Display。
46
46
  - **创建完成后交付(硬性)**:`ad batch diff` 的 stdout 含 `BEGIN_USER_DELIVERY_MARKDOWN`…`END_USER_DELIVERY_MARKDOWN`——**立刻**把中间全文原样发给用户(可先于补建);禁止只摘要、禁止只说「创建成功/详情已交付/未发现缺失」、禁止等全部补建结束再交付。
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.46",
4
- "publishedAt": 1787190466105
3
+ "version": "1.1.47-beta.2",
4
+ "publishedAt": 1787276448425
5
5
  }
@@ -160,7 +160,7 @@ siluzan-tso accounts-digest -m Google --start 2026-07-20 --end 2026-07-20 \
160
160
  >
161
161
  > - **Google**:走 `account-spend-overview`;`--start` / `--end` 日历日按 **UTC+8** 转为 `YYYY-MM-DDTHH:mm:ss+08:00` 再请求(起 00:00:00、止 23:59:59,含今天时 end 截到当前时刻)。与 `google-analysis`(只传年月日)口径不同。
162
162
  > - 窗口完全在历史 → `database` 模式;窗口含今天 → `googleCombined` 模式(仅实时消耗,无余额/状态/币种/账户名)。
163
- > - **TikTok / Yandex / BingV2 / Kwai**:走旧版 `accountsoverview`,每日凌晨同步昨天数据,**不能查今天**。判断这几家的「今天/当天/今日消耗」仍需走 `google-analysis(-batch) --sections overview`(仅 Google)。
163
+ > - **TikTok / Yandex / BingV2 / Kwai**:走旧版 `accountsoverview`,每日凌晨同步昨天数据,**不能查今天**。Bing 看昨天/今天消耗用 `bing-analysis`(数据可能不完整);TikTok 用 `tiktok-analysis official-report`。Google「今天」仍走 `google-analysis(-batch) --sections overview`。
164
164
  > - 完整时效性表见 `references/analytics/account-analytics.md` 顶部。
165
165
 
166
166
  ```bash
@@ -167,7 +167,7 @@ siluzan-tso account-history --start 2026-03-01 --end 2026-03-31 --json-out ./sna
167
167
  | 状态 | 含义 | 下一步操作 |
168
168
  | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
169
169
  | `Pending` | 审核中 | 等待,可反复运行此命令轮询;审核周期因媒体而异 |
170
- | `Approved` | 审核通过 | 运行 `list-accounts -m <媒体>` 确认账户已出现;引导用户充值激活(`config show` 取 `webUrl`,按 `finance.md` 打开对应媒体充值页;例如 Google 为 `https://www.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google`;Kwai、Yandex 当前没有对应充值界面) |
170
+ | `Approved` | 审核通过 | 运行 `list-accounts -m <媒体>` 确认账户已出现;引导用户充值激活(`config show` 取 `webUrl`,按 `finance.md` 打开对应媒体充值页;例如 Google 为 `https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google`;Kwai、Yandex 当前没有对应充值界面) |
171
171
  | `Rejected` | 被拒 | 查看 `--json-out` 落盘中的 `reason` 字段了解拒绝原因;修改资料后重新提交;若原因不明,引导用户联系丝路赞客服 |
172
172
 
173
173
  ---
@@ -33,6 +33,8 @@ siluzan-tso account me --check-phone 15130150466 --json-out ./snap-me
33
33
 
34
34
  ### check-access — Google 账户访问权限校验
35
35
 
36
+ **仅支持 Google。** 无 `-m`;`-a` 只接受 Google 纯数字 `mediaCustomerId`。TikTok / MetaAd / BingV2 / Yandex / Kwai **禁止**调用本命令,授权是否失效看 `list-accounts` 的 `invalidOAuthToken`。
37
+
36
38
  校验当前丝路赞凭据是否对指定 **Google** 广告账户有访问权限。应在拉数/诊断前调用,避免误用他户 ID。
37
39
 
38
40
  > **硬约束**:**禁止**凭 403、空结果或经验臆测「授权/OAuth 过期」。要对用户下「授权不可用 / 需重授权」结论前,**必须**先跑本命令,以 CLI 返回的 `status` 为准;**禁止**跳过本命令直接 `reauth` 或口头推断。
@@ -488,7 +490,7 @@ siluzan-tso account email-deauth -c <mediaCustomerId> --invitation-id <id> --res
488
490
 
489
491
  | 功能 | 媒体 | 网页路径 |
490
492
  | --------------------------------------- | ------ | ------------------------------------------------- |
491
- | **账户激活**(邀请他人激活 / 充值激活) | Google | `https://www.siluzan.com/v3/foreign_trade/tso/manageAccounts` |
493
+ | **账户激活**(邀请他人激活 / 充值激活) | Google | `https://www-ci.siluzan.com/v3/foreign_trade/tso/manageAccounts` |
492
494
 
493
495
  **Agent 建议话术**:
494
496
 
@@ -497,5 +499,5 @@ siluzan-tso account email-deauth -c <mediaCustomerId> --invitation-id <id> --res
497
499
  siluzan-tso config show # 查看 webUrl 字段
498
500
 
499
501
  # 账户激活(Google)→ 引导至账户管理页
500
- # https://www.siluzan.com/v3/foreign_trade/tso/manageAccounts
502
+ # https://www-ci.siluzan.com/v3/foreign_trade/tso/manageAccounts
501
503
  ```
@@ -20,7 +20,7 @@
20
20
  - `entityId`(UUID,分享/delink/账单/`reauth`)≠ `mediaCustomerId`(`balance`/`stats`/`accounts-digest`/`ad` 的 `-a`)。**Yandex 的 mediaCustomerId 形如 `porg-…`,不是 UUID。**
21
21
  - MetaAd OAuth 户的 `mediaCustomerId` **须带 `act_` 前缀**。
22
22
  - **Google CID**:广告后台展示常为 `XXX-XXX-XXXX`(连字符),`list-accounts` / 网关要的是**纯数字**(无横杠)。`stats`/`balance`/`ad *`/`google-analysis` 的 `-a` 可带连字符(CLI 会去掉);若仍见 `HTTP 403:123-456-7890` 这类**回显带横杠 ID**,优先改成纯数字重试,**不要**直接当 OAuth 失效去 `reauth`。
23
- - **禁止臆测授权过期**:403 / 拉数失败时**禁止**口头说「可能授权过期」。须 ID 核验后,对 Google 执行 `account check-access -a <mediaCustomerId>`,以返回 `status` 为准(见 [`accounts-permissions.md`](accounts-permissions.md));仅当 `list-accounts` 输出含激活字段且可判定未激活时,勿用其判断授权过期;非 Google 看 `invalidOAuthToken`。
23
+ - **禁止臆测授权过期**:403 / 拉数失败时**禁止**口头说「可能授权过期」。须 ID 核验后,**仅 Google** 执行 `account check-access -a <mediaCustomerId>`(无 `-m`,其他媒体禁止调用),以返回 `status` 为准(见 [`accounts-permissions.md`](accounts-permissions.md));仅当 `list-accounts` 输出含激活字段且可判定未激活时,勿用其判断授权过期;非 Google 看 `invalidOAuthToken`。
24
24
  - `stats`/`balance` 空结果 + verbose `HTTP 403`:先确认 `-a` 是否为 `ma.mediaCustomerId`(且 Google 已去连字符);**禁止**把 `entityId` / tokenId 当 `-a`;未跑 `account check-access`(或未见 `invalidOAuthToken=true`)**禁止** `reauth`。
25
25
  - 多账户余额预警用 `balance-scan`(P2),多账户消耗汇总用 `accounts-digest`(P3);**禁止**外层 for-loop 逐户 `balance`/`stats`。
26
26
  - `stats` 默认 `spend` = **区间合计**,不是日消耗;日均看 `balance-scan.dailySpend` 或合计÷天数。
@@ -126,7 +126,7 @@ siluzan-tso config show
126
126
 
127
127
  ### 充值页链接(按媒体 × 类型)
128
128
 
129
- 链接模式:`https://www.siluzan.com/v3/foreign_trade/tso/recharge/<page>?mediaType=<mediaType>`;丝路赞钱包:`https://www.siluzan.com/v3/foreign_trade/tso/recharge/siluzanWallet`(无媒体参数)。
129
+ 链接模式:`https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/<page>?mediaType=<mediaType>`;丝路赞钱包:`https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/siluzanWallet`(无媒体参数)。
130
130
 
131
131
  | 充值类型 | `<page>` | 支持媒体(`mediaType` 参数) |
132
132
  | ------------------------- | --------------------- | ------------------------------------------- |
@@ -1,6 +1,6 @@
1
1
  # 各媒体开户
2
2
 
3
- > 网页链接:`https://www.siluzan.com/v3/foreign_trade/tso/accountOpeningHistory?tso=%2Fv3umijs%2Ftso%2FaccountOpeningHistory`
3
+ > 网页链接:`https://www-ci.siluzan.com/v3/foreign_trade/tso/accountOpeningHistory?tso=%2Fv3umijs%2Ftso%2FaccountOpeningHistory`
4
4
  > 多命令串联见 `references/core/workflows.md` § 流程一。
5
5
 
6
6
  ## Contents
@@ -65,7 +65,7 @@ siluzan-tso open-account google-wizard
65
65
 
66
66
  ```bash
67
67
  siluzan-tso account-history -m Google
68
- # 审核通过后:config show → https://www.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google
68
+ # 审核通过后:config show → https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google
69
69
  ```
70
70
 
71
71
  ---
@@ -351,11 +351,13 @@ siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./sn
351
351
  --data-level AUCTION_CAMPAIGN --dimensions campaign_id --metrics spend
352
352
  ```
353
353
 
354
+ **ROAS 口径**(对齐 [网页事件](https://ads.tiktok.com/help/article/page-events?lang=zh) / [应用内事件](https://ads.tiktok.com/help/article/in-app-events?lang=zh)):`--metrics` 含完付或 ROAS 时 CLI 自动附带 `total_purchase_value,complete_payment_roas,total_active_pay_roas`。落盘行有 `revenue` / `revenueSource` / `roas` / `roasSource`,多行汇总在 `totals`(先加总 spend/revenue 再除)。`complete_payment` 是网页支付完成**次数**;金额用 `total_purchase_value`(总付费价值)。ROAS 优先官方列 `complete_payment_roas` / `total_active_pay_roas`。**禁止** `complete_payment/spend`,**禁止**把完付=0 判成「像素没回传」,**禁止**对各组 `roas` 取平均。不确定时先 `adgroup-entities` 看 `optimization_event`。
355
+
354
356
  ---
355
357
 
356
358
  ## Bing(BingV2)账户分析
357
359
 
358
- > **日期限制**:Bing 报表不能包含今天或昨天。省略时 CLI 默认截至前天的近 7 天。用户要**已结束的完整自然月**(如「6月份」)→ `--start` 当月 1 日、`--end` 当月最后一天(6 月 = `06-30`,**禁止**减 1 天),见 `report-templates/bing-period-report.md` §日期规则。
360
+ > **日期**:可含昨天(及今天,数据可能不完整)。网关未显式传 `returnOnlyCompleteData` 时,区间碰到今天/昨天会自动改为近实时。省略时 CLI 默认截至昨天的近 7 天。用户要**已结束的完整自然月**(如「6月份」)→ `--start` 当月 1 日、`--end` 当月最后一天(6 月 = `06-30`,**禁止**减 1 天),见 `report-templates/bing-period-report.md` §日期规则。
359
361
  >
360
362
  > **自动化巡检步骤**(封禁 / 拒审 / 落地页 / 当日超预算 / 空耗预警,只读告警):`references/operations/hosted-automation-bing.md`。
361
363
 
@@ -365,7 +367,7 @@ siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./sn
365
367
  | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
366
368
  | `--sections` / `--exclude` | `overview` `device` `geographic` `age-audience` `gender-audience` `audience-merged` `campaigns` `ad-groups` `ads` `keywords` `search-terms`(11 个);**别名**:`devices`→`device`,`geo`→`geographic`,`search-queries`→`search-terms` |
367
369
  | `--limit` | 仅 `keywords` / `search-terms`:条数上限,默认 100 |
368
- | `--start` / `--end` | 同传或同省略;不可含今天/昨天;省略=截至前天的近 7 天 |
370
+ | `--start` / `--end` | 同传或同省略;可含昨天/今天(可能不完整);省略=截至昨天的近 7 天 |
369
371
  | `--concurrency` | 默认 5,上限 16 |
370
372
 
371
373
  ```bash
@@ -431,12 +433,11 @@ siluzan-tso bing-analysis monthly-spend -a <mediaCustomerId>
431
433
  - `ad-entities` 单系列查必须带 `--campaign-id`;查全账户拒审/落地页用 `bulk-ads`,不要按系列遍历 `ad-entities`。
432
434
  - `bulk-ads` 底层为官方异步下载任务,网关等文件就绪才返回,大账户可能要几分钟,不适合放在页面同步加载场景,适合定时巡检。
433
435
 
434
- ### Bing 近实时报表(`bing-analysis --aggregation`,日期可含今天)
436
+ ### Bing 近实时报表(`bing-analysis --aggregation`,按日/小时分桶)
435
437
 
436
- > `bing-analysis` 默认面向周期报告,日期**禁止**含今天/昨天;一旦显式传 `--aggregation`
437
- > `--return-only-complete-data`,即视为「单日花费」「近两小时 CPA」等近实时监控意图,
438
- > **自动放开**日期含今天/昨天的限制。仅 `campaigns`/`ad-groups`/`ads` 三个 section 支持这两个参数
439
- > (分别对应网关 `CampaignReport`/`AdGroupReport`/`AdReport`)。
438
+ > `--aggregation` / `--return-only-complete-data` `campaigns`/`ad-groups`/`ads` 三个 section 支持
439
+ > (分别对应网关 `CampaignReport`/`AdGroupReport`/`AdReport`)。省略这两个参数时,含昨天/今天的区间
440
+ > 同样可以拉(网关自动近实时);`--aggregation` 用来拿 Daily/Hourly 分桶。省略日期且传了 `--aggregation` 时默认今天当天。
440
441
 
441
442
  ```bash
442
443
  # 单日花费与日预算(同一 campaignId 算 spend / dailyBudget;isSharedBudget=true 时 dailyBudget 是共享总额)
@@ -1,6 +1,6 @@
1
1
  # rag:知识库检索(TSO 广告投放辅助)
2
2
 
3
- 知识库管理页面在 https://www.siluzan.com/knowledge-base/
3
+ 知识库管理页面在 https://www-ci.siluzan.com/knowledge-base/
4
4
 
5
5
  为 **广告投放、账户分析、拓词、诊断报告** 等 TSO 业务提供**企业已入库**的产品、行业、客户背景事实依据。
6
6
 
@@ -190,9 +190,9 @@ siluzan-tso report push receive-emails -m Google [--json-out ./snap]
190
190
 
191
191
  | 媒体 | 报告类型 | URL 模板 |
192
192
  | ------ | ---------------- | ----------------------------------------------------------- |
193
- | Google | 日报(Daily) | `https://www.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
194
- | Google | 小时报(Hourly) | `https://www.siluzan.com/media-report/hour/{entityId}?culture=zh-CN` |
195
- | TikTok | 日报 | `https://www.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
193
+ | Google | 日报(Daily) | `https://www-ci.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
194
+ | Google | 小时报(Hourly) | `https://www-ci.siluzan.com/media-report/hour/{entityId}?culture=zh-CN` |
195
+ | TikTok | 日报 | `https://www-ci.siluzan.com/media-report/publish/{entityId}?culture=zh-CN` |
196
196
 
197
197
  `entityId` 来自 `siluzan-tso report list --json-out ./snap` 中每条记录的 `entityId` 字段。
198
198
 
@@ -205,8 +205,8 @@ siluzan-tso report list -m Google --json-out ./snap
205
205
 
206
206
  # 第二步:查看 webUrl
207
207
  siluzan-tso config show
208
- # webUrl: https://www.siluzan.com
208
+ # webUrl: https://www-ci.siluzan.com
209
209
 
210
210
  # 第三步:拼接链接(Google 日报)
211
- # https://www.siluzan.com/media-report/publish/rpt_abc123?culture=zh-CN
211
+ # https://www-ci.siluzan.com/media-report/publish/rpt_abc123?culture=zh-CN
212
212
  ```
@@ -125,9 +125,9 @@
125
125
  ## 四、硬规范
126
126
 
127
127
  - **账户状态 ≠ 系列状态**:`stats` / `balance` / `list-accounts` 的 `status` 只表示账户是否可用;系列状态必须来自 `ad campaigns`。
128
- - **数据时效性**:涉及「今天/当天/今日消耗」「实时消耗排行」前,必读 `references/analytics/account-analytics.md` 顶部「数据时效性」表。TikTok / Yandex / BingV2 / Kwai `accountsoverview` 同步昨天数据,**不能查今天**。
128
+ - **数据时效性**:涉及「今天/当天/今日消耗」「实时消耗排行」前,必读 `references/analytics/account-analytics.md` 顶部「数据时效性」表。TikTok / Yandex / BingV2 / Kwai `stats`/`accountsoverview` 同步昨天数据,**不能查今天**。Bing 昨天/今天消耗走 `bing-analysis`;TikTok 走 `tiktok-analysis official-report`。
129
129
  - **先查账户再操作**(拉数 / 改账户 / 报告 / **创建广告**):`list-accounts -m [mediaType] -k [mediaCustomerId]`;用户给出的 `mediaCustomerId` 必须 `-k` 核验,无结果则告知用户并停止,**禁止**翻页 grep 自行换 ID(会导致报告错户);拉数、脚本、报告文件名全链路用同一 ID(以 stdout `accountId` 为准)。
130
- - **禁止臆测授权过期(硬约束)**:任何 403、空结果、「可能授权/OAuth 过期」话术,**禁止**凭感觉下结论或直接 `reauth`。Google:**必须**执行 `account check-access -a <mediaCustomerId>`,仅当结果为 `reauth_required`(或列表 `invalidOAuthToken=true` 与之交叉确认)才谈重授权;`no_permission` 也可能是账户不在当前丝路赞账号下。若 `list-accounts` **输出含** `scopeActivatedSources` 且可判定未激活,则勿用 check-access 判断授权过期;**无该字段时禁止谈套餐激活**。非 Google 媒体以 `list-accounts` 的 `invalidOAuthToken` 为准,同样禁止臆测。详见 `accounts-permissions.md`。
130
+ - **禁止臆测授权过期(硬约束)**:任何 403、空结果、「可能授权/OAuth 过期」话术,**禁止**凭感觉下结论或直接 `reauth`。`account check-access` **仅支持 Google**(无 `-m`,禁止对 TikTok/Meta/Bing/Yandex/Kwai 调用)。Google:**必须**执行 `account check-access -a <mediaCustomerId>`,仅当结果为 `reauth_required`(或列表 `invalidOAuthToken=true` 与之交叉确认)才谈重授权;`no_permission` 也可能是账户不在当前丝路赞账号下。若 `list-accounts` **输出含** `scopeActivatedSources` 且可判定未激活,则勿用 check-access 判断授权过期;**无该字段时禁止谈套餐激活**。非 Google 媒体以 `list-accounts` 的 `invalidOAuthToken` 为准,同样禁止臆测。详见 `accounts-permissions.md`。
131
131
  - **一律走 CLI,禁止自拼网关请求**:查余额 / 拉数 / 写操作只用 `siluzan-tso …`。**禁止**用 curl、自写脚本或改请求头直连 TSO/Google 网关「另辟蹊径」取数;`balance` 等无数据时按 CLI/`list-accounts` **实际出现的字段**向用户说明(如授权失效;仅当输出含激活字段时才可谈套餐),**禁止**尝试任何绕过平台门禁的取数方式。
132
132
  - **W3 仅出方案例外(覆盖上条)**:用户只要「投放方案 / 规划 / 表格 / 先别创建·开户·投钱」,或未给账户且未要求创建/发布时——**禁止**把「请先提供 Google 广告账户」当作第一步;按 `google-ads-campaign-plan.md` §「仅出方案 vs 创建」落盘 JSON 后 **写代码**投影完整审查稿(默认 MD;用户指定则 Excel 等;`account`=`[PENDING_ACCOUNT]`),跳过 `list-accounts` / `geo resolve` / validate / create。**禁止**只交概览表。用户确认要创建后再要账户并续跑创建流水线。
133
133
  - **W3 审查稿(搜索与 PMax)**:JSON 落盘后、创建前,必须 **写代码**读取 JSON,按 `google-ads-launch-plan-template.md`(搜索)或 `google-ads-pmax-launch-plan-template.md`(PMax)生成完整审查文件交给用户;须含全部关键词/RSA 或全部 PMax 文案与附加资产。**禁止**用「方案总结」条数勾选代替。用户要求其他格式时改脚本输出,数据仍只从 JSON 来。
@@ -197,7 +197,7 @@
197
197
  **完整自然月口径**(用户说「X 月 / X月份 / 月报」且该月已结束,或上表「月报」默认):
198
198
 
199
199
  - `--start` = 当月 1 日,`--end` = 当月最后一天(6 月 → `06-01` ~ `06-30`,**禁止** `06-29`)。
200
- - **Bing** `bing-analysis`:「不能含今天/昨天」仅当区间碰到**执行日**的今天/昨天时才需把结束日压到前天;**已结束的历史自然月必须用该月最后一天**,细则见 `report-templates/bing-period-report.md` §日期规则。交付前核对 `overview` 的 `activeDays` = 该月日历天数。
200
+ - **Bing** `bing-analysis`:可含昨天/今天(今天可能不完整);**已结束的历史自然月必须用该月最后一天**,细则见 `report-templates/bing-period-report.md` §日期规则。交付前核对 `overview` 的 `activeDays` = 该月日历天数。
201
201
 
202
202
  ---
203
203
 
@@ -278,7 +278,7 @@
278
278
 
279
279
  - **400**:参数错误,查看对应 reference 或 `-h`
280
280
  - **401 / OAuth 失效**:仅当 `list-accounts` 的 `invalidOAuthToken=true`(或表格「授权状态」为失效)且用户确认后——Siluzan Agent **优先**用平台重新授权工具(如 `present_reauth`);备选 `account reauth -m <媒体> --id <entityId> --i-confirm --commit "…"`(内置 delink→OAuth,见 W9 / `accounts-permissions.md`)。走 CLI 时**必须把 stdout 中的完整授权 URL 原样贴给用户**,禁止只说「链接已生成」,也**禁止**自行改写/补全该 URL。解绑后若列表已无该户,恢复用平台授权工具或 `account auth -m <媒体>`。**丝路赞登录凭据失效** → `send-login-code` + `login --phone --code`,见 `references/core/setup.md`
281
- - **403(拉数空结果 / ad 网关)**:① **优先核验 `-a` 是否为 `ma.mediaCustomerId`**(勿传 `entityId`/UUID;Google 注意连字符 CID;Yandex 形如 `porg-…`);② 仅当 `list-accounts` **输出含** `scopeActivatedSources` 且可判定未激活时,才可说明需先激活;**无该字段则禁止谈套餐**;**禁止**非 CLI 绕过取数;③ **禁止臆测授权过期**:**必须**跑 `account check-access -a <mediaCustomerId>`,以 `status` 为准后再决定是否 `reauth`;**禁止**仅凭 403 文案对用户说「授权过期」。
281
+ - **403(拉数空结果 / ad 网关)**:① **优先核验 `-a` 是否为 `ma.mediaCustomerId`**(勿传 `entityId`/UUID;Google 注意连字符 CID;Yandex 形如 `porg-…`);② 仅当 `list-accounts` **输出含** `scopeActivatedSources` 且可判定未激活时,才可说明需先激活;**无该字段则禁止谈套餐**;**禁止**非 CLI 绕过取数;③ **禁止臆测授权过期**:Google **必须**跑 `account check-access -a <mediaCustomerId>`(**仅 Google**,无 `-m`),以 `status` 为准后再决定是否 `reauth`;非 Google 看 `list-accounts` 的 `invalidOAuthToken`,**禁止**对 TikTok/Meta/Bing/Yandex/Kwai 跑 `check-access`;**禁止**仅凭 403 文案对用户说「授权过期」。
282
282
  - **500**:服务可能正在部署/升级,建议反馈 Siluzan 相关人员
283
283
 
284
284
  ---
@@ -10,7 +10,7 @@
10
10
  ## 安装 CLI
11
11
 
12
12
  ```bash
13
- npm install -g siluzan-tso-cli
13
+ npm install -g siluzan-tso-cli@beta
14
14
  ```
15
15
 
16
16
  ---
@@ -64,7 +64,7 @@ siluzan-tso config set --api-key <Key> # 或 config 直接写入
64
64
  siluzan-tso config set --token <Token> # 备用:设置 JWT Token
65
65
  ```
66
66
 
67
- API Key 获取入口:`https://www.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
67
+ API Key 获取入口:`https://www-ci.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
68
68
 
69
69
  ```bash
70
70
  # 第 1 步:让用户报出手机号后,立刻发码(命令立即返回,不会等待输入)
@@ -127,9 +127,9 @@ siluzan-tso config show
127
127
 
128
128
  ```
129
129
  构建环境 : production
130
- apiBaseUrl : https://tso-api.siluzan.com
131
- googleApiUrl : https://googleapi.mysiluzan.com
132
- webUrl : https://www.siluzan.com
130
+ apiBaseUrl : https://tso-api-ci.siluzan.com
131
+ googleApiUrl : https://googleapi-ci.mysiluzan.com
132
+ webUrl : https://www-ci.siluzan.com
133
133
  apiKey : abcd****1234
134
134
  ```
135
135
 
@@ -6,7 +6,7 @@
6
6
  siluzan-tso config show # 取 webUrl
7
7
  ```
8
8
 
9
- 首页地址:`https://www.siluzan.com/v3/foreign_trade/tso/home`
9
+ 首页地址:`https://www-ci.siluzan.com/v3/foreign_trade/tso/home`
10
10
 
11
11
  ---
12
12
 
@@ -25,7 +25,7 @@ siluzan-tso config show # 取 webUrl
25
25
 
26
26
  ## 推荐话术
27
27
 
28
- 1. **「和首页一样的总览」** → 打开 `https://www.siluzan.com/v3/foreign_trade/tso/home`。
28
+ 1. **「和首页一样的总览」** → 打开 `https://www-ci.siluzan.com/v3/foreign_trade/tso/home`。
29
29
  2. **「某个 Google 账户昨天花了多少」** → `list-accounts -m Google` + `stats -m Google -a <id>`。
30
30
  3. **「有待充值账户」** → 说明聚合数据在首页;CLI 可 `list-accounts` + `balance` 逐户排查,或引导充值页。
31
31
 
@@ -9,7 +9,7 @@
9
9
  **硬约束**:
10
10
 
11
11
  - `guard` **只支持 Google**。禁止 `guard … -m BingV2`。
12
- - 周期报表(不带 `--aggregation`)日期**不能含今天/昨天**;省略则默认截至前天的近 7 天。
12
+ - 周期报表日期**可以含昨天**;今天数据可能不完整。省略则默认截至昨天的近 7 天。含昨天/今天时不要显式传 `--return-only-complete-data true`。
13
13
  - 当日花费、近两小时 CPA 必须显式传 `--aggregation Daily|Hourly`,且 `--sections` 只能是 `campaigns` / `ad-groups` / `ads`。
14
14
  - Hourly 时间桶是**太平洋时区**,和北京日期可能差一天;阈值按 `timePeriod` 对齐,不要按北京日历硬切。
15
15
  - 花费/转化用 `bing-analysis run --sections …`;日预算/启停状态用 `campaign-entities`。两套数据不要混成一行。
@@ -107,7 +107,7 @@ siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./sn
107
107
  --data-level AUCTION_CAMPAIGN --dimensions campaign_id,stat_time_hour --metrics spend
108
108
  ```
109
109
 
110
- 广告效果(CPA 用 `cost_per_conversion` 或 `spend/conversion`;ROAS 落盘里已自算 `complete_payment/spend`):
110
+ 广告效果(CPA 用 `cost_per_conversion` 或 `spend/conversion`;**ROAS 用落盘 `roas` / `totals.roas`**):
111
111
 
112
112
  ```bash
113
113
  siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./snap-tt \
@@ -116,7 +116,7 @@ siluzan-tso tiktok-analysis official-report -a <mediaCustomerId> --json-out ./sn
116
116
  --metrics spend,conversion,cost_per_conversion,complete_payment,ctr
117
117
  ```
118
118
 
119
- 非完付目标的 `complete_payment` 常为 0,ROAS 不能当通用门槛。出价在组,广告层只能关停(本批 CLI **不**关停)。
119
+ `--metrics` 含完付/ROAS 时 CLI 自动附带 `total_purchase_value,complete_payment_roas,total_active_pay_roas`。口径对齐 Ads Manager:`complete_payment` 是网页支付完成**次数**(不是金额);ROAS 优先官方列 `complete_payment_roas`(网页)/ `total_active_pay_roas`(App 付费),否则 `total_purchase_value/spend`。多行用 `totals`(先加总再除),**禁止**对各组 `roas` 取平均。**`complete_payment=0` 不是像素坏了**——App / `ACTIVE_PAY` 看 `total_purchase_value`。不确定优化目标时先 `adgroup-entities` 看 `optimization_event`。出价在组,广告层只能关停(本批 CLI **不**关停)。
120
120
 
121
121
  根因分解(含小时时起止仍须同一天,必须翻完页;CLI 已自动翻):
122
122
 
@@ -76,9 +76,9 @@ Agent 只需撰写 `narrative`;**禁止手写 HTML**,也**禁止**自填 `kp
76
76
 
77
77
  ## 日期规则(必读)
78
78
 
79
- - Bing 接口**无法**拉取过新的数据;时间范围内**任意一天**若为**今天**或**昨天**,请求会失败。
80
- - **CLI**:`--start` / `--end` 须**同传或同省略**;省略时默认**截至前天**的近 7 天(与 `bing-analysis` 实现一致)。
81
- - **自选区间且靠近当前日期**时,结束日须 **前天**(仅当区间会碰到今天/昨天才需要压到前天;**不是**把历史自然月最后一天减 1 天)。
79
+ - 可以含**昨天**;**今天**也可以拉,但可能不完整(网关未显式传 `returnOnlyCompleteData` 时自动近实时)。含昨天/今天时**不要**显式传 `--return-only-complete-data true`。
80
+ - **CLI**:`--start` / `--end` 须**同传或同省略**;省略时默认**截至昨天**的近 7 天(与 `bing-analysis` 实现一致)。
81
+ - 小时/日分桶(当日花费、近两小时 CPA)仍须对 `campaigns`/`ad-groups`/`ads` `--aggregation Daily|Hourly`。
82
82
 
83
83
  ### 用户说「X 月 / X月份 / 月报」(已结束的完整自然月)
84
84
 
@@ -96,7 +96,7 @@ Agent 只需撰写 `narrative`;**禁止手写 HTML**,也**禁止**自填 `kp
96
96
  siluzan-tso bing-analysis -a <id> --start 2026-06-01 --end 2026-06-30 --json-out ./snap-bing
97
97
  ```
98
98
 
99
- **禁止**因「Bing 不能含今天/昨天」而对**已结束的历史自然月**把 `--end` 减 1 天——该限制只针对区间内的日历日是否等于**执行当天的今天或昨天**;6 月 30 日在 7 月 8 日拉数完全合法。拉数后核对 `overview` 落盘里的 `activeDays` 应等于该月日历天数(6 月 = 30)。
99
+ **禁止**因旧规则「Bing 不能含今天/昨天」而对**已结束的历史自然月**把 `--end` 减 1 天。6 月 30 日在 7 月 8 日拉数完全合法。拉数后核对 `overview` 落盘里的 `activeDays` 应等于该月日历天数(6 月 = 30)。
100
100
 
101
101
  ---
102
102
 
@@ -76,9 +76,9 @@ Agent 只需撰写 `narrative`;**禁止手写 HTML**,也**禁止**自填 `kp
76
76
 
77
77
  ## 日期规则(必读)
78
78
 
79
- - Bing 接口**无法**拉取过新的数据;时间范围内**任意一天**若为**今天**或**昨天**,请求会失败。
80
- - **CLI**:`--start` / `--end` 须**同传或同省略**;省略时默认**截至前天**的近 7 天(与 `bing-analysis` 实现一致)。
81
- - **自选区间且靠近当前日期**时,结束日须 **前天**(仅当区间会碰到今天/昨天才需要压到前天;**不是**把历史自然月最后一天减 1 天)。
79
+ - 可以含**昨天**;**今天**也可以拉,但可能不完整(网关未显式传 `returnOnlyCompleteData` 时自动近实时)。含昨天/今天时**不要**显式传 `--return-only-complete-data true`。
80
+ - **CLI**:`--start` / `--end` 须**同传或同省略**;省略时默认**截至昨天**的近 7 天(与 `bing-analysis` 实现一致)。
81
+ - 小时/日分桶(当日花费、近两小时 CPA)仍须对 `campaigns`/`ad-groups`/`ads` `--aggregation Daily|Hourly`。
82
82
 
83
83
  ### 用户说「X 月 / X月份 / 月报」(已结束的完整自然月)
84
84
 
@@ -96,7 +96,7 @@ Agent 只需撰写 `narrative`;**禁止手写 HTML**,也**禁止**自填 `kp
96
96
  siluzan-tso bing-analysis -a <id> --start 2026-06-01 --end 2026-06-30 --json-out ./snap-bing
97
97
  ```
98
98
 
99
- **禁止**因「Bing 不能含今天/昨天」而对**已结束的历史自然月**把 `--end` 减 1 天——该限制只针对区间内的日历日是否等于**执行当天的今天或昨天**;6 月 30 日在 7 月 8 日拉数完全合法。拉数后核对 `overview` 落盘里的 `activeDays` 应等于该月日历天数(6 月 = 30)。
99
+ **禁止**因旧规则「Bing 不能含今天/昨天」而对**已结束的历史自然月**把 `--end` 减 1 天。6 月 30 日在 7 月 8 日拉数完全合法。拉数后核对 `overview` 落盘里的 `activeDays` 应等于该月日历天数(6 月 = 30)。
100
100
 
101
101
  ---
102
102
 
@@ -83,7 +83,7 @@
83
83
  <i class="fa-solid fa-circle-info text-amber-400" aria-hidden="true"></i>
84
84
  数据口径提示(示例)
85
85
  </p>
86
- <p class="mt-2 pl-7 text-amber-100/80">BingV2 区间不可含今天/昨天;以接口与 CLI 为准。</p>
86
+ <p class="mt-2 pl-7 text-amber-100/80">BingV2 昨天/今天可能不完整;以接口与 CLI 为准。</p>
87
87
  </aside>
88
88
 
89
89
  <section class="mb-14" data-section-id="section-executive-summary">
@@ -94,7 +94,7 @@
94
94
  <i class="fa-solid fa-triangle-exclamation mr-1 text-amber-500" aria-hidden="true"></i
95
95
  >口径提示
96
96
  </p>
97
- <p class="mt-1">BingV2 区间勿含今天/昨天。</p>
97
+ <p class="mt-1">BingV2 昨天/今天数据可能不完整。</p>
98
98
  </aside>
99
99
 
100
100
  <section class="mb-10" data-section-id="section-executive-summary">
@@ -60,7 +60,7 @@
60
60
 
61
61
  <aside class="no-print mb-8 border-2 border-black border-dashed bg-neutral-50 p-4 text-sm">
62
62
  <p class="font-bold">数据口径(示例)</p>
63
- <p class="mt-1 text-neutral-800">BingV2:区间不可含今天/昨天。</p>
63
+ <p class="mt-1 text-neutral-800">BingV2:昨天/今天数据可能不完整。</p>
64
64
  </aside>
65
65
 
66
66
  <section class="mb-10" data-section-id="section-executive-summary">
@@ -9,11 +9,11 @@ $ErrorActionPreference = 'Stop'
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  $PKG_NAME = 'siluzan-tso-cli'
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- $PKG_VERSION = '1.1.46'
12
+ $PKG_VERSION = '1.1.47-beta.2'
13
13
  $CLI_BIN = 'siluzan-tso'
14
14
  $SKILL_LABEL = 'Siluzan TSO'
15
- $INSTALL_CMD = 'npm install -g siluzan-tso-cli'
16
- $WEB_BASE = 'https://www.siluzan.com'
15
+ $INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
16
+ $WEB_BASE = 'https://www-ci.siluzan.com'
17
17
 
18
18
  # -- Constants ----------------------------------------------------------------
19
19
  $NODE_MAJOR_MIN = 18
@@ -9,11 +9,11 @@ set -euo pipefail
9
9
  # -- Package info (injected at build time) ------------------------------------
10
10
  readonly PKG_NAME="siluzan-tso-cli"
11
11
  # PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
12
- readonly PKG_VERSION="1.1.46"
12
+ readonly PKG_VERSION="1.1.47-beta.2"
13
13
  readonly CLI_BIN="siluzan-tso"
14
14
  readonly SKILL_LABEL="Siluzan TSO"
15
- readonly INSTALL_CMD="npm install -g siluzan-tso-cli"
16
- readonly WEB_BASE="https://www.siluzan.com"
15
+ readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
16
+ readonly WEB_BASE="https://www-ci.siluzan.com"
17
17
 
18
18
  # -- Constants ----------------------------------------------------------------
19
19
  readonly NODE_MAJOR_MIN=18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.46",
3
+ "version": "1.1.47-beta.2",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",