siluzan-tso-cli 1.1.15-beta.4 → 1.1.15-beta.6

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 (34) hide show
  1. package/README.md +1 -1
  2. package/assets/siluzan-ads/references/hosted-automation-user-catalog.md +4 -4
  3. package/dist/index.js +73 -112
  4. package/dist/skill/SKILL.md +122 -268
  5. package/dist/skill/_meta.json +2 -2
  6. package/dist/skill/references/account-analytics.md +161 -228
  7. package/dist/skill/references/google-ads-rules/google-ads-account-audit.md +64 -65
  8. package/dist/skill/references/google-ads-rules/google-ads-audience-strategy.md +14 -14
  9. package/dist/skill/references/google-ads-rules/google-ads-campaign-optimization.md +9 -9
  10. package/dist/skill/references/google-ads-rules/google-ads-conversion-architecture.md +12 -12
  11. package/dist/skill/references/google-ads-rules/google-ads-creative-optimization.md +1 -1
  12. package/dist/skill/references/google-ads-rules/google-ads-keyword-optimization.md +15 -15
  13. package/dist/skill/references/google-ads-rules/google-ads-pmax-guide.md +24 -24
  14. package/dist/skill/references/google-ads.md +2 -2
  15. package/dist/skill/references/hosted-automation-monitoring-json.md +5 -5
  16. package/dist/skill/references/hosted-automation-optimize-ab-winner.md +1 -1
  17. package/dist/skill/references/hosted-automation-optimize-index.md +1 -1
  18. package/dist/skill/references/hosted-automation-optimize-scale.md +4 -4
  19. package/dist/skill/references/hosted-automation-optimize-weak-downbid.md +2 -2
  20. package/dist/skill/references/hosted-automation-scenarios.md +2 -2
  21. package/dist/skill/references/hosted-automation-user-catalog.md +4 -4
  22. package/dist/skill/references/tips.md +21 -0
  23. package/dist/skill/report-templates/REPORT-WORKFLOW.md +1 -1
  24. package/dist/skill/report-templates/google-account-diagnosis-report.md +11 -11
  25. package/dist/skill/report-templates/google-period-report.md +28 -27
  26. package/dist/skill/report-templates/meta-period-report.md +1 -1
  27. package/eval/cases/google-analysis-keywords-route.scenario.json +2 -2
  28. package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +1 -1
  29. package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +1 -1
  30. package/eval/cases/uj-roi-keywords-high-cpa-low-cvr-triage.scenario.json +1 -1
  31. package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +2 -2
  32. package/eval/stub-fixtures/google-analysis.json +32 -5
  33. package/package.json +1 -1
  34. package/eval/stub-fixtures/google-analysis-search-terms.json +0 -10
package/README.md CHANGED
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
- > **注意**:当前为测试版(1.1.15-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.15-beta.6),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
@@ -21,10 +21,10 @@
21
21
  | 功能名称 | 监控层级 / 说明 | CLI 检查入口(`--json`) |
22
22
  | ----------------- | --------------- | ------------------------ |
23
23
  | 1. 账户被封禁 | 账户级;与 Google `CustomerStatus` 及 `list-accounts` 的字段映射**暂不写进本 skill**,由宿主约定。 | `list-accounts`、`balance` 等见 [`accounts.md`](accounts.md) |
