siluzan-tso-cli 1.1.37-beta.14 → 1.1.37-beta.16

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 (129) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +260 -32
  3. package/dist/skill/AGENTS.md +28 -21
  4. package/dist/skill/SKILL.md +12 -11
  5. package/dist/skill/_meta.json +2 -2
  6. package/dist/skill/assets/pmax-create-template.md +1 -1
  7. package/dist/skill/references/README.md +10 -10
  8. package/dist/skill/references/accounts/accounts-balance-stats.md +35 -18
  9. package/dist/skill/references/accounts/accounts-list.md +1 -1
  10. package/dist/skill/references/accounts/accounts-permissions.md +2 -9
  11. package/dist/skill/references/accounts/accounts.md +3 -3
  12. package/dist/skill/references/accounts/finance.md +1 -2
  13. package/dist/skill/references/accounts/open-account-by-media.md +1 -2
  14. package/dist/skill/references/analytics/account-analytics.md +4 -7
  15. package/dist/skill/references/analytics/google-analysis-batch.md +1 -2
  16. package/dist/skill/references/analytics/keyword-planner-workflows.md +3 -3
  17. package/dist/skill/references/analytics/market-analysis-guide.md +1 -4
  18. package/dist/skill/references/analytics/reporting.md +1 -3
  19. package/dist/skill/references/core/agent-conventions.md +72 -8
  20. package/dist/skill/references/core/intent-routing.md +54 -11
  21. package/dist/skill/references/core/playbooks.md +24 -19
  22. package/dist/skill/references/core/setup.md +1 -4
  23. package/dist/skill/references/core/skill-authoring.md +18 -6
  24. package/dist/skill/references/core/user-communication-guide.md +2 -116
  25. package/dist/skill/references/core/workflows.md +17 -18
  26. package/dist/skill/references/google-ads/google-ads-batch.md +3 -4
  27. package/dist/skill/references/google-ads/google-ads-campaign-plan.md +2 -2
  28. package/dist/skill/references/google-ads/google-ads-read.md +3 -4
  29. package/dist/skill/references/google-ads/google-ads-write.md +3 -4
  30. package/dist/skill/references/google-ads/google-ads.md +3 -3
  31. package/dist/skill/references/google-ads/rules/README.md +3 -4
  32. package/dist/skill/references/google-ads/rules/google-ads-account-audit.md +2 -3
  33. package/dist/skill/references/google-ads/rules/google-ads-audience-strategy.md +1 -2
  34. package/dist/skill/references/google-ads/rules/google-ads-campaign-optimization.md +1 -3
  35. package/dist/skill/references/google-ads/rules/google-ads-compliance.md +2 -4
  36. package/dist/skill/references/google-ads/rules/google-ads-conversion-architecture.md +1 -2
  37. package/dist/skill/references/google-ads/rules/google-ads-creative-optimization.md +1 -3
  38. package/dist/skill/references/google-ads/rules/google-ads-keyword-optimization.md +1 -3
  39. package/dist/skill/references/google-ads/rules/google-ads-keyword-strategy.md +1 -3
  40. package/dist/skill/references/google-ads/rules/google-ads-keyword-taxonomy.md +1 -2
  41. package/dist/skill/references/google-ads/rules/google-ads-launch-plan-template.md +1 -5
  42. package/dist/skill/references/google-ads/rules/google-ads-plan-source-fidelity.md +1 -4
  43. package/dist/skill/references/google-ads/rules/google-ads-pmax-guide.md +1 -2
  44. package/dist/skill/references/google-ads/rules/sensitive-industries.md +1 -3
  45. package/dist/skill/references/operations/clue.md +1 -3
  46. package/dist/skill/references/operations/hosted-automation-optimize-scale.md +1 -2
  47. package/dist/skill/references/operations/hosted-automation-user-catalog.md +1 -3
  48. package/dist/skill/references/operations/optimize.md +1 -3
  49. package/dist/skill/references/report-templates/google-inquiry-analysis.md +1 -3
  50. package/dist/skill/references/report-templates/meta-period-report-excel.md +1 -2
  51. package/dist/skill/report-templates/google-inquiry-analysis.md +1 -3
  52. package/dist/skill/report-templates/meta-period-report-excel.md +1 -2
  53. package/dist/skill/scripts/install.ps1 +1 -1
  54. package/dist/skill/scripts/install.sh +1 -1
  55. package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +14 -2
  56. package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +3 -1
  57. package/eval/cases/accounts-single-balance-not-bulk.scenario.json +14 -3
  58. package/eval/cases/budget-display-not-raw-micros.scenario.json +8 -1
  59. package/eval/cases/clue-meta-leads-json.scenario.json +14 -2
  60. package/eval/cases/clue-tiktok-leads-json.scenario.json +11 -2
  61. package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +9 -3
  62. package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +9 -3
  63. package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +9 -3
  64. package/eval/cases/facebook-analysis-google-section-aliases.scenario.json +11 -2
  65. package/eval/cases/facebook-analysis-not-google-keywords.scenario.json +10 -3
  66. package/eval/cases/facebook-analysis-period-default.scenario.json +14 -2
  67. package/eval/cases/finance-invoice-info-list.scenario.json +11 -3
  68. package/eval/cases/forewarning-list-google.scenario.json +14 -3
  69. package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +6 -2
  70. package/eval/cases/google-analysis-keywords-route.scenario.json +14 -2
  71. package/eval/cases/human-p1-multiturn.scenario.json +5 -1
  72. package/eval/cases/ir-p1-vs-p8-account-vs-url.scenario.json +17 -5
  73. package/eval/cases/ir-p3-cpa-monitor-not-p7.scenario.json +33 -0
  74. package/eval/cases/ir-p3-zero-inquiry-not-p7.scenario.json +27 -0
  75. package/eval/cases/ir-p4-period-not-diagnosis.scenario.json +16 -3
  76. package/eval/cases/ir-p8-network-diagnosis-with-url.scenario.json +25 -3
  77. package/eval/cases/ir-p8-website-monitor-report-with-url.scenario.json +28 -3
  78. package/eval/cases/ir-p8-website-test-report-no-url.scenario.json +14 -3
  79. package/eval/cases/ir-p9-industry-not-p8.scenario.json +19 -3
  80. package/eval/cases/ir-vague-report-clarify.scenario.json +14 -3
  81. package/eval/cases/ir-w3-vs-p8-from-website.scenario.json +8 -2
  82. package/eval/cases/ir-w5-keyword-planner-url-core-terms.scenario.json +22 -2
  83. package/eval/cases/meta-single-balance-not-bulk.scenario.json +17 -3
  84. package/eval/cases/no-legacy-json-flag.scenario.json +6 -2
  85. package/eval/cases/open-account-google-noninteractive.scenario.json +3 -1
  86. package/eval/cases/open-account-tiktok-license-file.scenario.json +3 -1
  87. package/eval/cases/optimize-list-by-account.scenario.json +11 -3
  88. package/eval/cases/p1-single-account-profile.scenario.json +11 -1
  89. package/eval/cases/p2-balance-scan-bulk.scenario.json +9 -2
  90. package/eval/cases/p3-accounts-digest.scenario.json +5 -1
  91. package/eval/cases/p3-cpa-scan-max-cpa.scenario.json +23 -0
  92. package/eval/cases/p4-fb-meta-period-report.scenario.json +12 -2
  93. package/eval/cases/p4-period-report-window.scenario.json +8 -1
  94. package/eval/cases/pmax-asset-group-create-with-bg.scenario.json +13 -2
  95. package/eval/cases/pmax-brand-edit-routing.scenario.json +16 -3
  96. package/eval/cases/pmax-edit-not-campaign-edit.scenario.json +12 -2
  97. package/eval/cases/pmax-enable-brand-guidelines.scenario.json +12 -2
  98. package/eval/cases/pmax-no-assets-update-brand-fields.scenario.json +14 -3
  99. package/eval/cases/rag-before-keyword-expand.scenario.json +11 -1
  100. package/eval/cases/rag-list-then-query.scenario.json +14 -2
  101. package/eval/cases/report-list-google.scenario.json +11 -2
  102. package/eval/cases/report-push-list-google.scenario.json +11 -2
  103. package/eval/cases/setup-login-or-env.scenario.json +3 -1
  104. package/eval/cases/setup-siluzan-data-permission-env.scenario.json +3 -1
  105. package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +6 -2
  106. package/eval/cases/time-range-user-delegates-default.scenario.json +8 -1
  107. package/eval/cases/tips-json-out-filtering.scenario.json +3 -1
  108. package/eval/cases/tips-large-json-pagination.scenario.json +3 -1
  109. package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +11 -2
  110. package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +3 -1
  111. package/eval/cases/uj-ad-website-search-plan-table-format.scenario.json +3 -1
  112. package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +18 -6
  113. package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +8 -1
  114. package/eval/cases/uj-analytics-ecommerce-industry-report.scenario.json +14 -2
  115. package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +11 -2
  116. package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +3 -1
  117. package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +11 -2
  118. package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +4 -1
  119. package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +17 -2
  120. package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +14 -2
  121. package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +6 -2
  122. package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +17 -3
  123. package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +5 -1
  124. package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +3 -1
  125. package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +17 -3
  126. package/eval/cases/uj-patrol-google-balances-low.scenario.json +11 -2
  127. package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +14 -3
  128. package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +14 -2
  129. package/package.json +1 -1
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.37-beta.14),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.37-beta.16),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -5023,6 +5023,33 @@ var init_list_fetch = __esm({
5023
5023
  }
5024
5024
  });
