siluzan-tso-cli 1.1.21 → 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 +3 -2
  2. package/dist/index.js +140 -58
  3. package/dist/skill/AGENTS.md +45 -0
  4. package/dist/skill/SKILL.md +60 -346
  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/{finance.md → accounts/finance.md} +5 -5
  13. package/dist/skill/references/accounts/open-account-by-media.md +151 -0
  14. package/dist/skill/references/{open-account-google-ui.md → accounts/open-account-google-ui.md} +2 -2
  15. package/dist/skill/references/{account-analytics.md → analytics/account-analytics.md} +2 -2
  16. package/dist/skill/references/{google-analysis-batch.md → analytics/google-analysis-batch.md} +2 -2
  17. package/dist/skill/references/{keyword-planner-workflows.md → analytics/keyword-planner-workflows.md} +9 -9
  18. package/dist/skill/references/{rag.md → analytics/rag.md} +6 -6
  19. package/dist/skill/references/{reporting.md → analytics/reporting.md} +6 -6
  20. package/dist/skill/references/core/agent-conventions.md +180 -0
  21. package/dist/skill/references/core/playbooks.md +141 -0
  22. package/dist/skill/references/{setup.md → core/setup.md} +5 -5
  23. package/dist/skill/references/core/skill-authoring.md +192 -0
  24. package/dist/skill/references/{tips.md → core/tips.md} +5 -5
  25. package/dist/skill/references/{workflows.md → core/workflows.md} +23 -25
  26. package/dist/skill/references/{google-ads-campaign-plan.md → google-ads/google-ads-campaign-plan.md} +22 -22
  27. package/dist/skill/references/{google-ads.md → google-ads/google-ads.md} +11 -15
  28. package/dist/skill/references/{pmax-api.md → google-ads/pmax-api.md} +0 -2
  29. package/dist/skill/references/google-ads/rules/README.md +21 -0
  30. package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-keyword-taxonomy.md +2 -2
  31. package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-launch-plan-template.md +8 -8
  32. package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-pmax-guide.md +1 -1
  33. package/dist/skill/references/{tso-home.md → misc/tso-home.md} +5 -5
  34. package/dist/skill/references/{hosted-automation-monitoring-json.md → operations/hosted-automation-monitoring-json.md} +8 -8
  35. package/dist/skill/references/{hosted-automation-optimize-ab-winner.md → operations/hosted-automation-optimize-ab-winner.md} +2 -2
  36. package/dist/skill/references/{hosted-automation-optimize-index.md → operations/hosted-automation-optimize-index.md} +6 -6
  37. package/dist/skill/references/{hosted-automation-optimize-scale.md → operations/hosted-automation-optimize-scale.md} +2 -2
  38. package/dist/skill/references/{hosted-automation-optimize-weak-downbid.md → operations/hosted-automation-optimize-weak-downbid.md} +5 -5
  39. package/dist/skill/references/operations/hosted-automation-scenarios.md +23 -0
  40. package/dist/skill/references/{hosted-automation-self-control.md → operations/hosted-automation-self-control.md} +14 -14
  41. package/{assets/siluzan-ads/references → dist/skill/references/operations}/hosted-automation-user-catalog.md +12 -12
  42. package/dist/skill/report-templates/README.md +2 -1
  43. package/dist/skill/report-templates/REPORT-WORKFLOW.md +2 -2
  44. package/dist/skill/report-templates/google-ads-diagnosis.md +1 -1
  45. package/dist/skill/report-templates/google-inquiry-analysis.md +6 -6
  46. package/dist/skill/report-templates/google-period-report.md +1 -1
  47. package/dist/skill/report-templates/okki-weekly-google-client.md +4 -4
  48. package/dist/skill/scripts/install.ps1 +3 -3
  49. package/dist/skill/scripts/install.sh +3 -3
  50. package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +2 -2
  51. package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +3 -3
  52. package/eval/cases/accounts-single-balance-not-bulk.scenario.json +2 -2
  53. package/eval/cases/clue-meta-leads-json.scenario.json +2 -2
  54. package/eval/cases/clue-tiktok-leads-json.scenario.json +2 -2
  55. package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +1 -1
  56. package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +1 -1
  57. package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +1 -1
  58. package/eval/cases/finance-invoice-info-list.scenario.json +2 -2
  59. package/eval/cases/forewarning-list-google.scenario.json +2 -2
  60. package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +3 -3
  61. package/eval/cases/google-analysis-keywords-route.scenario.json +2 -2
  62. package/eval/cases/hosted-sop-cpa-spike-downbid.scenario.json +5 -5
  63. package/eval/cases/hosted-sop-daily-budget-circuit-breaker.scenario.json +3 -3
  64. package/eval/cases/hosted-sop-empty-spend-pause-p1.scenario.json +5 -5
  65. package/eval/cases/meta-single-balance-not-bulk.scenario.json +2 -2
  66. package/eval/cases/open-account-bing-noninteractive.scenario.json +3 -3
  67. package/eval/cases/open-account-google-noninteractive.scenario.json +2 -2
  68. package/eval/cases/open-account-tiktok-license-file.scenario.json +2 -2
  69. package/eval/cases/optimize-list-by-account.scenario.json +2 -2
  70. package/eval/cases/rag-before-keyword-expand.scenario.json +3 -3
  71. package/eval/cases/rag-list-then-query.scenario.json +2 -2
  72. package/eval/cases/report-list-google.scenario.json +2 -2
  73. package/eval/cases/report-push-list-google.scenario.json +3 -3
  74. package/eval/cases/reporting-vs-account-analytics-routing.scenario.json +4 -4
  75. package/eval/cases/setup-login-or-env.scenario.json +2 -2
  76. package/eval/cases/setup-siluzan-data-permission-env.scenario.json +2 -2
  77. package/eval/cases/skill-optimize-vs-google-ads-distinction.scenario.json +4 -4
  78. package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +2 -2
  79. package/eval/cases/tips-json-filtering.scenario.json +4 -4
  80. package/eval/cases/tips-large-json-pagination.scenario.json +3 -3
  81. package/eval/cases/uj-ad-bluetooth-keywords-exclude-cheap-free.scenario.json +1 -1
  82. package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +2 -2
  83. package/eval/cases/uj-ad-keywords-camping-tent-outdoor-plan.scenario.json +1 -1
  84. package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +3 -3
  85. package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +2 -2
  86. package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +1 -1
  87. package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +2 -2
  88. package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +3 -3
  89. package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +2 -2
  90. package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +2 -2
  91. package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +2 -2
  92. package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +2 -2
  93. package/eval/cases/uj-ops-open-google-b2c-usd-shenzhen.scenario.json +3 -3
  94. package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +2 -2
  95. package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +2 -2
  96. package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +3 -3
  97. package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +3 -3
  98. package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +2 -2
  99. package/eval/cases/uj-patrol-google-balances-low.scenario.json +2 -2
  100. package/eval/cases/uj-roi-full-google-account-diagnosis.scenario.json +1 -1
  101. package/eval/cases/uj-roi-keywords-high-cpa-low-cvr-triage.scenario.json +1 -1
  102. package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +2 -2
  103. package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +2 -2
  104. package/package.json +2 -2
  105. package/dist/skill/references/hosted-automation-scenarios.md +0 -23
  106. package/dist/skill/references/hosted-automation-user-catalog.md +0 -38
  107. /package/dist/skill/references/{write-audit-restore.md → accounts/write-audit-restore.md} +0 -0
  108. /package/dist/skill/references/{geo-continents.json → analytics/geo-continents.json} +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