24
- | 2. 落地页死链强停 | URL 不可用或跳错页时暂停关联创意,避免无效消耗。 | **`google-analysis final-urls`**;或 **`ad list`** / **`google-analysis ads`** 取落地页字段后由宿主 HTTP 校验 |
25
- | 3. 广告素材拒审 | 创意政策状态异常时告警或配合工单。 | **`ad list`** / **`google-analysis ads`**:`policyApprovalStatusV2`、`approvalStatusDetails` 等(见 **[`google-ads.md`](google-ads.md)**) |
26
- | 4. 花费异动监控 | 系列或账户维度花费相对历史异常波动。 | **`google-analysis campaign-hour`**:按 `campaignId` + `date` + `hour` 看 **`spend`** |
27
- | 5. 余额枯竭预警 | 余额过低或按日均推算续航不足。 | **`balance-scan`**(`balance`、`remainingDays`、`hitReason`、`meta`)或 **`balance`** / **`google-analysis overview`**(`remainingAccountBudget`、`averageDailyCost` 等) |
24
+ | 2. 落地页死链强停 | URL 不可用或跳错页时暂停关联创意,避免无效消耗。 | **`google-analysis --sections final-urls`**;或 **`ad list`** / **`google-analysis --sections ads`** 取落地页字段后由宿主 HTTP 校验 |
25
+ | 3. 广告素材拒审 | 创意政策状态异常时告警或配合工单。 | **`ad list`** / **`google-analysis --sections ads`**:`policyApprovalStatusV2`、`approvalStatusDetails` 等(见 **[`google-ads.md`](google-ads.md)**) |
26
+ | 4. 花费异动监控 | 系列或账户维度花费相对历史异常波动。 | **`google-analysis --sections campaign-hour`**:按 `campaignId` + `date` + `hour` 看 **`spend`** |
27
+ | 5. 余额枯竭预警 | 余额过低或按日均推算续航不足。 | **`balance-scan`**(`balance`、`remainingDays`、`hitReason`、`meta`)或 **`balance`** / **`google-analysis --sections overview`**(`remainingAccountBudget`、`averageDailyCost` 等) |
28
28
 
29
29
  ## 自动优化
30
30
 