5025
5025
 
5026
+ // src/commands/accounts-digest/filters.ts
5027
+ function keepDigestRow(row, opts) {
5028
+ const { maxCpa, zeroConversions } = opts;
5029
+ if (maxCpa == null && !zeroConversions) return true;
5030
+ const spend = row.spend ?? 0;
5031
+ const conv = row.conversions;
5032
+ const cpa = row.cpa;
5033
+ if (zeroConversions) {
5034
+ if (spend > 0 && (conv == null || conv === 0)) return true;
5035
+ if (maxCpa == null) return false;
5036
+ }
5037
+ if (maxCpa != null) {
5038
+ if (cpa != null && cpa > maxCpa) return true;
5039
+ if (spend > 0 && conv === 0) return true;
5040
+ return false;
5041
+ }
5042
+ return false;
5043
+ }
5044
+ function countNullConversions(rows) {
5045
+ return rows.reduce((n, r) => n + (r.conversions == null ? 1 : 0), 0);
5046
+ }
5047
+ var init_filters = __esm({
5048
+ "src/commands/accounts-digest/filters.ts"() {
5049
+ "use strict";
5050
+ }
5051
+ });
5052
+
5026
5053
  // src/commands/accounts-digest/run.ts
5027
5054
  function round2(n) {
5028
5055
  if (n == null) return null;
@@ -5136,7 +5163,7 @@ async function runAccountsDigest(opts) {
5136
5163
  const ctr = impressions && impressions > 0 && clicks != null ? clicks / impressions : null;
5137
5164
  const cpc = clicks && clicks > 0 && spend != null ? spend / clicks : null;
5138
5165
  const cpa = conversions && conversions > 0 && spend != null ? spend / conversions : null;
5139
- rows.push({
5166
+ const row = {
5140
5167
  mediaCustomerId: id,
5141
5168
  name: ma.mediaCustomerName ?? bal?.name ?? ov?.mediaCustomerName ?? null,
5142
5169
  advertiserName: item.mag?.advertiserName ?? null,
@@ -5151,7 +5178,14 @@ async function runAccountsDigest(opts) {
5151
5178
  cpc: round2(cpc),
5152
5179
  cpa: round2(cpa),
5153
5180
  invalidOAuthToken: !!ma.invalidOAuthToken
5154
- });
5181
+ };
5182
+ if (!keepDigestRow(row, {
5183
+ maxCpa: opts.maxCpa,
5184
+ zeroConversions: opts.zeroConversions
5185
+ })) {
5186
+ continue;
5187
+ }
5188
+ rows.push(row);
5155
5189
  }
5156
5190
  rows.sort((a, b) => (b.spend ?? 0) - (a.spend ?? 0));
5157
5191
  const totals = rows.reduce(
@@ -5175,6 +5209,12 @@ async function runAccountsDigest(opts) {
5175
5209
  /** 取数策略:list = 全量翻清单后过滤,subset = 跳过翻页直接对 -a 指定的 ID 拉数据 */
5176
5210
  source: scannedFromList ? "list" : "subset",
5177
5211
  returned: rows.length,
5212
+ nullConversionCount: countNullConversions(rows),
5213
+ filters: {
5214
+ minSpend,
5215
+ maxCpa: opts.maxCpa ?? null,
5216
+ zeroConversions: !!opts.zeroConversions
5217
+ },
5178
5218
  batchChunkSize: validIds.length > 0 ? batchChunkSize : null,
5179
5219
  totals: {
5180
5220
  spend: +totals.spend.toFixed(2),
@@ -5234,13 +5274,20 @@ ${media} \u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u8FD4\u56DE ${rows.length} \
5234
5274
  balance: formatMoneyDisplay(r.balance, r.currencyCode)
5235
5275
  }));
5236
5276
  printCliTable(tableRows, cols);
5277
+ if (meta.nullConversionCount > 0) {
5278
+ process.stderr.write(
5279
+ `\u26A0 \u8F6C\u5316\u5B57\u6BB5\u4E3A\u7A7A ${meta.nullConversionCount} \u6237\uFF1A\u52FF\u7528\u6570\u503C\u6BD4\u8F83 null\uFF1B\u8FC7\u6EE4\u89C1 --max-cpa / --zero-conversions
5280
+ `
5281
+ );
5282
+ }
5237
5283
  console.log(
5238
5284
  `
5239
- \u6C47\u603B\uFF1A\u6D88\u8017 ${meta.totals.spend}\uFF0C\u70B9\u51FB ${meta.totals.clicks}\uFF0C\u5C55\u793A ${meta.totals.impressions}\uFF0C\u8F6C\u5316 ${meta.totals.conversions}
5285
+ \u6C47\u603B\uFF1A\u6D88\u8017 ${meta.totals.spend}\uFF0C\u70B9\u51FB ${meta.totals.clicks}\uFF0C\u5C55\u793A ${meta.totals.impressions}\uFF0C\u8F6C\u5316 ${meta.totals.conversions}\uFF08\u8F6C\u5316\u672A\u8FD4\u56DE ${meta.nullConversionCount} \u6237\uFF09
5240
5286
  \u63D0\u793A\uFF1A
5241
5287
  - \u91D1\u989D\u4F7F\u7528\u5404\u8D26\u6237\u4E3B\u5E01\u79CD\uFF1B\u8DE8\u5E01\u79CD\u8D26\u6237\u52A0 --json-out \u81EA\u884C\u6309 currencyCode \u805A\u5408
5242
5288
  - \u6307\u5B9A\u8D26\u6237\u5B50\u96C6\uFF1A--accounts <id1,id2,...>\uFF08\u4E0E list-accounts \u7684 mediaCustomerId \u4E00\u81F4\uFF09
5243
- - \u8FC7\u6EE4\u4F4E\u6D88\u8017\uFF1A--min-spend 10\uFF08\u53EA\u4FDD\u7559\u533A\u95F4\u5185\u6D88\u8017 > 10 \u7684\u8D26\u6237\uFF09
5289
+ - \u8FC7\u6EE4\u4F4E\u6D88\u8017\uFF1A--min-spend 10\uFF1B\u9AD8 CPA\uFF1A--max-cpa 500\uFF1B\u96F6\u8F6C\u5316\uFF1A--zero-conversions
5290
+ - \u65E0 --period\uFF1B\u533A\u95F4\u7528 --start/--end\uFF08\u6216 --start-date/--end-date\uFF09
5244
5291
  `
5245
5292
  );
5246
5293
  }
@@ -5255,23 +5302,32 @@ var init_run = __esm({
5255
5302
  init_media_account_batch();
5256
5303
  init_media_account_lookup();
5257
5304
  init_list_fetch();
5305
+ init_filters();
5258
5306
  }
5259
5307
  });