@@ -43,7 +43,7 @@ HTML 报告模板引用以下 CDN:`cdn.tailwindcss.com`、`cdnjs.cloudflare.co
43
43
  在**用户的目标项目根目录**执行(根据用户使用的助手选择 `--ai`):
44
44
 
45
45
  ```bash
46
- npm install -g siluzan-tso-cli
46
+ npm install -g siluzan-tso-cli@beta
47
47
  siluzan-tso init --ai cursor # 写入 Cursor(默认)
48
48
  siluzan-tso init --ai cursor,claude # 同时写入多个平台
49
49
  siluzan-tso init --ai all # 写入所有支持的平台
@@ -51,6 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
+ > **注意**:当前为测试版(1.1.22-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
55
 
55
56
  | 助手 | 建议 `--ai` |
56
57
  | ----------------------- | ------------------------------------ |
@@ -68,7 +69,7 @@ siluzan-tso init --force # 强制覆盖已存在文件
68
69
 
69
70
  **若用户已安装 `siluzan-cso` 并完成登录,无需重复操作,直接跳到第 4 步。**
70
71
 
71
- **推荐顺序**:① **手机号 + 验证码**(首选)完整参数与排错见 `references/setup.md`(随 Skill 安装到本地)。
72
+ **推荐顺序**:① **手机号 + 验证码**(首选)完整参数与排错见 `references/core/setup.md`(随 Skill 安装到本地)。
72
73
 
73
74
  ### 方式 A:手机号 + 验证码(**推荐**)
74
75
 
package/dist/index.js CHANGED
@@ -3328,7 +3328,7 @@ var DEFAULT_API_BASE;
3328
3328
  var init_defaults = __esm({
3329
3329
  "src/config/defaults.ts"() {
3330
3330
  "use strict";
3331
- DEFAULT_API_BASE = "https://tso-api.siluzan.com";
3331
+ DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
3332
3332
  }
3333
3333
  });
3334
3334
 
@@ -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();
@@ -114340,28 +114431,14 @@ function buildPmaxCreateUrl(googleApiUrl, accountId) {
114340
114431
  return `${base}/accounts/${accountId}/campaign/pmax`;
114341
114432
  }
114342
114433
 
114343
- // src/commands/ad/pmax-feature-guard.ts
114344
- var ENV_KEY = "SILUZAN_PMAX_SETUP";
114345
- function isPmaxSetupEnabled() {
114346
- const v = process.env[ENV_KEY]?.trim();
114347
- return v === "1" || v?.toLowerCase() === "true";
114348
- }
114349
- function assertPmaxSetupEnabled(feature) {
114350
- if (isPmaxSetupEnabled()) return;
114351
- console.error(
114352
- `
114353
- \u274C PMax\u300C${feature}\u300D\u6682\u672A\u5728\u751F\u4EA7\u73AF\u5883\u5F00\u653E\uFF08\u642D\u5EFA\u80FD\u529B\u4ECD\u5728\u6D4B\u8BD5\uFF09\u3002
114354
- \u8BF7\u5728 Google Ads UI \u64CD\u4F5C\uFF0C\u6216\u672C\u5730\u8054\u8C03\u65F6\u8BBE\u7F6E ${ENV_KEY}=1 \u540E\u91CD\u8BD5\u3002
114355
- `
114356
- );
114357
- process.exit(1);
114358
- }
114359
-
114360
114434
  // src/commands/ad/pmax-create.ts
114361
114435
  async function runAdPmaxCreate(opts) {
114362
- assertPmaxSetupEnabled("pmax-create");
114363
114436
  const cfg = loadPmaxCreateConfig(opts.configFile);
114364
- const { errors: cfgErrors, warnings: cfgWarnings } = runPmaxCreateValidation(cfg);
114437
+ const {
114438
+ errors: cfgErrors,
114439
+ warnings: cfgWarnings,
114440
+ lengthViolations: cfgLengthViolations
114441
+ } = runPmaxCreateValidation(cfg);
114365
114442
  const { errors: imgErrors, warnings: imgWarnings } = await runPmaxImageValidation(
114366
114443
  opts.configFile,
114367
114444
  cfg
@@ -114375,6 +114452,10 @@ async function runAdPmaxCreate(opts) {
114375
114452
  if (errors.length > 0) {
114376
114453
  console.error("\n\u274C PMax \u914D\u7F6E\u6821\u9A8C\u5931\u8D25\uFF1A");
114377
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);
114378
114459
  console.error();
114379
114460
  process.exit(1);
114380
114461
  }
@@ -114449,15 +114530,19 @@ async function runAdPmaxCreate(opts) {
114449
114530
  import { writeFileSync as writeFileSync3 } from "fs";
114450
114531
  init_cli_json_snapshot();
114451
114532
  async function runAdPmaxValidate(opts) {
114452
- assertPmaxSetupEnabled("pmax-validate");
114453
114533
  const cfg = loadPmaxCreateConfig(opts.configFile);
114454
- const { errors: cfgErrors, warnings: cfgWarnings } = runPmaxCreateValidation(cfg);
114534
+ const {
114535
+ errors: cfgErrors,
114536
+ warnings: cfgWarnings,
114537
+ lengthViolations: cfgLengthViolations
114538
+ } = runPmaxCreateValidation(cfg);
114455
114539
  const { errors: imgErrors, warnings: imgWarnings } = await runPmaxImageValidation(
114456
114540
  opts.configFile,
114457
114541
  cfg
114458
114542
  );
114459
114543
  const errors = [...cfgErrors, ...imgErrors];
114460
114544
  const warnings = [...cfgWarnings, ...imgWarnings];
114545
+ const lengthViolations = cfgLengthViolations;
114461
114546
  if (opts.writeNormalized) {
114462
114547
  const toWrite = stripMetaKeysForExport(cfg);
114463
114548
  writeFileSync3(opts.writeNormalized, `${JSON.stringify(toWrite, null, 2)}
@@ -114468,7 +114553,9 @@ async function runAdPmaxValidate(opts) {
114468
114553
  configFile: opts.configFile,
114469
114554
  account: cfg.account?.toString().trim() || void 0,
114470
114555
  errors,
114471
- warnings
114556
+ warnings,
114557
+ lengthViolations,
114558
+ agentHint: lengthViolations.length > 0 ? lengthViolationAgentHint("pmax-validate") : void 0
114472
114559
  };
114473
114560
  const accountSuffix = payload.account;
114474
114561
  if (await emitCliJsonOrSnapshot(opts, {
@@ -114488,6 +114575,10 @@ async function runAdPmaxValidate(opts) {
114488
114575
  if (errors.length > 0) {
114489
114576
  console.error("\n\u274C PMax \u914D\u7F6E\u6821\u9A8C\u5931\u8D25\uFF1A");
114490
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);
114491
114582
  console.error();
114492
114583
  process.exit(1);
114493
114584
  }
@@ -114691,7 +114782,6 @@ async function runAdPmaxGet(opts) {
114691
114782
  });
114692
114783
  }
114693
114784
  async function runAdPmaxEdit(opts) {
114694
- assertPmaxSetupEnabled("pmax-edit");
114695
114785
  const accountId = requireAccountId(opts.account);
114696
114786
  const campaignId = opts.campaignId.trim();
114697
114787
  let body;
@@ -114753,7 +114843,6 @@ async function runAdPmaxEdit(opts) {
114753
114843
  });
114754
114844
  }
114755
114845
  async function runAdPmaxAssetGroupCreate(opts) {
114756
- assertPmaxSetupEnabled("pmax-asset-group-create");
114757
114846
  const cfg = loadPmaxJsonFile(opts.configFile);
114758
114847
  const accountId = requireAccountId(String(cfg["account"] ?? ""));
114759
114848
  const campaignId = String(cfg["campaignId"] ?? "").trim();
@@ -114839,7 +114928,6 @@ async function runAdPmaxAssetGroupCreate(opts) {
114839
114928
  });
114840
114929
  }
114841
114930
  async function runAdPmaxAssetGroupEdit(opts) {
114842
- assertPmaxSetupEnabled("pmax-asset-group-edit");
114843
114931
  const accountId = requireAccountId(opts.account);
114844
114932
  const assetGroupId = opts.assetGroupId.trim();
114845
114933
  let body;
@@ -114891,7 +114979,6 @@ async function runAdPmaxAssetGroupEdit(opts) {
114891
114979
  });
114892
114980
  }
114893
114981
  async function runAdPmaxAssetsUpdate(opts) {
114894
- assertPmaxSetupEnabled("pmax-assets-update");
114895
114982
  const cfg = loadPmaxJsonFile(opts.configFile);
114896
114983
  const accountId = requireAccountId(String(cfg["account"] ?? ""));
114897
114984
  const assetGroupId = String(cfg["assetGroupId"] ?? "").trim();
@@ -114952,7 +115039,6 @@ async function runAdPmaxAssetsUpdate(opts) {
114952
115039
  });
114953
115040
  }
114954
115041
  async function runAdPmaxYoutubeLink(opts) {
114955
- assertPmaxSetupEnabled("pmax-youtube-link");
114956
115042
  const accountId = requireAccountId(opts.account);
114957
115043
  const assetGroupId = opts.assetGroupId.trim();
114958
115044
  let body;
@@ -115042,7 +115128,6 @@ async function runAdPmaxSignalsGet(opts) {
115042
115128
  });
115043
115129
  }
115044
115130
  async function runAdPmaxSignalsSet(opts) {
115045
- assertPmaxSetupEnabled("pmax-signals-set");
115046
115131
  const cfg = loadPmaxJsonFile(opts.configFile);
115047
115132
  const accountId = requireAccountId(String(cfg["account"] ?? ""));
115048
115133
  const assetGroupId = String(cfg["assetGroupId"] ?? "").trim();
@@ -115162,7 +115247,6 @@ PMax \u53D7\u4F17\u6570\u636E\u6E90\uFF08\u8D26\u6237 ${accountId}\uFF09
115162
115247
  });
115163
115248
  }
115164
115249
  async function runAdPmaxImageUpload(opts) {
115165
- assertPmaxSetupEnabled("pmax-image-upload");
115166
115250
  const accountId = requireAccountId(opts.account);
115167
115251
  await withGoogleApi(opts, async (config, googleApiUrl) => {
115168
115252
  let data;
@@ -115309,7 +115393,6 @@ PMax \u5730\u7406\u62A5\u8868\uFF08\u6D3B\u52A8\xD7\u56FD\u5BB6\uFF0C${query.get
115309
115393
  // src/commands/ad/campaign-validate.ts
115310
115394
  import { writeFileSync as writeFileSync4 } from "fs";
115311
115395
  init_cli_json_snapshot();
115312
- 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";
115313
115396
  async function runAdCampaignValidate(opts) {
115314
115397
  const cfg = loadCampaignCreateConfig(opts.configFile);
115315
115398
  const { errors, warnings, lengthViolations } = runCampaignCreateValidation(cfg);
@@ -115325,7 +115408,7 @@ async function runAdCampaignValidate(opts) {
115325
115408
  errors,
115326
115409
  warnings,
115327
115410
  lengthViolations,
115328
- agentHint: lengthViolations.length > 0 ? LENGTH_VIOLATION_AGENT_HINT : void 0
115411
+ agentHint: lengthViolations.length > 0 ? lengthViolationAgentHint("campaign-validate") : void 0
115329
115412
  };
115330
115413
  const accountSuffix = payload.account || void 0;
115331
115414
  if (await emitCliJsonOrSnapshot(opts, {
@@ -115392,7 +115475,6 @@ async function convertOne(sharpFn, inputPath, outputPath, spec, quality) {
115392
115475
  await pipeline.toFile(outputPath);
115393
115476
  }
115394
115477
  async function runAdPmaxImageConvert(opts) {
115395
- assertPmaxSetupEnabled("pmax-image-convert");
115396
115478
  const { input, inputMarketing, inputSquare, inputLogo } = opts;
115397
115479
  if (!input && !inputMarketing && !inputSquare && !inputLogo) {
115398
115480
  console.error("\n\u274C \u8BF7\u81F3\u5C11\u6307\u5B9A --input \u6216 --input-marketing / --input-square / --input-logo\n");
@@ -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 |