siluzan-tso-cli 1.1.22-beta.2 → 1.1.22-beta.3

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 (121) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +139 -30
  3. package/dist/skill/AGENTS.md +45 -0
  4. package/dist/skill/SKILL.md +60 -347
  5. package/dist/skill/_meta.json +2 -2
  6. package/dist/skill/assets/campaign-create-template.md +4 -4
  7. package/dist/skill/assets/pmax-create-template.md +4 -3
  8. package/dist/skill/docs/skill-guide.md +44 -0
  9. package/dist/skill/references/README.md +73 -0
  10. package/dist/skill/references/{accounts.md → accounts/accounts.md} +7 -7
  11. package/dist/skill/references/{currency.md → accounts/currency.md} +7 -7
  12. package/dist/skill/references/accounts/open-account-by-media.md +151 -0
  13. package/dist/skill/references/{open-account-google-ui.md → accounts/open-account-google-ui.md} +2 -2
  14. package/dist/skill/references/{account-analytics.md → analytics/account-analytics.md} +2 -2
  15. package/dist/skill/references/{google-analysis-batch.md → analytics/google-analysis-batch.md} +2 -2
  16. package/dist/skill/references/{keyword-planner-workflows.md → analytics/keyword-planner-workflows.md} +9 -9
  17. package/dist/skill/references/{rag.md → analytics/rag.md} +5 -5
  18. package/dist/skill/references/{reporting.md → analytics/reporting.md} +4 -4
  19. package/dist/skill/references/core/agent-conventions.md +180 -0
  20. package/dist/skill/references/core/playbooks.md +141 -0
  21. package/dist/skill/references/core/skill-authoring.md +192 -0
  22. package/dist/skill/references/{tips.md → core/tips.md} +5 -5
  23. package/dist/skill/references/{workflows.md → core/workflows.md} +23 -25
  24. package/dist/skill/references/{google-ads-campaign-plan.md → google-ads/google-ads-campaign-plan.md} +22 -22
  25. package/dist/skill/references/{google-ads.md → google-ads/google-ads.md} +11 -11
  26. package/dist/skill/references/google-ads/rules/README.md +21 -0
  27. package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-keyword-taxonomy.md +2 -2
  28. package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-launch-plan-template.md +8 -8
  29. package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-pmax-guide.md +1 -1
  30. package/dist/skill/references/{tso-home.md → misc/tso-home.md} +4 -4
  31. package/dist/skill/references/{hosted-automation-monitoring-json.md → operations/hosted-automation-monitoring-json.md} +8 -8
  32. package/dist/skill/references/{hosted-automation-optimize-ab-winner.md → operations/hosted-automation-optimize-ab-winner.md} +2 -2
  33. package/dist/skill/references/{hosted-automation-optimize-index.md → operations/hosted-automation-optimize-index.md} +6 -6
  34. package/dist/skill/references/{hosted-automation-optimize-scale.md → operations/hosted-automation-optimize-scale.md} +2 -2
  35. package/dist/skill/references/{hosted-automation-optimize-weak-downbid.md → operations/hosted-automation-optimize-weak-downbid.md} +5 -5
  36. package/dist/skill/references/operations/hosted-automation-scenarios.md +23 -0
  37. package/dist/skill/references/{hosted-automation-self-control.md → operations/hosted-automation-self-control.md} +14 -14
  38. package/{assets/siluzan-ads/references → dist/skill/references/operations}/hosted-automation-user-catalog.md +12 -12
  39. package/dist/skill/report-templates/README.md +2 -1
  40. package/dist/skill/report-templates/REPORT-WORKFLOW.md +2 -2
  41. package/dist/skill/report-templates/google-ads-diagnosis.md +1 -1
  42. package/dist/skill/report-templates/google-inquiry-analysis.md +6 -6
  43. package/dist/skill/report-templates/google-period-report.md +1 -1
  44. package/dist/skill/report-templates/okki-weekly-google-client.md +4 -4
  45. package/dist/skill/scripts/install.ps1 +1 -1
  46. package/dist/skill/scripts/install.sh +1 -1
  47. package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +2 -2
  48. package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +3 -3
  49. package/eval/cases/accounts-single-balance-not-bulk.scenario.json +2 -2
  50. package/eval/cases/clue-meta-leads-json.scenario.json +2 -2
  51. package/eval/cases/clue-tiktok-leads-json.scenario.json +2 -2
  52. package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +1 -1
  53. package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +1 -1
  54. package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +1 -1
  55. package/eval/cases/finance-invoice-info-list.scenario.json +2 -2
  56. package/eval/cases/forewarning-list-google.scenario.json +2 -2
  57. package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +3 -3
  58. package/eval/cases/google-analysis-keywords-route.scenario.json +2 -2
  59. package/eval/cases/hosted-sop-cpa-spike-downbid.scenario.json +5 -5
  60. package/eval/cases/hosted-sop-daily-budget-circuit-breaker.scenario.json +3 -3
  61. package/eval/cases/hosted-sop-empty-spend-pause-p1.scenario.json +5 -5
  62. package/eval/cases/meta-single-balance-not-bulk.scenario.json +2 -2
  63. package/eval/cases/open-account-bing-noninteractive.scenario.json +3 -3
  64. package/eval/cases/open-account-google-noninteractive.scenario.json +2 -2
  65. package/eval/cases/open-account-tiktok-license-file.scenario.json +2 -2
  66. package/eval/cases/optimize-list-by-account.scenario.json +2 -2
  67. package/eval/cases/rag-before-keyword-expand.scenario.json +3 -3
  68. package/eval/cases/rag-list-then-query.scenario.json +2 -2
  69. package/eval/cases/report-list-google.scenario.json +2 -2
  70. package/eval/cases/report-push-list-google.scenario.json +3 -3
  71. package/eval/cases/reporting-vs-account-analytics-routing.scenario.json +4 -4
  72. package/eval/cases/setup-login-or-env.scenario.json +2 -2
  73. package/eval/cases/setup-siluzan-data-permission-env.scenario.json +2 -2
  74. package/eval/cases/skill-optimize-vs-google-ads-distinction.scenario.json +4 -4
  75. package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +2 -2
  76. package/eval/cases/tips-json-filtering.scenario.json +4 -4
  77. package/eval/cases/tips-large-json-pagination.scenario.json +3 -3
  78. package/eval/cases/uj-ad-bluetooth-keywords-exclude-cheap-free.scenario.json +1 -1
  79. package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +2 -2
  80. package/eval/cases/uj-ad-keywords-camping-tent-outdoor-plan.scenario.json +1 -1
  81. package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +3 -3
  82. package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +2 -2
  83. package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +1 -1
  84. package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +2 -2
  85. package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +3 -3
  86. package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +2 -2
  87. package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +2 -2
  88. package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +2 -2
  89. package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +2 -2
  90. package/eval/cases/uj-ops-open-google-b2c-usd-shenzhen.scenario.json +3 -3
  91. package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +2 -2
  92. package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +2 -2
  93. package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +3 -3
  94. package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +3 -3
  95. package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +2 -2
  96. package/eval/cases/uj-patrol-google-balances-low.scenario.json +2 -2
  97. package/eval/cases/uj-roi-full-google-account-diagnosis.scenario.json +1 -1
  98. package/eval/cases/uj-roi-keywords-high-cpa-low-cvr-triage.scenario.json +1 -1
  99. package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +2 -2
  100. package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +2 -2
  101. package/package.json +2 -2
  102. package/dist/skill/references/hosted-automation-scenarios.md +0 -23
  103. package/dist/skill/references/hosted-automation-user-catalog.md +0 -38
  104. /package/dist/skill/references/{finance.md → accounts/finance.md} +0 -0
  105. /package/dist/skill/references/{write-audit-restore.md → accounts/write-audit-restore.md} +0 -0
  106. /package/dist/skill/references/{geo-continents.json → analytics/geo-continents.json} +0 -0
  107. /package/dist/skill/references/{setup.md → core/setup.md} +0 -0
  108. /package/dist/skill/references/{pmax-api.md → google-ads/pmax-api.md} +0 -0
  109. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-account-audit.md +0 -0
  110. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-audience-strategy.md +0 -0
  111. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-campaign-optimization.md +0 -0
  112. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-compliance.md +0 -0
  113. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-conversion-architecture.md +0 -0
  114. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-creative-optimization.md +0 -0
  115. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-keyword-optimization.md +0 -0
  116. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-keyword-strategy.md +0 -0
  117. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-landing-page-discovery-via-webfetch.md +0 -0
  118. /package/dist/skill/references/{google-ads-rules → google-ads/rules}/sensitive-industries.md +0 -0
  119. /package/dist/skill/references/{clue.md → operations/clue.md} +0 -0
  120. /package/dist/skill/references/{forewarning.md → operations/forewarning.md} +0 -0
  121. /package/dist/skill/references/{optimize.md → operations/optimize.md} +0 -0
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.22-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.22-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
@@ -69,7 +69,7 @@ siluzan-tso init --force # 强制覆盖已存在文件
69
69
 