package/dist/index.js CHANGED
@@ -3538,6 +3538,10 @@ async function writeGoogleAnalysisSnapshot(params) {
3538
3538
  const writtenAt = (/* @__PURE__ */ new Date()).toISOString();
3539
3539
  const body = JSON.stringify(params.payload, null, 2);
3540
3540
  await fs4.writeFile(path6.join(absDir, fileName), `${body}
3541
+ `, "utf8");
3542
+ const outlineFileName = snapshotOutlineFileName(fileName);
3543
+ const outlineBody = formatOutlineFileBody(fileName, params.payload);
3544
+ await fs4.writeFile(path6.join(absDir, outlineFileName), `${outlineBody}
3541
3545
  `, "utf8");
3542
3546
  const existing = await readManifestIfExists(absDir, accountSlug || void 0);
3543
3547
  const newArtifact = {
@@ -3564,8 +3568,10 @@ async function writeGoogleAnalysisSnapshot(params) {
3564
3568
  schemaVersion: SCHEMA_VERSION,
3565
3569
  absoluteSnapshotDir: absDir,
3566
3570
  manifestFile,
3567
- writtenFiles: [fileName],
3568
- section: params.section
3571
+ writtenFiles: [fileName, outlineFileName],
3572
+ section: params.section,
3573
+ outlineFile: outlineFileName,
3574
+ agentHint: OUTLINE_AGENT_HINT
3569
3575
  };
3570
3576
  }
3571
3577
 
@@ -3573,6 +3579,7 @@ async function writeGoogleAnalysisSnapshot(params) {
3573
3579
  var CLI_SNAPSHOT_MANIFEST_FILE = "cli-manifest.json";
3574
3580
  var CLI_PACKAGE2 = "siluzan-tso-cli";
3575
3581
  var SCHEMA_VERSION2 = 1;
3582
+ var OUTLINE_AGENT_HINT = "\u5904\u7406\u987A\u5E8F\uFF1A\u5148\u8BFB outlineFile\uFF08schema \u63CF\u8FF0\uFF0C\u975E\u6570\u636E\uFF09\u4E86\u89E3\u5B57\u6BB5\u7C7B\u578B\uFF0C\u518D\u7528\u811A\u672C\u8BFB writtenFiles[0]\uFF08\u771F\u5B9E JSON \u6570\u636E\uFF09\u505A\u7B5B\u9009/\u805A\u5408\uFF1B\u4E0D\u8981\u628A outline \u5F53\u6210\u6570\u636E\uFF0C\u4E5F\u4E0D\u8981\u628A JSON \u5F53\u6210 schema\u3002";
3576
3583
  var DEFAULT_FIELD_GUIDE2 = {
3577
3584
  markdownRefs: ["references/tips.md", "references/accounts.md"]
3578
3585
  };
@@ -3706,9 +3713,18 @@ function snapshotOutlineFileName(jsonBasename) {
3706
3713
  }
3707
3714
  return `${jsonBasename.slice(0, -".json".length)}.outline.txt`;
3708
3715
  }
3716
+ function formatOutlineFileBody(jsonBasename, payload) {
3717
+ const typeText = describePayloadTsLike(payload);
3718
+ const lines = [
3719
+ `// outline of \`${jsonBasename}\` \u2014 schema-only, NOT the data.`,
3720
+ `// \u7528\u6CD5\uFF1A\u5904\u7406\u540C\u76EE\u5F55 \`${jsonBasename}\` \u7684\u6570\u636E\u524D\uFF0C\u5148\u8BFB\u672C\u6587\u4EF6\u4E86\u89E3\u5B57\u6BB5\u7C7B\u578B\uFF0C\u518D\u7528\u811A\u672C\uFF08fs.readFileSync / require\uFF09\u8BFB\u53D6\u8BE5 JSON \u505A\u7B5B\u9009\u4E0E\u805A\u5408\u3002`,
3721
+ `// \u7C7B\u578B\u7531 JSON \u63A8\u65AD\uFF1A\u6570\u7EC4\u53D6\u524D 8 \u9879\u53BB\u91CD\u5E76\u96C6\uFF1B\u73AF\u6216\u91CD\u590D\u5BF9\u8C61\u5F15\u7528\u8BB0\u4E3A any\u3002`,
3722
+ typeText
3723
+ ];
3724
+ return lines.join("\n");
3725
+ }
3709
3726
  async function writeCliJsonSnapshot(params) {
3710
3727
  const trimmed = params.snapshotDir.trim();
3711
- const outlineText = describePayloadTsLike(params.payload);
3712
3728
  const idSlug = slugifyIdSuffix(params.idSuffix);
3713
3729
  const manifestFile = cliManifestFileName(idSlug || void 0);
3714
3730
  let absDir;
@@ -3744,7 +3760,8 @@ async function writeCliJsonSnapshot(params) {
3744
3760
  if (outlineFileName.toLowerCase() === manifestFile.toLowerCase()) {
3745
3761
  throw new Error(`\u975E\u6CD5\u8F93\u51FA\uFF1A${outlineFileName} \u4E0E\u6E05\u5355\u6587\u4EF6\u540D\u51B2\u7A81`);
3746
3762
  }
3747
- await fs5.writeFile(path7.join(absDir, outlineFileName), `${outlineText}
3763
+ const outlineBody = formatOutlineFileBody(fileName, params.payload);
3764
+ await fs5.writeFile(path7.join(absDir, outlineFileName), `${outlineBody}
3748
3765
  `, "utf8");
3749
3766
  const existing = await readCliManifestIfExists(absDir, idSlug || void 0);
3750
3767
  const newArtifact = {
@@ -3770,7 +3787,8 @@ async function writeCliJsonSnapshot(params) {
3770
3787
  manifestFile,
3771
3788
  writtenFiles: [fileName, outlineFileName],
3772
3789
  section,
3773
- outlineFile: outlineFileName
3790
+ outlineFile: outlineFileName,
3791
+ agentHint: OUTLINE_AGENT_HINT
3774
3792
  };
3775
3793
  }
3776
3794
  async function emitCliJsonOrSnapshot(opts, params) {
@@ -6429,7 +6447,7 @@ async function writeReportAnalysisSnapshot(params) {
6429
6447
  );
6430
6448
  await fs8.writeFile(
6431
6449
  path11.join(absDir, outlineFileName),
6432
- `${describePayloadTsLike(params.payload)}
6450
+ `${formatOutlineFileBody(fileName, params.payload)}
6433
6451
  `,
6434
6452
  "utf8"
6435
6453
  );
@@ -6459,7 +6477,8 @@ async function writeReportAnalysisSnapshot(params) {
6459
6477
  manifestFile,
6460
6478
  writtenFiles: [fileName, outlineFileName],
6461
6479
  section: params.section,
6462
- outlineFile: outlineFileName
6480
+ outlineFile: outlineFileName,
6481
+ agentHint: OUTLINE_AGENT_HINT
6463
6482
  };
6464
6483
  }
6465
6484
 
@@ -7305,22 +7324,6 @@ function manifestDateRange(def, start, end) {
7305
7324
  const { startDate, endDate } = resolveDateRange(start, end);
7306
7325
  return { mode: "range", start: startDate, end: endDate };
7307
7326
  }
7308
- function summarizeHuman(section, data) {
7309
- if (data === null || data === void 0) return "\u65E0\u6570\u636E";
7310
- if (Array.isArray(data)) return `\u6570\u7EC4\uFF0C\u5171 ${data.length} \u6761`;
7311
- if (typeof data === "object") {
7312
- const o = data;
7313
- if (Array.isArray(o.keywords)) return `keywords\uFF1A${o.keywords.length} \u6761`;
7314
- if (Array.isArray(o.campaigns)) return `campaigns\uFF1A${o.campaigns.length} \u6761`;
7315
- if (Array.isArray(o.devices)) return `devices\uFF1A${o.devices.length} \u6761`;
7316
- if (Array.isArray(o.countries)) return `countries\uFF1A${o.countries.length} \u6761`;
7317
- if (Array.isArray(o.audience)) return `audience\uFF1A${o.audience.length} \u6761`;
7318
- if (Array.isArray(o.data)) return `data\uFF1A${o.data.length} \u6761`;
7319
- const keys = Object.keys(o);
7320
- return `\u5BF9\u8C61\uFF0C\u9876\u5C42\u5B57\u6BB5\uFF1A${keys.slice(0, 8).join(", ")}${keys.length > 8 ? "\u2026" : ""}`;
7321
- }
7322
- return String(data);
7323
- }
7324
7327
  async function fetchSectionPayload(def, opts, config, id) {
7325
7328
  const extras = {};
7326
7329
  if (def.keywordOptions) {
@@ -7363,63 +7366,6 @@ function endpointHintForSection(def) {
7363
7366
  if (def.name === "materials") return "CampaignAssetView+Videos";
7364
7367
  return endpointHintFrom(def);
7365
7368
  }
7366
- async function runOneSection(def, opts) {
7367
- const config = loadConfig(opts.token);
7368
- const id = opts.account.trim();
7369
- if (!/^\d+$/.test(id)) {
7370
- console.error("\n\u274C --account \u987B\u4E3A\u6570\u5B57 mediaCustomerId\u3002\n");
7371
- process.exit(1);
7372
- }
7373
- if (opts.json && opts.jsonOut) {
7374
- console.error(
7375
- "\n\u274C --json \u4E0E --json-out \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528\u3002\n"
7376
- );
7377
- process.exit(1);
7378
- }
7379
- try {
7380
- const payload = await fetchSectionPayload(def, opts, config, id);
7381
- if (opts.json) {
7382
- console.log(JSON.stringify(payload, null, 2));
7383
- return;
7384
- }
7385
- if (opts.jsonOut) {
7386
- const summary = await writeGoogleAnalysisSnapshot({
7387
- snapshotDir: opts.jsonOut,
7388
- section: def.name,
7389
- accountId: id,
7390
- dateRange: manifestDateRange(def, opts.start, opts.end),
7391
- payload,
7392
- cliVersion: getCurrentVersion2(),
7393
- endpointHint: endpointHintForSection(def)
7394
- });
7395
- console.log(JSON.stringify(summary));
7396
- return;
7397
- }
7398
- if (def.name === "materials") {
7399
- const m = payload;
7400
- const iLen = Array.isArray(m.images) ? m.images.length : 0;
7401
- const vLen = Array.isArray(m.videos) ? m.videos.length : 0;
7402
- console.log(
7403
- `
7404
- \u2705 google-analysis materials \u8D26\u6237 ${id}
7405
- \u56FE\u7247\u7D20\u6750\uFF1A${iLen} \u6761\uFF0C\u89C6\u9891\u7D20\u6750\uFF1A${vLen} \u6761\uFF08\u5B8C\u6574\u6570\u636E\u8BF7\u52A0 --json-out <\u76EE\u5F55>\uFF09
7406
- `
7407
- );
7408
- return;
7409
- }
7410
- console.log(
7411
- `
7412
- \u2705 google-analysis ${def.name} \u8D26\u6237 ${id}
7413
- ${summarizeHuman(def.name, payload)}
7414
- `
7415
- );
7416
- } catch (err) {
7417
- console.error(`
7418
- \u274C \u8BF7\u6C42\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
7419
- `);
7420
- process.exit(1);
7421
- }
7422
- }
7423
7369
  function resolveSectionList(sections, exclude) {
7424
7370
  const allNames = SECTIONS.map((s) => s.name);
7425
7371
  const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
@@ -7523,13 +7469,15 @@ async function runAllSections(opts) {
7523
7469
  if (failed > 0) process.exit(2);
7524
7470
  }
7525
7471
  function registerGoogleAnalysisCommands(program2) {
7526
- const root = program2.command("google-analysis").description("Google \u8D26\u6237\u5206\u6790\u7F51\u5173\uFF08googleApiUrl\uFF09\uFF1A\u6309\u7EF4\u5EA6\u62C9\u53D6\u62A5\u8868/\u7ED3\u6784\u6570\u636E");
7527
- root.command("all").description(
7528
- "\u5355\u8FDB\u7A0B\u6279\u91CF\u62C9\u53D6\u6240\u6709\u7EF4\u5EA6\uFF08\u9ED8\u8BA4 21 \u4E2A\uFF09\uFF1A\u590D\u7528\u8FDB\u7A0B + keep-alive\uFF0C\u6BD4\u9010\u4E2A\u8C03\u7528\u5FEB 3-7x"
7529
- ).requiredOption("-a, --account <id>", "Google mediaCustomerId").requiredOption(
7472
+ program2.command("google-analysis").description(
7473
+ "Google \u8D26\u6237\u5206\u6790\uFF08googleApiUrl\uFF09\uFF1A\u5355\u8FDB\u7A0B\u6279\u91CF\u62C9\u53D6\u4E00\u4E2A\u6216\u591A\u4E2A\u7EF4\u5EA6\uFF0C\u81EA\u52A8\u590D\u7528 keep-alive \u8FDE\u63A5"
7474
+ ).argument("[legacy-section]", "\uFF08\u5DF2\u5E9F\u5F03\uFF09\u5386\u53F2\u7684\u5355\u7EF4\u5EA6\u5B50\u547D\u4EE4\u540D\uFF0C\u8BF7\u6539\u7528 --sections <name>").requiredOption("-a, --account <id>", "Google mediaCustomerId").requiredOption(
7530
7475
  "--json-out <dir>",
7531
- "\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u4E2A\u7EF4\u5EA6\u4E00\u4E2A <section>-<accountId>.json\uFF0C\u4E0E manifest-<accountId>.json \u540C\u7EA7"
7532
- ).option("-t, --token <token>", "Auth Token").option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08range \u7C7B\u7EF4\u5EA6\u751F\u6548\uFF1B\u9ED8\u8BA4\u8FD1 7 \u5929\u622A\u81F3\u6628\u5929\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
7476
+ "\u7ED3\u679C\u843D\u76D8\u76EE\u5F55\uFF1B\u6BCF\u7EF4\u5EA6\u4E00\u4E2A <section>-<accountId>.json\uFF0C\u4E0E manifest-<accountId>.json \u540C\u7EA7"
7477
+ ).option("-t, --token <token>", "Auth Token").option(
7478
+ "--start <date>",
7479
+ "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08range \u7C7B\u7EF4\u5EA6\u751F\u6548\uFF1B\u7701\u7565=\u8FD1 7 \u5929\u622A\u81F3\u6628\u5929\uFF1B\u4E0E --end \u540C\u4F20\u6216\u540C\u7701\uFF09"
7480
+ ).option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
7533
7481
  "--sections <list>",
7534
7482
  "\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,keywords,ads\uFF1B\u7701\u7565=\u5168\u90E8 21 \u4E2A"
7535
7483
  ).option(
@@ -7537,35 +7485,48 @@ function registerGoogleAnalysisCommands(program2) {
7537
7485
  "\u6392\u9664\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 materials,gold-account\uFF1B\u4E0E --sections \u53EF\u53E0\u52A0"
7538
7486
  ).option(
7539
7487
  "--concurrency <n>",
7540
- "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF1B\u4E0A\u9650 16\uFF08\u4E0E http-raw maxSockets \u5BF9\u9F50\uFF09",
7488
+ "\u5E76\u53D1\u6570\uFF0C\u9ED8\u8BA4 5\uFF1B\u4E0A\u9650 16\uFF08\u4E0E http-raw maxSockets \u5BF9\u9F50\uFF09\uFF1B\u8D26\u6237\u5927\u6216\u7F51\u7EDC\u6162\u65F6\u8C03\u5C0F",
7541
7489
  (v) => parseInt(v, 10)
7542
- ).option("--limit <n>", "keywords/search-terms \u7684\u6761\u6570\u4E0A\u9650", (v) => parseInt(v, 10)).option("--no-order-by-cost", "keywords/search-terms \u4E0D\u6309\u6D88\u8017\u6392\u5E8F", false).option("--level <level>", "extensions \u7684 level \u8FC7\u6EE4").option("--audience-type <type>", "audience \u7684 audienceTypeFilter").option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
7543
- await runAllSections(opts);
7544
- });
7545
- for (const def of SECTIONS) {
7546
- const cmd = root.command(def.name).description(def.description).requiredOption("-a, --account <id>", "Google mediaCustomerId").option("-t, --token <token>", "Auth Token").option("--json", "\u8F93\u51FA\u5B8C\u6574 JSON", false).option(
7547
- "--json-out <dir>",
7548
- "\u5C06 JSON \u5199\u5165\u76EE\u5F55\uFF08\u6587\u4EF6\u540D\u5E26 accountId \u540E\u7F00\uFF09\u5E76\u66F4\u65B0 manifest-<accountId>.json\uFF08\u4E0E --json \u4E92\u65A5\uFF09\uFF1Bstdout \u4EC5\u4E00\u884C\u6458\u8981 JSON"
7549
- ).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false);
7550
- if (def.dateMode === "range") {
7551
- cmd.option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u4E0E --end \u540C\u4F20\uFF0C\u6216\u5747\u7701\u7565\u7528\u9ED8\u8BA4\u8FD1 7 \u5929\uFF09").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD");
7552
- }
7553
- if (def.keywordOptions) {
7554
- cmd.option("--limit <n>", "\u6761\u6570\u4E0A\u9650\uFF08\u9ED8\u8BA4 100\uFF09", (v) => parseInt(v, 10)).option("--no-order-by-cost", "\u4E0D\u6309\u6D88\u8017\u6392\u5E8F", false);
7555
- }
7556
- if (def.extensionLevelOption) {
7557
- cmd.option(
7558
- "--level <level>",
7559
- "\u6309\u5C42\u7EA7\u7B5B\u9009\uFF1AAccount | Campaign | Ad Group\uFF08\u4F20\u5165\u5219\u4F5C\u4E3A\u67E5\u8BE2\u53C2\u6570\uFF09"
7490
+ ).option("--limit <n>", "\u900F\u4F20\u7ED9 keywords / search-terms \u7EF4\u5EA6\u7684\u6761\u6570\u4E0A\u9650", (v) => parseInt(v, 10)).option("--no-order-by-cost", "\u900F\u4F20\uFF1Akeywords / search-terms \u4E0D\u6309\u6D88\u8017\u6392\u5E8F", false).option(
7491
+ "--level <level>",
7492
+ "\u900F\u4F20\u7ED9 extensions \u7EF4\u5EA6\u7684 level \u8FC7\u6EE4\uFF1AAccount | Campaign | Ad Group"
7493
+ ).option(
7494
+ "--audience-type <type>",
7495
+ "\u900F\u4F20\u7ED9 audience \u7EF4\u5EA6\u7684 audienceTypeFilter\uFF1ASystemDefined | UserDefined"
7496
+ ).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).addHelpText(
7497
+ "after",
7498
+ [
7499
+ "",
7500
+ "\u53EF\u7528\u7EF4\u5EA6\uFF0821 \u4E2A\uFF0C\u4E0E --sections / --exclude \u53D6\u503C\u4E00\u81F4\uFF09\uFF1A",
7501
+ ...SECTIONS.map((s) => ` ${s.name.padEnd(20)} ${s.description}`),
7502
+ "",
7503
+ "\u793A\u4F8B\uFF1A",
7504
+ " # \u62C9\u5168\u90E8 21 \u4E2A\u7EF4\u5EA6",
7505
+ " siluzan-tso google-analysis -a <mediaCustomerId> --json-out ./snap",
7506
+ "",
7507
+ " # \u4EC5\u62C9\u5355\u4E2A\u7EF4\u5EA6\uFF08\u66FF\u4EE3\u5386\u53F2\u7684 google-analysis overview ...\uFF09",
7508
+ " siluzan-tso google-analysis -a <mediaCustomerId> --sections overview --json-out ./snap",
7509
+ "",
7510
+ " # \u4EC5\u62C9\u62A5\u544A\u9700\u8981\u7684\u5B50\u96C6",
7511
+ " siluzan-tso google-analysis -a <mediaCustomerId> --start 2026-04-01 --end 2026-04-30 \\",
7512
+ " --sections overview,daily-metrics,campaigns,devices,geographic,keywords --json-out ./snap"
7513
+ ].join("\n")
7514
+ ).action(async (legacySection, opts) => {
7515
+ if (legacySection) {
7516
+ const sectionNames = SECTIONS.map((s) => s.name);
7517
+ const isKnown = sectionNames.includes(legacySection);
7518
+ const fixHint = isKnown ? ` siluzan-tso google-analysis -a ${opts.account ?? "<id>"} --sections ${legacySection} --json-out <dir>` : ` \u672A\u77E5\u7EF4\u5EA6\u540D"${legacySection}"\u3002\u53EF\u9009\u7EF4\u5EA6\uFF1A${sectionNames.join(", ")}`;
7519
+ console.error(
7520
+ `
7521
+ \u274C google-analysis \u5DF2\u4E0D\u518D\u652F\u6301\u5355\u7EF4\u5EA6\u5B50\u547D\u4EE4\uFF08\u66FE\u7ECF\u7684 \`google-analysis ${legacySection} ...\` \u7528\u6CD5\u5DF2\u5220\u9664\uFF09\u3002
7522
+ \u73B0\u5728\u6240\u6709\u7EF4\u5EA6\u90FD\u901A\u8FC7 --sections \u9009\u53D6\uFF0C\u5355\u7EF4\u5EA6\u7B49\u4EF7\u5199\u6CD5\uFF1A
7523
+ ${fixHint}
7524
+ `
7560
7525
  );
7526
+ process.exit(1);
7561
7527
  }
7562
- if (def.audienceFilterOption) {
7563
- cmd.option("--audience-type <type>", "audienceTypeFilter\uFF1ASystemDefined | UserDefined");
7564
- }
7565
- cmd.action(async (opts) => {
7566
- await runOneSection(def, opts);
7567
- });
7568
- }
7528
+ await runAllSections(opts);
7529
+ });
7569
7530
  }
7570
7531
 
7571
7532
  // src/commands/report-tiktok-analysis.ts