5260
5308
 
5261
5309
  // src/commands/accounts-digest/_register.ts
5262
5310
  function register10(program2) {
5263
5311
  program2.command("accounts-digest").description(
5264
- "\u591A\u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u4E00\u6761\u547D\u4EE4\u62FF\u9F50\u6D88\u8017/\u70B9\u51FB/\u8F6C\u5316/\u4F59\u989D/\u6D3E\u751F\u6307\u6807\uFF08CTR/CPC/CPA\uFF09\uFF0C\u66FF\u4EE3 AI \u9010\u8D26\u6237 `list-accounts` + `stats` \u7684\u7F16\u6392\u3002\u5178\u578B\u573A\u666F\uFF1A\u300C\u8FD9 10 \u4E2A\u8D26\u6237\uFF08\u6216\u67D0\u5A92\u4F53\u5168\u90E8\u8D26\u6237\uFF09\u5E2E\u6211\u6574\u7406\u4E00\u4E0B\u300D\u3002"
5312
+ "\u591A\u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u4E00\u6761\u547D\u4EE4\u62FF\u9F50\u6D88\u8017/\u70B9\u51FB/\u8F6C\u5316/\u4F59\u989D/\u6D3E\u751F\u6307\u6807\uFF08CTR/CPC/CPA\uFF09\uFF0C\u66FF\u4EE3 AI \u9010\u8D26\u6237 `list-accounts` + `stats` \u7684\u7F16\u6392\u3002\u5178\u578B\u573A\u666F\uFF1A\u300C\u8FD9 10 \u4E2A\u8D26\u6237\uFF08\u6216\u67D0\u5A92\u4F53\u5168\u90E8\u8D26\u6237\uFF09\u5E2E\u6211\u6574\u7406\u4E00\u4E0B\u300D\u3001\u8F6C\u5316\u6210\u672C/CPA/\u96F6\u8F6C\u5316\u5DE1\u68C0\u3002\u65E0 --period\uFF1B\u533A\u95F4\u7528 --start/--end\uFF08\u6216 --start-date/--end-date\uFF09\u3002"
5265
5313
  ).requiredOption(
5266
5314
  "-m, --media <type>",
5267
5315
  "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | MetaAd | BingV2 | Kwai"
5268
5316
  ).option(
5269
5317
  "-a, --accounts <ids>",
5270
5318
  "\u6307\u5B9A\u8D26\u6237 mediaCustomerId\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A\u5219\u626B\u63CF\u8BE5\u5A92\u4F53\u4E0B\u5168\u90E8\u8D26\u6237"
5271
- ).option("--start <date>", "\u7EDF\u8BA1\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08SKILL \u8981\u6C42 AI \u5148\u4E0E\u7528\u6237\u786E\u8BA4\uFF09").option("--end <date>", "\u7EDF\u8BA1\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08SKILL \u8981\u6C42 AI \u5148\u4E0E\u7528\u6237\u786E\u8BA4\uFF09").option(
5319
+ ).option("--start <date>", "\u7EDF\u8BA1\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08SKILL \u8981\u6C42 AI \u5148\u4E0E\u7528\u6237\u786E\u8BA4\uFF09").option("--end <date>", "\u7EDF\u8BA1\u7ED3\u675F\u65E5\u671F YYYY-MM-DD\uFF08SKILL \u8981\u6C42 AI \u5148\u4E0E\u7528\u6237\u786E\u8BA4\uFF09").option("--start-date <date>", "\u540C --start\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("--end-date <date>", "\u540C --end\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option(
5272
5320
  "--min-spend <n>",
5273
5321
  "\u8FC7\u6EE4\uFF1A\u533A\u95F4\u5185\u6D88\u8017 \u2264 \u6B64\u503C\u7684\u8D26\u6237\u4E0D\u8FD4\u56DE\uFF08\u9ED8\u8BA4 0\uFF09",
5274
5322
  (v) => parseFloat(v)
5323
+ ).option(
5324
+ "--max-cpa <n>",
5325
+ "\u8FC7\u6EE4\uFF1A\u4FDD\u7559 CPA > \u6B64\u503C\u7684\u8D26\u6237\uFF1B\u6709\u6D88\u8017\u4E14\u8F6C\u5316\u4E3A 0 \u89C6\u4E3A\u547D\u4E2D\uFF08CPA \u65E0\u7A77\uFF09",
5326
+ (v) => parseFloat(v)
5327
+ ).option(
5328
+ "--zero-conversions",
5329
+ "\u8FC7\u6EE4\uFF1A\u4FDD\u7559\u533A\u95F4\u6D88\u8017 > 0 \u4E14\u8F6C\u5316 = 0 \u6216\u8F6C\u5316\u672A\u8FD4\u56DE\u7684\u8D26\u6237",
5330
+ false
5275
5331
  ).option("--page-size <n>", "\u8D26\u6237\u6E05\u5355\u5206\u9875\u5927\u5C0F\uFF08\u9ED8\u8BA4 200\uFF0C\u4E0A\u9650 500\uFF09", (v) => parseInt(v, 10)).option("--max-pages <n>", "\u6700\u591A\u626B\u63CF\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 200\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option(
5276
5332
  "--json-out <path>",
5277
5333
  "\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
@@ -5282,9 +5338,11 @@ function register10(program2) {
5282
5338
  token: opts.token,
5283
5339
  media: opts.media,
5284
5340
  accounts: opts.accounts,
5285
- startDate: opts.start,
5286
- endDate: opts.end,
5341
+ startDate: opts.start ?? opts.startDate,
5342
+ endDate: opts.end ?? opts.endDate,
5287
5343
  minSpend: opts.minSpend,
5344
+ maxCpa: opts.maxCpa,
5345
+ zeroConversions: opts.zeroConversions,
5288
5346
  pageSize: opts.pageSize,
5289
5347
  maxPages: opts.maxPages,
5290
5348
  jsonOut: opts.jsonOut,
@@ -5307,6 +5365,7 @@ var init_accounts_digest = __esm({
5307
5365
  "use strict";
5308
5366
  init_run();
5309
5367
  init_list_fetch();
5368
+ init_filters();
5310
5369
  init_register();
5311
5370
  }
5312
5371
  });
@@ -103436,8 +103495,8 @@ async function cmdRun(opts) {
103436
103495
  const result = await executeBatchRun({
103437
103496
  jsonOut: opts.jsonOut,
103438
103497
  runId: opts.runId,
103439
- start: opts.start,
103440
- end: opts.end,
103498
+ start: opts.start ?? opts.startDate,
103499
+ end: opts.end ?? opts.endDate,
103441
103500
  sections,
103442
103501
  explicitAccountIds: explicitIds.length > 0 ? explicitIds : void 0,
103443
103502
  accountConcurrency,
@@ -103612,7 +103671,7 @@ function registerGoogleAnalysisBatchCommands(program2) {
103612
103671
  grp.command("run").description("\u4E00\u6B21\u6027\u6279\u8DD1\uFF1A\u81EA\u52A8\u62C9\u8D26\u6237\u6E05\u5355 + \u6309\u8D26\u6237\xD7\u7EF4\u5EA6\u5E76\u53D1\u6267\u884C + \u843D\u76D8 manifest/progress").requiredOption("--json-out <baseDir>", "\u4EA7\u7269\u6839\u76EE\u5F55\uFF08\u6BCF\u4E2A runId \u4E00\u4E2A\u5B50\u76EE\u5F55\uFF09").option("--run-id <id>", "\u53EF\u9009\u81EA\u5B9A\u4E49 runId\uFF08\u7559\u7A7A\u81EA\u52A8\u751F\u6210 run-YYYYMMDD-HHmmss-<rand4>\uFF09").option(
103613
103672
  "-a, --accounts <ids>",
103614
103673
  "\u663E\u5F0F\u6307\u5B9A mediaCustomerId \u5217\u8868\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF1B\u4F20\u5165\u5219\u8DF3\u8FC7\u8D26\u6237\u6E05\u5355\u62C9\u53D6\u4E0E --min-spend \u8FC7\u6EE4"
103615
- ).option("--start <date>", "\u7EDF\u8BA1\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u5F3A\u70C8\u5EFA\u8BAE\u663E\u5F0F\u6307\u5B9A\uFF09").option("--end <date>", "\u7EDF\u8BA1\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--sections <list>", `\u8981\u62C9\u53D6\u7684\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u9ED8\u8BA4 ${DEFAULT_SECTIONS.join(",")}`).option(
103674
+ ).option("--start <date>", "\u7EDF\u8BA1\u5F00\u59CB\u65E5\u671F YYYY-MM-DD\uFF08\u5F3A\u70C8\u5EFA\u8BAE\u663E\u5F0F\u6307\u5B9A\uFF09").option("--end <date>", "\u7EDF\u8BA1\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--start-date <date>", "\u540C --start\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("--end-date <date>", "\u540C --end\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("--sections <list>", `\u8981\u62C9\u53D6\u7684\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u9ED8\u8BA4 ${DEFAULT_SECTIONS.join(",")}`).option(
103616
103675
  "--account-concurrency <n>",
103617
103676
  `\u8D26\u6237\u7EA7\u5E76\u53D1\uFF0C\u9ED8\u8BA4 ${DEFAULT_ACCOUNT_CONCURRENCY}\uFF08\u4E0A\u9650 16\uFF09`,
103618
103677
  (v) => parseInt(v, 10)
@@ -103681,6 +103740,13 @@ var init_google_analysis_batch = __esm({
103681
103740
  import * as path30 from "path";
103682
103741
  import { performance as performance4 } from "perf_hooks";
103683
103742
  import { Command } from "commander";
103743
+ function normalizeAnalysisDateOpts(opts) {
103744
+ return {
103745
+ ...opts,
103746
+ start: opts.start ?? opts.startDate,
103747
+ end: opts.end ?? opts.endDate
103748
+ };
103749
+ }
103684
103750
  function resolveSectionList(sections, exclude) {
103685
103751
  const allNames = SECTIONS.map((s) => s.name);
103686
103752
  const splitClean = (s) => (s ?? "").split(",").map((x) => x.trim()).filter(Boolean);
@@ -103900,7 +103966,7 @@ function registerRunCommand() {
103900
103966
  ).option("-t, --token <token>", "Auth Token").option(
103901
103967
  "--start <date>",
103902
103968
  "\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"
103903
- ).option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option(
103969
+ ).option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("--start-date <date>", "\u540C --start\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option("--end-date <date>", "\u540C --end\uFF08\u6587\u6863/Playbook \u517C\u5BB9\u522B\u540D\uFF09").option(
103904
103970
  "--sections <list>",
103905
103971
  "\u4EC5\u6267\u884C\u6307\u5B9A\u7EF4\u5EA6\uFF08\u9017\u53F7\u5206\u9694\uFF09\uFF0C\u5982 overview,keywords,ads\uFF1B\u7701\u7565=\u5168\u90E8 25 \u4E2A"
103906
103972
  ).option(
@@ -103971,20 +104037,21 @@ ${fixHint}
103971
104037
  );
103972
104038
  process.exit(1);
103973
104039
  }
103974
- const ids = parseAccountIdsForAnalysis(opts.account ?? "");
104040
+ const normalized = normalizeAnalysisDateOpts(opts);
104041
+ const ids = parseAccountIdsForAnalysis(normalized.account ?? "");
103975
104042
  if (ids.length === 0) {
103976
104043
  console.error("\n\u274C --account \u4E0D\u80FD\u4E3A\u7A7A\u3002\n");
103977
104044
  process.exit(1);
103978
104045
  }
103979
104046
  if (ids.length >= 2) {
103980
- if (!opts.jsonOut?.trim()) {
104047
+ if (!normalized.jsonOut?.trim()) {
103981
104048
  console.error("\n\u274C \u591A\u8D26\u6237\u6A21\u5F0F\u5FC5\u987B\u4F20 --json-out <\u76EE\u5F55>\uFF08\u6309 runId \u5B50\u76EE\u5F55\u7EC4\u7EC7\u4EA7\u7269\uFF09\u3002\n");
103982
104049
  process.exit(1);
103983
104050
  }
103984
- await runMultiAccountViaBatch(ids, opts);
104051
+ await runMultiAccountViaBatch(ids, normalized);
103985
104052
  return;
103986
104053
  }
103987
- await runAllSections({ ...opts, account: ids[0] });
104054
+ await runAllSections({ ...normalized, account: ids[0] });
103988
104055
  });
103989
104056
  }
103990
104057
  function registerGoogleAnalysisCommands(program2) {
@@ -106606,6 +106673,37 @@ async function fetchAllAccountPages(media, pageSize, maxPages, config, verbose)
106606
106673
  return { items: allItems, total };
106607
106674
  }
106608
106675
 
106676
+ // src/commands/balance-scan/data-health.ts
106677
+ function assessBalanceScanDataHealth(input) {
106678
+ const { scannedAccounts, validAccounts, balances } = input;
106679
+ if (scannedAccounts <= 0) {
106680
+ return {
106681
+ code: "no_accounts",
106682
+ message: "\u672A\u626B\u5230\u4EFB\u4F55\u8D26\u6237\uFF1A\u8BF7\u786E\u8BA4 -m \u5A92\u4F53\u7C7B\u578B\uFF0C\u6216\u7528 -a \u6307\u5B9A mediaCustomerId\uFF1B\u5168\u91CF\u6A21\u5F0F\u68C0\u67E5\u767B\u5F55\u4E0E list-accounts \u662F\u5426\u6709\u6570\u636E"
106683
+ };
106684
+ }
106685
+ if (validAccounts <= 0) {
106686
+ return {
106687
+ code: "all_oauth_invalid",
106688
+ message: "\u8D26\u6237\u5747\u56E0 OAuth \u5931\u6548\u88AB\u8DF3\u8FC7\uFF1A\u8BF7\u5728\u4E1D\u8DEF\u8D5E\u91CD\u65B0\u6388\u6743\u540E\u518D balance-scan\uFF1B\u6216\u7528 -a \u6307\u5B9A\u4ECD\u6709\u6548\u7684\u8D26\u6237"
106689
+ };
106690
+ }
106691
+ if (balances.length === 0) {
106692
+ return {
106693
+ code: "all_balances_null",
106694
+ message: "\u672A\u62C9\u5230\u4EFB\u4F55\u4F59\u989D\u884C\uFF1A\u8BF7\u52A0 --verbose \u67E5\u770B\u63A5\u53E3\u9519\u8BEF\uFF1B\u53EF\u8BD5 --refresh-dp \u6216 siluzan-tso login \u540E\u91CD\u8BD5"
106695
+ };
106696
+ }
106697
+ const allNull = balances.every((b) => b == null);
106698
+ if (allNull) {
106699
+ return {
106700
+ code: "all_balances_null",
106701
+ message: "\u5168\u90E8\u8D26\u6237\u4F59\u989D\u4E3A null\uFF08\u63A5\u53E3\u672A\u8FD4\u56DE\u53EF\u7528\u4F59\u989D\uFF09\uFF1A\u65E0\u6CD5\u8BA1\u7B97\u7EED\u822A\u3002\u8BF7\u68C0\u67E5\u5A92\u4F53\u6388\u6743/\u8D26\u6237\u72B6\u6001\uFF0C\u52A0 --verbose \u770B\u539F\u59CB\u54CD\u5E94\uFF0C\u6216 --refresh-dp / \u91CD\u65B0 login \u540E\u91CD\u8BD5"
106702
+ };
106703
+ }
106704
+ return null;
106705
+ }
106706
+
106609
106707
  // src/commands/balance-scan/run.ts
106610
106708
  async function runBalanceScan(opts) {
106611
106709
  let config = loadConfig(opts.token);
@@ -106750,6 +106848,12 @@ async function runBalanceScan(opts) {
106750
106848
  const bd = b.remainingDays ?? Number.POSITIVE_INFINITY;
106751
106849
  return ad - bd;
106752
106850
  });
106851
+ const balancesForHealth = validIds.map((id) => balanceMap.get(id)?.remainingAccountBudget);
106852
+ const dataIssue = assessBalanceScanDataHealth({
106853
+ scannedAccounts: allItems.length,
106854
+ validAccounts: validIds.length,
106855
+ balances: balancesForHealth
106856
+ });
106753
106857
  const meta = {
106754
106858
  media,
106755
106859
  source: isSubset ? "subset" : "list",
@@ -106757,6 +106861,9 @@ async function runBalanceScan(opts) {
106757
106861
  validAccounts: validIds.length,
106758
106862
  skippedInvalidOAuth: allItems.length - validIds.length,
106759
106863
  hitCount: evaluated.length,
106864
+ nullBalanceCount: balancesForHealth.filter((b) => b == null).length,
106865
+ dataIssue: dataIssue?.code ?? null,
106866
+ dataIssueMessage: dataIssue?.message ?? null,
106760
106867
  thresholds: {
106761
106868
  days: thresholdDays,
106762
106869
  minBalance,
@@ -106769,14 +106876,24 @@ async function runBalanceScan(opts) {
106769
106876
  totalReported: total ?? null,
106770
106877
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
106771
106878
  };
106772
- const scanPayload = { ok: true, data: { items: evaluated }, meta };
106773
- if (await emitCliJsonOrSnapshot(opts, {
106879
+ const scanPayload = { ok: !dataIssue, data: { items: evaluated }, meta };
106880
+ const wroteJson = await emitCliJsonOrSnapshot(opts, {
106774
106881
  section: `balance-scan-${media}`,
106775
106882
  commandLabel: "balance-scan",
106776
106883
  commandHint: media,
106777
106884
  payload: scanPayload,
106778
106885
  idSuffix: media
106779
- })) {
106886
+ });
106887
+ if (dataIssue) {
106888
+ console.error(`
106889
+ \u274C balance-scan \u6570\u636E\u5F02\u5E38\uFF08${dataIssue.code}\uFF09\uFF1A${dataIssue.message}
106890
+ `);
106891
+ if (wroteJson) {
106892
+ console.error(" \u5DF2\u5199\u5165 --json-out\uFF08ok=false\uFF0C\u89C1 meta.dataIssue\uFF09\uFF1B\u8BF7\u4FEE\u590D\u540E\u91CD\u8DD1\uFF0C\u52FF\u5F53\u4F5C\u300C\u65E0\u9884\u8B66\u547D\u4E2D\u300D\u3002\n");
106893
+ }
106894
+ process.exit(2);
106895
+ }
106896
+ if (wroteJson) {
106780
106897
  return;
106781
106898
  }
106782
106899
  if (isSubset) {
@@ -106794,7 +106911,7 @@ ${media} \u8D26\u6237\u4F59\u989D\u626B\u63CF\u5B8C\u6210\uFF1A\u5171\u626B\u63C
106794
106911
  );
106795
106912
  }
106796
106913
  if (evaluated.length === 0) {
106797
- console.log(" \u2705 \u65E0\u547D\u4E2D\u8D26\u6237\u3002\n");
106914
+ console.log(" \u2705 \u65E0\u547D\u4E2D\u8D26\u6237\uFF08\u9608\u503C\u5185\u65E0\u9884\u8B66\uFF1B\u6570\u636E\u5DF2\u6B63\u5E38\u8FD4\u56DE\uFF09\u3002\n");
106798
106915
  return;
106799
106916
  }
106800
106917
  const cols = [
@@ -116225,6 +116342,7 @@ function runPmaxCreateValidation(cfg) {
116225
116342
  }
116226
116343
 
116227
116344
  // src/commands/ad/pmax-image-validate.ts
116345
+ import { existsSync as existsSync3 } from "fs";
116228
116346
  import { isAbsolute as isAbsolute2, resolve as resolve6, dirname as dirname7 } from "path";
116229
116347
 
116230
116348
  // src/commands/ad/pmax-image-dims.ts
@@ -116384,6 +116502,12 @@ async function runPmaxImageValidation(configFile, cfg) {
116384
116502
  fileSizeKB = Math.ceil(b64.trim().length * 0.75 / 1024);
116385
116503
  } else if (pathRaw?.trim()) {
116386
116504
  const abs = resolveImagePath(configFile, pathRaw.trim());
116505
+ if (!existsSync3(abs)) {
116506
+ errors.push(
116507
+ `${spec.label}\uFF1A\u56FE\u7247\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${abs}\uFF08\u76F8\u5BF9\u8DEF\u5F84\u76F8\u5BF9 --config-file \u6240\u5728\u76EE\u5F55\u89E3\u6790\uFF1B\u8BF7\u6539\u7528\u7EDD\u5BF9\u8DEF\u5F84\u6216\u628A\u56FE\u653E\u5230\u914D\u7F6E\u65C1\uFF09`
116508
+ );
116509
+ continue;
116510
+ }
116387
116511
  const result = readImageDimsFromPath(abs);
116388
116512
  if (result) {
116389
116513
  dims = result;
@@ -116407,7 +116531,7 @@ async function runPmaxImageValidation(configFile, cfg) {
116407
116531
  // src/commands/ad/pmax-image-resolve.ts
116408
116532
  init_auth();
116409
116533
  import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, resolve as resolve7 } from "path";
116410
- import { readFileSync as readFileSync10 } from "fs";
116534
+ import { existsSync as existsSync4, readFileSync as readFileSync10 } from "fs";
116411
116535
 
116412
116536
  // src/commands/ad/pmax-urls.ts
116413
116537
  function pmaxChannelTypesUrl(googleApiUrl) {
@@ -116570,7 +116694,19 @@ async function uploadPmaxImageFileBase64(config, googleApiUrl, accountId, absIma
116570
116694
  async function uploadPmaxImageFile(config, googleApiUrl, accountId, absImagePath, name2, verbose) {
116571
116695
  const fileName = basename4(absImagePath);
116572
116696
  const assetName = (name2 ?? fileName).trim() || fileName;
116573
- const fileBuffer = readFileSync10(absImagePath);
116697
+ if (!existsSync4(absImagePath)) {
116698
+ throw new Error(
116699
+ `\u56FE\u7247\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${absImagePath}\uFF08\u76F8\u5BF9\u8DEF\u5F84\u76F8\u5BF9 --config-file \u6240\u5728\u76EE\u5F55\uFF1B\u8BF7\u6838\u5BF9 imagePaths \u6216\u6539\u7528\u7EDD\u5BF9\u8DEF\u5F84\uFF09`
116700
+ );
116701
+ }
116702
+ let fileBuffer;
116703
+ try {
116704
+ fileBuffer = readFileSync10(absImagePath);
116705
+ } catch (err) {
116706
+ throw new Error(
116707
+ `\u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF08${absImagePath}\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`
116708
+ );
116709
+ }
116574
116710
  if (fileBuffer.length <= MAX_BASE64_UPLOAD_BYTES) {
116575
116711
  return uploadPmaxImageFileBase64(
116576
116712
  config,
@@ -117345,7 +117481,7 @@ function buildPmaxCreateUrl(googleApiUrl, accountId) {
117345
117481
  }
117346
117482
 
117347
117483
  // src/commands/ad/pmax-video-upload.ts
117348
- import { existsSync as existsSync3, readFileSync as readFileSync13 } from "fs";
117484
+ import { existsSync as existsSync5, readFileSync as readFileSync13 } from "fs";
117349
117485
  import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute5, resolve as resolve9 } from "path";
117350
117486
 
117351
117487
  // src/commands/ad/pmax-shared.ts
@@ -117552,7 +117688,7 @@ function runPmaxVideoValidation(configFile, cfg) {
117552
117688
  );
117553
117689
  }
117554
117690
  const abs = resolveVideoPath(configFile, videoPath);
117555
- if (!existsSync3(abs)) {
117691
+ if (!existsSync5(abs)) {
117556
117692
  errors.push(`videoPath \u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${abs}`);
117557
117693
  return { errors, warnings };
117558
117694
  }
@@ -117674,7 +117810,7 @@ function validatePmaxVideoPathQuick(absPath) {
117674
117810
  if (!VIDEO_UPLOAD_SUFFIX.test(absPath)) {
117675
117811
  return `videoPath \u6269\u5C55\u540D\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u4F7F\u7528 .mp4 / .mov / .webm / .avi / .mpeg / .mpg`;
117676
117812
  }
117677
- if (!existsSync3(absPath)) {
117813
+ if (!existsSync5(absPath)) {
117678
117814
  return `\u89C6\u9891\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${absPath}`;
117679
117815
  }
117680
117816
  try {
@@ -117744,6 +117880,45 @@ async function linkPmaxVideoAfterCreate(opts) {
117744
117880
  }
117745
117881
  }
117746
117882
 
117883
+ // src/commands/ad/pmax-create-http-error.ts
117884
+ function classifyPmaxCreateHttpError(raw) {
117885
+ const msg = raw.trim() || "\u672A\u77E5\u9519\u8BEF";
117886
+ const lower = msg.toLowerCase();
117887
+ const hints = [];
117888
+ if (/^HTTP 400\b/i.test(msg) || lower.includes("bad request")) {
117889
+ if (/budget|日预算|budgetamount|micros/i.test(msg)) {
117890
+ hints.push("\u68C0\u67E5 campaign.budget / \u65E5\u9884\u7B97\u91D1\u989D\u4E0E\u5E01\u79CD\u662F\u5426\u5408\u7406");
117891
+ }
117892
+ if (/bidding|target.?cpa|target.?roas|maximize|出价/i.test(msg)) {
117893
+ hints.push("\u68C0\u67E5 BiddingStrategyTypeV2 \u4E0E\u914D\u5957\u91D1\u989D\u5B57\u6BB5\uFF08tCPA/tROAS\uFF09\u662F\u5426\u9F50\u5168\u4E14\u517C\u5BB9");
117894
+ }
117895
+ if (/image|asset|marketing|logo|图片/i.test(msg)) {
117896
+ hints.push("\u68C0\u67E5\u6A2A\u56FE/\u65B9\u56FE/Logo \u662F\u5426\u5DF2\u4E0A\u4F20\u6210\u529F\u3001\u89C4\u683C\u662F\u5426\u8FBE\u6807\uFF08\u5148\u8DD1 ad pmax-validate\uFF09");
117897
+ }
117898
+ if (/final.?url|landing|落地页|url/i.test(msg)) {
117899
+ hints.push("\u68C0\u67E5 finalUrls \u662F\u5426\u4E3A\u53EF\u8BBF\u95EE\u7684 http(s) \u843D\u5730\u9875");
117900
+ }
117901
+ if (/name|duplicate|已存在|同名/i.test(msg)) {
117902
+ hints.push("\u7CFB\u5217\u540D\u53EF\u80FD\u91CD\u590D\uFF1B\u6362\u4E00\u4E2A name \u518D\u8BD5");
117903
+ }
117904
+ if (/geo|location|language|地域|语言/i.test(msg)) {
117905
+ hints.push("\u68C0\u67E5 geo / language id \u662F\u5426\u6765\u81EA ad geo resolve / \u6587\u6863\u679A\u4E3E");
117906
+ }
117907
+ if (hints.length === 0) {
117908
+ hints.push(
117909
+ "\u5E38\u89C1\uFF1A\u9884\u7B97/\u51FA\u4EF7\u4E0D\u517C\u5BB9\u3001\u56FE\u7247\u8D44\u4EA7\u65E0\u6548\u3001finalUrl \u65E0\u6548\u3001\u7CFB\u5217\u91CD\u540D\uFF1B\u5148 ad pmax-validate\uFF0C\u4ECD\u5931\u8D25\u52A0 --verbose \u770B\u7F51\u5173\u539F\u6587"
117910
+ );
117911
+ }
117912
+ } else if (/^HTTP 403\b/i.test(msg)) {
117913
+ hints.push("\u8D26\u6237\u53EF\u80FD\u65E0\u6743\u521B\u5EFA\uFF1B\u5148 account check-access -a <id>\uFF0C\u786E\u8BA4\u5728\u5F53\u524D\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u4E0B");
117914
+ } else if (/^HTTP 401\b/i.test(msg)) {
117915
+ hints.push("\u9274\u6743\u5931\u6548\uFF1B\u6267\u884C siluzan-tso login \u540E\u91CD\u8BD5");
117916
+ }
117917
+ if (hints.length === 0) return msg;
117918
+ return `${msg}
117919
+ \u63D0\u793A\uFF1A${hints.join("\uFF1B")}`;
117920
+ }
117921
+
117747
117922
  // src/commands/ad/pmax-create.ts
117748
117923
  function printVideoLinkRecoveryHint(accountId, assetGroupId, campaignId, youtubeTarget) {
117749
117924
  const cid = campaignId != null ? ` --campaign-id ${campaignId}` : "";
@@ -117831,11 +118006,10 @@ async function runAdPmaxCreate(opts) {
117831
118006
  opts.verbose
117832
118007
  );
117833
118008
  } catch (err) {
117834
- console.error(
117835
- `
117836
- \u274C \u521B\u5EFA PMax \u5E7F\u544A\u7CFB\u5217\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}
117837
- `
117838
- );
118009
+ const raw = err instanceof Error ? err.message : String(err);
118010
+ console.error(`
118011
+ \u274C \u521B\u5EFA PMax \u5E7F\u544A\u7CFB\u5217\u5931\u8D25\uFF1A${classifyPmaxCreateHttpError(raw)}
118012
+ `);
117839
118013
  process.exit(1);
117840
118014
  }
117841
118015
  const campaignId = data["campaignId"] ?? data["campaign_id"];
@@ -119081,7 +119255,7 @@ async function runAdCampaignValidate(opts) {
119081
119255
  }
119082
119256
 
119083
119257
  // src/commands/ad/pmax-image-convert.ts
119084
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5, readFileSync as readFileSync14, existsSync as existsSync4 } from "fs";
119258
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5, readFileSync as readFileSync14, existsSync as existsSync6 } from "fs";
119085
119259
  import { resolve as resolve10, dirname as dirname11, basename as basename7, extname } from "path";
119086
119260
  var SPECS = [
119087
119261
  { kind: "marketing", width: 1200, height: 628, fit: "cover", suffix: "_marketing" },
@@ -119142,7 +119316,7 @@ async function runAdPmaxImageConvert(opts) {
119142
119316
  continue;
119143
119317
  }
119144
119318
  const absInput = resolve10(src);
119145
- if (!existsSync4(absInput)) {
119319
+ if (!existsSync6(absInput)) {
119146
119320
  console.error(`\u274C \u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${absInput}`);
119147
119321
  hasError = true;
119148
119322
  continue;
@@ -122906,6 +123080,31 @@ async function runAccountBmBind(opts) {
122906
123080
  // src/commands/account-manage-register.ts
122907
123081
  function register23(program2) {
122908
123082
  const accountCmd = program2.command("account").description("\u5E7F\u544A\u8D26\u6237\u7BA1\u7406\uFF1AOAuth \u6DFB\u52A0\u6388\u6743\u3001\u89E3\u9664\u5173\u8054\uFF08\u89E3\u7ED1\uFF09\u3001Google MCC \u7ED1\u5B9A/\u89E3\u7ED1\u3001\u8D26\u53F7\u5206\u4EAB");
123083
+ accountCmd.command("list", { hidden: true }).description("\uFF08\u517C\u5BB9\u522B\u540D\uFF09\u540C list-accounts").option(
123084
+ "-m, --media <type>",
123085
+ "\u5A92\u4F53\u7C7B\u578B\uFF1AGoogle | TikTok | Yandex | MetaAd | BingV2 | Kwai\uFF08\u7559\u7A7A\u5219\u67E5\u8BE2\u5168\u90E8\u5A92\u4F53\uFF09"
123086
+ ).option("-k, --keyword <text>", "\u5173\u952E\u5B57\uFF08\u516C\u53F8/\u8D26\u6237\u540D/\u8D26\u6237ID \u6A21\u7CCA\u5339\u914D\uFF09").option(
123087
+ "-s, --status <status>",
123088
+ "\u8D26\u6237\u72B6\u6001\uFF1Anormal\uFF08\u6B63\u5E38\uFF09| invalid\uFF08\u5931\u6548\uFF09| all\uFF08\u5168\u90E8\uFF0C\u9ED8\u8BA4\uFF09",
123089
+ "all"
123090
+ ).option("-p, --page <n>", "\u9875\u7801\uFF08\u9ED8\u8BA4 1\uFF09", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF\uFF08\u9ED8\u8BA4 20\uFF09", parseInt).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option("--json-out <path>", "\u843D\u76D8\u76EE\u5F55\u6216 *.json\uFF1Bstdout \u4E00\u884C\u6458\u8981", void 0).option("--refresh-dp", "\u5F3A\u5236\u91CD\u62C9 Datapermission", false).option("--verbose", "\u663E\u793A\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(
123091
+ async (opts) => {
123092
+ process.stderr.write(
123093
+ "\u2139 `account list` \u5DF2\u8F6C\u53D1\u4E3A `list-accounts`\uFF08\u8BF7\u4F18\u5148\u4F7F\u7528 list-accounts\uFF09\u3002\n"
123094
+ );
123095
+ await runListAccounts({
123096
+ token: opts.token,
123097
+ media: opts.media,
123098
+ keyword: opts.keyword,
123099
+ status: opts.status,
123100
+ page: opts.page,
123101
+ pageSize: opts.pageSize,
123102
+ jsonOut: opts.jsonOut,
123103
+ refreshDp: opts.refreshDp,
123104
+ verbose: opts.verbose
123105
+ });
123106
+ }
123107
+ );
122909
123108
  accountCmd.command("me").description(
122910
123109
  "\u67E5\u8BE2\u5F53\u524D\u767B\u5F55\u4E1D\u8DEF\u8D5E\u8D26\u53F7\u4FE1\u606F\uFF08GET /query/account/me\uFF09\uFF1B--check-phone \u7528\u4E8E\u6821\u9A8C\u7528\u6237\u6307\u5B9A\u7684\u4F01\u4E1A\u7BA1\u5BB6\u624B\u673A\u53F7"
122911
123110
  ).option("-t, --token <token>", "Auth Token").option(
@@ -131954,6 +132153,34 @@ function registerTikTokAnalysisCommands(program2) {
131954
132153
  // src/index.ts
131955
132154
  init_version();
131956
132155
  init_cli_json_snapshot();
132156
+
132157
+ // src/utils/agent-compat-argv.ts
132158
+ function agentCompatFatalMessage(argv) {
132159
+ const args = argv.slice(2);
132160
+ if (args.includes("--period") || args.some((a) => a.startsWith("--period="))) {
132161
+ return "\n\u274C \u6CA1\u6709 --period \u9009\u9879\u3002\u8BF7\u7528 --start/--end\uFF08\u6216\u517C\u5BB9\u522B\u540D --start-date/--end-date\uFF09\u6307\u5B9A YYYY-MM-DD \u533A\u95F4\u3002\n";
132162
+ }
132163
+ const cmd = args[0];
132164
+ if (cmd === "google-ads") {
132165
+ return "\n\u274C \u6CA1\u6709 `google-ads` \u547D\u540D\u7A7A\u95F4\u3002\u8BF7\u6539\u7528\uFF1A\n - \u8D26\u6237\u5206\u6790\u62C9\u6570\uFF1Asiluzan-tso google-analysis \u2026\n - \u5E7F\u544A\u7CFB\u5217/\u7EC4/\u5173\u952E\u8BCD CRUD\uFF1Asiluzan-tso ad \u2026\n - \u5E7F\u544A\u8BCA\u65AD\u62A5\u544A\uFF1Asiluzan-tso google-ads-diagnosis \u2026\n";
132166
+ }
132167
+ if (cmd === "list" && (args.length === 1 || args[1]?.startsWith("-"))) {
132168
+ return "\n\u274C \u6CA1\u6709\u9876\u5C42 `list` \u547D\u4EE4\u3002\u67E5\u8BE2\u5E7F\u544A\u8D26\u6237\u5217\u8868\u8BF7\u7528\uFF1A\n siluzan-tso list-accounts [-m Google] [-k <id\u6216\u5173\u952E\u5B57>] --json-out <dir>\n";
132169
+ }
132170
+ if (cmd === "accounts") {
132171
+ return "\n\u274C \u6CA1\u6709 `accounts` \u547D\u540D\u7A7A\u95F4\u3002\u67E5\u8BE2\u8D26\u6237\u5217\u8868\u8BF7\u7528\uFF1A\n siluzan-tso list-accounts \u2026\n \u591A\u8D26\u6237\u753B\u50CF\uFF1Asiluzan-tso accounts-digest \u2026\n";
132172
+ }
132173
+ return null;
132174
+ }
132175
+ function interceptAgentCompatArgv(argv) {
132176
+ const msg = agentCompatFatalMessage(argv);
132177
+ if (msg) {
132178
+ console.error(msg);
132179
+ process.exit(1);
132180
+ }
132181
+ }
132182
+
132183
+ // src/index.ts
131957
132184
  installProcessHandlers();
131958
132185
  function getVersion() {
131959
132186
  try {
@@ -132036,4 +132263,5 @@ function argvHasDeprecatedJsonFlag(argv) {
132036
132263
  if (argvHasDeprecatedJsonFlag(process.argv)) {
132037
132264
  rejectDeprecatedJsonFlag(true, process.argv[2] ?? "siluzan-tso");
132038
132265
  }
132266
+ interceptAgentCompatArgv(process.argv);
132039
132267
  program.parseAsync();
@@ -1,28 +1,35 @@
1
- # siluzan-tso — Agent 文档目录
1
+ # siluzan-tso — 文档目录
2
2
 
3
- > 运行时入口:`SKILL.md` → 按路由表 Read **leaf** 文档。维护时对齐 `references/core/skill-authoring.md`。
3
+ > 入口:`SKILL.md` → 按路由表 Read 列出的文件。改文档结构时对齐 `references/core/skill-authoring.md`。
4
+
5
+ ## 怎么找文档
6
+
7
+ | 要做什么 | 读哪里 |
8
+ | -------- | ------ |
9
+ | 锁定工作流 | `SKILL.md` 路由表 |
10
+ | 报告类话术模糊 | `references/core/intent-routing.md` |
11
+ | 步骤与产物 | `playbooks.md`(P*)或 `workflows.md`(W*) |
12
+ | 命令参数 / 字段 | 路由表「必读文档」 |
13
+ | 纪律与对用户怎么说 | `references/core/agent-conventions.md` |
14
+ | 报告结构纲要 | `report-templates/*.md`(勿读 `*.html`) |
4
15
 
5
16
  ## 启动顺序
6
17
 
7
18
  1. Read `SKILL.md` 路由表,锁定唯一工作流
8
- 2. 报告类模糊 → Read `references/core/intent-routing.md`
9
- 3. Read 路由行「必读文档」+ 对应 `playbooks.md` / `workflows.md` 卡片(**直接读 leaf**,不必先经 `accounts.md` 索引)
10
- 4. 写操作 / 报告交付 / 批量拉数 → Read `references/core/agent-conventions.md` 相关节
11
- 5. P5/P6/P7 或长 CLI:可选 `references/core/subagent-orchestration.md`
12
-
13
- ## 文档域
14
-
15
- | 域 | 路径 | 何时 Read |
16
- | -- | ---- | --------- |
17
- | Core | `references/core/` | 纪律、intent-routing、playbooks、workflows |
18
- | Accounts | `references/accounts/` | `accounts-list` / `accounts-balance-stats` / `accounts-permissions`(`accounts.md` 仅索引) |
19
- | Google Ads | `references/google-ads/` | `google-ads-read` / `write` / `batch`(索引:`google-ads.md`);`rules/` 先 README 再单文件 |
20
- | Analytics | `references/analytics/` | 拉数、批处理、拓词、RAG |
21
- | Operations | `references/operations/` | 预警、线索;自动化仅用户问起时 |
22
- | Templates | `report-templates/*.md`、`assets/` | 报告纲要 / JSON 契约;**禁止** Read `*.html` |
19
+ 2. 报告类模糊 → Read `intent-routing.md`
20
+ 3. Read「必读文档」+ 对应 playbook/workflow 卡片
21
+ 4. 写操作 / 报告交付 / 批量 / 对用户写结论 → Read `agent-conventions.md` 相关节
22
+ 5. P5/P6/P7 或长 CLI:可选 `subagent-orchestration.md`
23
23
 
24
- ## 维护
24
+ ## 按域
25
+
26
+ | 域 | 路径 | 说明 |
27
+ | -- | ---- | ---- |
28
+ | Core | `references/core/` | conventions、intent-routing、playbooks、workflows |
29
+ | Accounts | `references/accounts/` | 直接读 `accounts-list` / `accounts-balance-stats` / `accounts-permissions` |
30
+ | Google Ads | `references/google-ads/` | `google-ads-read` / `write` / `batch`;`rules/` 先 README 再单文件 |
31
+ | Analytics | `references/analytics/` | 拉数、批处理、拓词 |
32
+ | Operations | `references/operations/` | 预警、线索;自动化仅用户问起时 |
33
+ | Templates | `report-templates/*.md`、`assets/` | 报告纲要 / JSON 契约 |
25
34
 
26
- - 源码:`tso-cli/assets/siluzan-ads/`
27
- - 纪律真相源:`references/core/agent-conventions.md`
28
- - 规范:`references/core/skill-authoring.md`
35
+ 源码目录:`tso-cli/assets/siluzan-ads/`。