70
70
  **若用户已安装 `siluzan-cso` 并完成登录,无需重复操作,直接跳到第 4 步。**
71
71
 
72
- **推荐顺序**:① **手机号 + 验证码**(首选)完整参数与排错见 `references/setup.md`(随 Skill 安装到本地)。
72
+ **推荐顺序**:① **手机号 + 验证码**(首选)完整参数与排错见 `references/core/setup.md`(随 Skill 安装到本地)。
73
73
 
74
74
  ### 方式 A:手机号 + 验证码(**推荐**)
75
75
 
package/dist/index.js CHANGED
@@ -4019,7 +4019,7 @@ var init_cli_json = __esm({
4019
4019
  SCHEMA_VERSION = 1;
4020
4020
  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";
4021
4021
  DEFAULT_FIELD_GUIDE = {
4022
- markdownRefs: ["references/currency.md", "references/tips.md", "references/accounts.md"]
4022
+ markdownRefs: ["references/accounts/currency.md", "references/core/tips.md", "references/accounts/accounts.md"]
4023
4023
  };
4024
4024
  TS_LIKE_ARRAY_SAMPLE_MAX = 8;
4025
4025
  }
@@ -4731,7 +4731,7 @@ var init_google_analysis = __esm({
4731
4731
  CLI_PACKAGE2 = "siluzan-tso-cli";
4732
4732
  SCHEMA_VERSION2 = 2;
4733
4733
  DEFAULT_FIELD_GUIDE2 = {
4734
- markdownRefs: ["references/currency.md", "references/account-analytics.md"],
4734
+ markdownRefs: ["references/accounts/currency.md", "references/analytics/account-analytics.md"],
4735
4735
  tsTypesModule: "tso-cli/src/types/google-analysis-api.ts"
4736
4736
  };
4737
4737
  GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS = [
@@ -105686,7 +105686,7 @@ function reportManifestFile(accountId) {
105686
105686
  return `${applyIdSuffix("report-manifest", accountId)}.json`;
105687
105687
  }
105688
105688
  var DEFAULT_FIELD_GUIDE3 = {
105689
- markdownRefs: ["references/account-analytics.md"]
105689
+ markdownRefs: ["references/analytics/account-analytics.md"]
105690
105690
  };
105691
105691
  function sectionToFilename(section, accountId) {
105692
105692
  if (!/^[a-z0-9_-]+$/.test(section)) {
@@ -108655,7 +108655,7 @@ function register17(program2) {
108655
108655
  const optimizeCmd = program2.command("optimize").description("AI \u5E7F\u544A\u4F18\u5316\uFF08Google\uFF09");
108656
108656
  optimizeCmd.command("list").description("\u67E5\u8BE2\u8D26\u6237\u7EA7\u4F18\u5316\u4E3B\u5217\u8868\uFF08\u5BF9\u5E94\u9875\u9762 /advertising/intelligentOptimization\uFF09").option("-a, --account <id>", "\u8D26\u6237 mediaCustomerId\uFF08\u7559\u7A7A\u67E5\u5168\u90E8\uFF09").option(
108657
108657
  "--match-media-customer-id <id>",
108658
- "\u5728\u5168\u91CF\u5217\u8868\u4E2D\u7B5B\u9009\u8BE5 Google \u5BA2\u6237 ID\uFF08\u81EA\u52A8\u7FFB\u9875\uFF1B\u5DF2\u8131\u7BA1\u52FF\u7528 -a\uFF0C\u89C1 optimize.md\uFF09"
108658
+ "\u5728\u5168\u91CF\u5217\u8868\u4E2D\u7B5B\u9009\u8BE5 Google \u5BA2\u6237 ID\uFF08\u81EA\u52A8\u7FFB\u9875\uFF1B\u5DF2\u8131\u7BA1\u52FF\u7528 -a\uFF0C\u89C1 operations/optimize.md\uFF09"
108659
108659
  ).option("--start <date>", "\u5F00\u59CB\u65E5\u671F YYYY-MM-DD").option("--end <date>", "\u7ED3\u675F\u65E5\u671F YYYY-MM-DD").option("-p, --page <n>", "\u9875\u7801", parseInt).option("--page-size <n>", "\u6BCF\u9875\u6570\u91CF", parseInt).option("-t, --token <token>", "Auth Token").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).option(
108660
108660
  "--json-out <path>",
108661
108661
  "\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF08\u4E0E --json \u4E92\u65A5\uFF09\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",
@@ -109499,12 +109499,16 @@ init_cli_table();
109499
109499
  function pushLengthViolation(violations, item) {
109500
109500
  violations.push({ ...item, excess: item.actual - item.limit });
109501
109501
  }
109502
- function formatLengthViolationsReport(violations) {
109502
+ function lengthViolationAgentHint(revalidateCommand = "campaign-validate") {
109503
+ return `\u52FF\u81EA\u52A8\u622A\u65AD JSON\u3002\u8BFB\u53D6\u843D\u76D8 JSON \u7684 lengthViolations\uFF0C\u5C06\u5168\u90E8\u6761\u76EE\u4E0E\u4FEE\u6539\u65B9\u6848\u5217\u7ED9\u7528\u6237\uFF0C\u786E\u8BA4\u540E\u5199\u5165\u914D\u7F6E\u5E76\u91CD\u65B0 ${revalidateCommand}\u3002`;
109504
+ }
109505
+ function formatLengthViolationsReport(violations, opts) {
109503
109506
  if (violations.length === 0) return "";
109507
+ const cmd = opts?.revalidateCommand ?? "campaign-validate";
109504
109508
  const lines = [
109505
109509
  "",
109506
109510
  `\u{1F4CF} \u8D85\u957F\u5185\u5BB9\u6E05\u5355\uFF08\u5171 ${violations.length} \u9879\uFF09`,
109507
- " \u8BF7\u52FF\u5728 JSON \u4E2D\u81EA\u52A8\u622A\u65AD\uFF1B\u8BF7\u5C06\u5168\u90E8\u6761\u76EE\u4E0E\u4FEE\u6539\u65B9\u6848\u5217\u7ED9\u7528\u6237\uFF0C\u786E\u8BA4\u540E\u518D\u6539 JSON \u5E76\u91CD\u65B0 campaign-validate\u3002",
109511
+ ` \u8BF7\u52FF\u5728 JSON \u4E2D\u81EA\u52A8\u622A\u65AD\uFF1B\u8BF7\u5C06\u5168\u90E8\u6761\u76EE\u4E0E\u4FEE\u6539\u65B9\u6848\u5217\u7ED9\u7528\u6237\uFF0C\u786E\u8BA4\u540E\u518D\u6539 JSON \u5E76\u91CD\u65B0 ${cmd}\u3002`,
109508
109512
  ""
109509
109513
  ];
109510
109514
  for (let i = 0; i < violations.length; i++) {
@@ -113751,9 +113755,6 @@ var BUSINESS_NAME_MAX = 25;
113751
113755
  function pushErr3(errors, msg) {
113752
113756
  errors.push(msg);
113753
113757
  }
113754
- function pushWarn3(warnings, msg) {
113755
- warnings.push(msg);
113756
- }
113757
113758
  function nonEmptyStrings(list) {
113758
113759
  return (list ?? []).map((s) => s?.trim()).filter((s) => Boolean(s));
113759
113760
  }
@@ -113784,20 +113785,28 @@ function normalizeBidding(raw) {
113784
113785
  };
113785
113786
  return byNum[n] ?? null;
113786
113787
  }
113787
- function warnTextLengths(cfg, warnings, prefix, texts, limit, label) {
113788
+ function validateTextLengths(errors, lengthViolations, texts, arrayField, kind, limit, label) {
113788
113789
  texts.forEach((text, i) => {
113789
113790
  const len = calcGoogleCharLength(text);
113790
113791
  if (len > limit) {
113791
- pushWarn3(
113792
- warnings,
113793
- `${prefix}${label}[${i}] \u6709\u6548\u957F\u5EA6 ${len} \u8D85\u8FC7\u5EFA\u8BAE\u4E0A\u9650 ${limit}\uFF08Google \u53EF\u80FD\u62D2\u767B\uFF09`
113794
- );
113792
+ const field = `${arrayField}[${i}]`;
113793
+ pushLengthViolation(lengthViolations, {
113794
+ path: field,
113795
+ field,
113796
+ kind,
113797
+ limit,
113798
+ actual: len,
113799
+ countMode: "google",
113800
+ text
113801
+ });
113802
+ pushErr3(errors, `${label}[${i}] \u8D85\u8FC7 ${limit} \u5B57\u7B26\uFF08\u5F53\u524D ${len}\uFF0CCJK \u8BA1 2\uFF09\uFF1A"${text}"`);
113795
113803
  }
113796
113804
  });
113797
113805
  }
113798
113806
  function runPmaxCreateValidation(cfg) {
113799
113807
  const errors = [];
113800
113808
  const warnings = [];
113809
+ const lengthViolations = [];
113801
113810
  const account = cfg.account?.toString().trim();
113802
113811
  if (!account) {
113803
113812
  pushErr3(errors, "account\uFF08\u5A92\u4F53\u5BA2\u6237 ID\uFF09\u5FC5\u586B");
@@ -113893,19 +113902,53 @@ function runPmaxCreateValidation(cfg) {
113893
113902
  }
113894
113903
  }
113895
113904
  }
113896
- warnTextLengths(cfg, warnings, "", headlines, HEADLINE_MAX, "headlines");
113897
- warnTextLengths(cfg, warnings, "", longHeadlines, LONG_HEADLINE_MAX, "longHeadlines");
113898
- warnTextLengths(cfg, warnings, "", descriptions, DESCRIPTION_MAX, "descriptions");
113905
+ validateTextLengths(
113906
+ errors,
113907
+ lengthViolations,
113908
+ headlines,
113909
+ "headlines",
113910
+ "pmax_headline",
113911
+ HEADLINE_MAX,
113912
+ "headlines"
113913
+ );
113914
+ validateTextLengths(
113915
+ errors,
113916
+ lengthViolations,
113917
+ longHeadlines,
113918
+ "longHeadlines",
113919
+ "pmax_long_headline",
113920
+ LONG_HEADLINE_MAX,
113921
+ "longHeadlines"
113922
+ );
113923
+ validateTextLengths(
113924
+ errors,
113925
+ lengthViolations,
113926
+ descriptions,
113927
+ "descriptions",
113928
+ "pmax_description",
113929
+ DESCRIPTION_MAX,
113930
+ "descriptions"
113931
+ );
113899
113932
  if (cfg.businessName?.trim()) {
113900
- const bn = calcGoogleCharLength(cfg.businessName.trim());
113933
+ const bnText = cfg.businessName.trim();
113934
+ const bn = calcGoogleCharLength(bnText);
113901
113935
  if (bn > BUSINESS_NAME_MAX) {
113902
- pushWarn3(
113903
- warnings,
113904
- `businessName \u6709\u6548\u957F\u5EA6 ${bn} \u8D85\u8FC7\u5EFA\u8BAE\u4E0A\u9650 ${BUSINESS_NAME_MAX}`
113936
+ pushLengthViolation(lengthViolations, {
113937
+ path: "businessName",
113938
+ field: "businessName",
113939
+ kind: "pmax_business_name",
113940
+ limit: BUSINESS_NAME_MAX,
113941
+ actual: bn,
113942
+ countMode: "google",
113943
+ text: bnText
113944
+ });
113945
+ pushErr3(
113946
+ errors,
113947
+ `businessName \u8D85\u8FC7 ${BUSINESS_NAME_MAX} \u5B57\u7B26\uFF08\u5F53\u524D ${bn}\uFF0CCJK \u8BA1 2\uFF09\uFF1A"${bnText}"`
113905
113948
  );
113906
113949
  }
113907
113950
  }
113908
- return { errors, warnings };
113951
+ return { errors, warnings, lengthViolations };
113909
113952
  }
113910
113953
 
113911
113954
  // src/commands/ad/pmax-image-validate.ts
@@ -114082,6 +114125,7 @@ async function runPmaxImageValidation(configFile, cfg) {
114082
114125
  }
114083
114126
 
114084
114127
  // src/commands/ad/pmax-image-resolve.ts
114128
+ init_auth();
114085
114129
  import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, resolve as resolve7 } from "path";
114086
114130
  import { readFileSync as readFileSync5 } from "fs";
114087
114131
 
@@ -114167,6 +114211,7 @@ var SLOT_META = {
114167
114211
  label: "Logo (LOGO)"
114168
114212
  }
114169
114213
  };
114214
+ var MAX_BASE64_UPLOAD_BYTES = 2e5;
114170
114215
  function guessContentType(fileName) {
114171
114216
  const lower = fileName.toLowerCase();
114172
114217
  if (lower.endsWith(".png")) return "image/png";
@@ -114183,10 +114228,8 @@ function normalizePmaxAssetId(value) {
114183
114228
  const m = s.match(/\/assets\/(\d+)\s*$/);
114184
114229
  return m ? m[1] : s;
114185
114230
  }
114186
- async function uploadPmaxImageFile(config, googleApiUrl, accountId, absImagePath, name2, verbose) {
114231
+ async function uploadPmaxImageFileMultipart(config, googleApiUrl, accountId, absImagePath, assetName, fileBuffer, verbose) {
114187
114232
  const fileName = basename4(absImagePath);
114188
- const assetName = (name2 ?? fileName).trim() || fileName;
114189
- const fileBuffer = readFileSync5(absImagePath);
114190
114233
  const mimeType = guessContentType(fileName);
114191
114234
  const form = new FormData();
114192
114235
  form.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
@@ -114215,6 +114258,54 @@ async function uploadPmaxImageFile(config, googleApiUrl, accountId, absImagePath
114215
114258
  }
114216
114259
  return id;
114217
114260
  }
114261
+ async function uploadPmaxImageFileBase64(config, googleApiUrl, accountId, absImagePath, assetName, fileBuffer, verbose) {
114262
+ const url = pmaxImageAssetUrl(googleApiUrl, accountId);
114263
+ let data;
114264
+ try {
114265
+ data = await apiFetch2(
114266
+ url,
114267
+ config,
114268
+ {
114269
+ method: "POST",
114270
+ body: JSON.stringify({ name: assetName, base64String: fileBuffer.toString("base64") })
114271
+ },
114272
+ verbose
114273
+ );
114274
+ } catch (err) {
114275
+ const msg = err instanceof Error ? err.message : String(err);
114276
+ throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7\u5931\u8D25\uFF1A${msg}\uFF08${absImagePath}\uFF09`);
114277
+ }
114278
+ const id = normalizePmaxAssetId(data["id"]);
114279
+ if (!id) {
114280
+ throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7\u672A\u8FD4\u56DE id\uFF08${absImagePath}\uFF09`);
114281
+ }
114282
+ return id;
114283
+ }
114284
+ async function uploadPmaxImageFile(config, googleApiUrl, accountId, absImagePath, name2, verbose) {
114285
+ const fileName = basename4(absImagePath);
114286
+ const assetName = (name2 ?? fileName).trim() || fileName;
114287
+ const fileBuffer = readFileSync5(absImagePath);
114288
+ if (fileBuffer.length <= MAX_BASE64_UPLOAD_BYTES) {
114289
+ return uploadPmaxImageFileBase64(
114290
+ config,
114291
+ googleApiUrl,
114292
+ accountId,
114293
+ absImagePath,
114294
+ assetName,
114295
+ fileBuffer,
114296
+ verbose
114297
+ );
114298
+ }
114299
+ return uploadPmaxImageFileMultipart(
114300
+ config,
114301
+ googleApiUrl,
114302
+ accountId,
114303
+ absImagePath,
114304
+ assetName,
114305
+ fileBuffer,
114306
+ verbose
114307
+ );
114308
+ }
114218
114309
  async function resolveOneSlot(configFile, cfg, kind, accountId, config, googleApiUrl, verbose) {
114219
114310
  const meta = SLOT_META[kind];
114220
114311
  const presetId = String(cfg[meta.configAssetIdKey] ?? "").trim();
@@ -114343,7 +114434,11 @@ function buildPmaxCreateUrl(googleApiUrl, accountId) {
114343
114434
  // src/commands/ad/pmax-create.ts
114344
114435
  async function runAdPmaxCreate(opts) {
114345
114436
  const cfg = loadPmaxCreateConfig(opts.configFile);
114346
- const { errors: cfgErrors, warnings: cfgWarnings } = runPmaxCreateValidation(cfg);
114437
+ const {
114438
+ errors: cfgErrors,
114439
+ warnings: cfgWarnings,
114440
+ lengthViolations: cfgLengthViolations
114441
+ } = runPmaxCreateValidation(cfg);
114347
114442
  const { errors: imgErrors, warnings: imgWarnings } = await runPmaxImageValidation(
114348
114443
  opts.configFile,
114349
114444
  cfg
@@ -114357,6 +114452,10 @@ async function runAdPmaxCreate(opts) {
114357
114452
  if (errors.length > 0) {
114358
114453
  console.error("\n\u274C PMax \u914D\u7F6E\u6821\u9A8C\u5931\u8D25\uFF1A");
114359
114454
  for (const e of errors) console.error(` \u2022 ${e}`);
114455
+ const lengthReport = formatLengthViolationsReport(cfgLengthViolations, {
114456
+ revalidateCommand: "pmax-validate"
114457
+ });
114458
+ if (lengthReport) console.error(lengthReport);
114360
114459
  console.error();
114361
114460
  process.exit(1);
114362
114461
  }
@@ -114432,13 +114531,18 @@ import { writeFileSync as writeFileSync3 } from "fs";
114432
114531
  init_cli_json_snapshot();
114433
114532
  async function runAdPmaxValidate(opts) {
114434
114533
  const cfg = loadPmaxCreateConfig(opts.configFile);
114435
- const { errors: cfgErrors, warnings: cfgWarnings } = runPmaxCreateValidation(cfg);
114534
+ const {
114535
+ errors: cfgErrors,
114536
+ warnings: cfgWarnings,
114537
+ lengthViolations: cfgLengthViolations
114538
+ } = runPmaxCreateValidation(cfg);
114436
114539
  const { errors: imgErrors, warnings: imgWarnings } = await runPmaxImageValidation(
114437
114540
  opts.configFile,
114438
114541
  cfg
114439
114542
  );
114440
114543
  const errors = [...cfgErrors, ...imgErrors];
114441
114544
  const warnings = [...cfgWarnings, ...imgWarnings];
114545
+ const lengthViolations = cfgLengthViolations;
114442
114546
  if (opts.writeNormalized) {
114443
114547
  const toWrite = stripMetaKeysForExport(cfg);
114444
114548
  writeFileSync3(opts.writeNormalized, `${JSON.stringify(toWrite, null, 2)}
@@ -114449,7 +114553,9 @@ async function runAdPmaxValidate(opts) {
114449
114553
  configFile: opts.configFile,
114450
114554
  account: cfg.account?.toString().trim() || void 0,
114451
114555
  errors,
114452
- warnings
114556
+ warnings,
114557
+ lengthViolations,
114558
+ agentHint: lengthViolations.length > 0 ? lengthViolationAgentHint("pmax-validate") : void 0
114453
114559
  };
114454
114560
  const accountSuffix = payload.account;
114455
114561
  if (await emitCliJsonOrSnapshot(opts, {
@@ -114469,6 +114575,10 @@ async function runAdPmaxValidate(opts) {
114469
114575
  if (errors.length > 0) {
114470
114576
  console.error("\n\u274C PMax \u914D\u7F6E\u6821\u9A8C\u5931\u8D25\uFF1A");
114471
114577
  for (const e of errors) console.error(` \u2022 ${e}`);
114578
+ const lengthReport = formatLengthViolationsReport(lengthViolations, {
114579
+ revalidateCommand: "pmax-validate"
114580
+ });
114581
+ if (lengthReport) console.error(lengthReport);
114472
114582
  console.error();
114473
114583
  process.exit(1);
114474
114584
  }
@@ -115283,7 +115393,6 @@ PMax \u5730\u7406\u62A5\u8868\uFF08\u6D3B\u52A8\xD7\u56FD\u5BB6\uFF0C${query.get
115283
115393
  // src/commands/ad/campaign-validate.ts
115284
115394
  import { writeFileSync as writeFileSync4 } from "fs";
115285
115395
  init_cli_json_snapshot();
115286
- var LENGTH_VIOLATION_AGENT_HINT = "\u52FF\u81EA\u52A8\u622A\u65AD JSON\u3002\u8BFB\u53D6\u843D\u76D8 JSON \u7684 lengthViolations\uFF0C\u5C06\u5168\u90E8\u6761\u76EE\u4E0E\u4FEE\u6539\u65B9\u6848\u5217\u7ED9\u7528\u6237\uFF0C\u786E\u8BA4\u540E\u5199\u5165 campaign.json \u5E76\u91CD\u65B0 campaign-validate\u3002";
115287
115396
  async function runAdCampaignValidate(opts) {
115288
115397
  const cfg = loadCampaignCreateConfig(opts.configFile);
115289
115398
  const { errors, warnings, lengthViolations } = runCampaignCreateValidation(cfg);
@@ -115299,7 +115408,7 @@ async function runAdCampaignValidate(opts) {
115299
115408
  errors,
115300
115409
  warnings,
115301
115410
  lengthViolations,
115302
- agentHint: lengthViolations.length > 0 ? LENGTH_VIOLATION_AGENT_HINT : void 0
115411
+ agentHint: lengthViolations.length > 0 ? lengthViolationAgentHint("campaign-validate") : void 0
115303
115412
  };
115304
115413
  const accountSuffix = payload.account || void 0;
115305
115414
  if (await emitCliJsonOrSnapshot(opts, {
@@ -0,0 +1,45 @@
1
+ # siluzan-tso — Agent 文档目录
2
+
3
+ > 单一 Skill 包(`name: siluzan-tso`)。运行时入口:`SKILL.md` → 按域 lazy-load `references/`。
4
+
5
+ ## 启动顺序
6
+
7
+ 1. Read `SKILL.md`(路由 + 任务→文档表)
8
+ 2. **每个新任务** Read `references/core/agent-conventions.md`
9
+ 3. 按任务表 Read 对应域文档后再执行 `siluzan-tso …`
10
+
11
+ ## 文档域(gstack-style domains)
12
+
13
+ | 域 | 路径 | 何时 Read |
14
+ | --- | --- | --- |
15
+ | **Core** | `references/core/` | 纪律、安装、tips、playbooks、workflows |
16
+ | **Accounts** | `references/accounts/` | 列表、余额、开户、财务、审计 |
17
+ | **Google Ads** | `references/google-ads/` | CRUD、PMax、搜索系列流水线;`rules/` 为优化 SOP |
18
+ | **Analytics** | `references/analytics/` | 拉数、批处理、拓词、RAG、TSO 报告 |
19
+ | **Operations** | `references/operations/` | 预警、线索、优化记录、宿主编排自动化 |
20
+ | **Templates** | `assets/`、`report-templates/` | JSON 契约、报告纲要 |
21
+
22
+ ## Playbook(标准动作)
23
+
24
+ | ID | 文档 |
25
+ | --- | --- |
26
+ | P1–P7 | `references/core/playbooks.md` |
27
+ | OKKI 周报 | `report-templates/okki-weekly-google-client.md` |
28
+ | 询盘分析 | `report-templates/google-inquiry-analysis.md` |
29
+
30
+ ## 维护
31
+
32
+ - 结构规范:`references/core/skill-authoring.md`
33
+ - 人类深读:`docs/skill-guide.md`
34
+ - 源码唯一真相:`tso-cli/assets/siluzan-ads/`(勿编辑 `skills/siluzan-tso/` 副本)
35
+
36
+ ## CLI 代码域(与文档对齐)
37
+
38
+ | CLI 模块 | 文档域 |
39
+ | --- | --- |
40
+ | `src/commands/list-accounts/`、`accounts-digest/`、`balance-scan/` | accounts |
41
+ | `src/commands/open-account/`、`account-manage-register.ts` | accounts |
42
+ | `src/commands/ad/` | google-ads |
43
+ | `src/commands/google-analysis/`、`google-analysis-batch.ts` | analytics |
44
+ | `src/commands/report/` | analytics + report-templates |
45
+ | `src/commands/forewarning/`、`optimize/`、`clue.ts` | operations |