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.
- package/README.md +3 -2
- package/dist/index.js +140 -58
- package/dist/skill/AGENTS.md +45 -0
- package/dist/skill/SKILL.md +60 -346
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/campaign-create-template.md +4 -4
- package/dist/skill/assets/pmax-create-template.md +4 -3
- package/dist/skill/docs/skill-guide.md +44 -0
- package/dist/skill/references/README.md +73 -0
- package/dist/skill/references/{accounts.md → accounts/accounts.md} +7 -7
- package/dist/skill/references/{currency.md → accounts/currency.md} +7 -7
- package/dist/skill/references/{finance.md → accounts/finance.md} +5 -5
- package/dist/skill/references/accounts/open-account-by-media.md +151 -0
- package/dist/skill/references/{open-account-google-ui.md → accounts/open-account-google-ui.md} +2 -2
- package/dist/skill/references/{account-analytics.md → analytics/account-analytics.md} +2 -2
- package/dist/skill/references/{google-analysis-batch.md → analytics/google-analysis-batch.md} +2 -2
- package/dist/skill/references/{keyword-planner-workflows.md → analytics/keyword-planner-workflows.md} +9 -9
- package/dist/skill/references/{rag.md → analytics/rag.md} +6 -6
- package/dist/skill/references/{reporting.md → analytics/reporting.md} +6 -6
- package/dist/skill/references/core/agent-conventions.md +180 -0
- package/dist/skill/references/core/playbooks.md +141 -0
- package/dist/skill/references/{setup.md → core/setup.md} +5 -5
- package/dist/skill/references/core/skill-authoring.md +192 -0
- package/dist/skill/references/{tips.md → core/tips.md} +5 -5
- package/dist/skill/references/{workflows.md → core/workflows.md} +23 -25
- package/dist/skill/references/{google-ads-campaign-plan.md → google-ads/google-ads-campaign-plan.md} +22 -22
- package/dist/skill/references/{google-ads.md → google-ads/google-ads.md} +11 -15
- package/dist/skill/references/{pmax-api.md → google-ads/pmax-api.md} +0 -2
- package/dist/skill/references/google-ads/rules/README.md +21 -0
- package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-keyword-taxonomy.md +2 -2
- package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-launch-plan-template.md +8 -8
- package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-pmax-guide.md +1 -1
- package/dist/skill/references/{tso-home.md → misc/tso-home.md} +5 -5
- package/dist/skill/references/{hosted-automation-monitoring-json.md → operations/hosted-automation-monitoring-json.md} +8 -8
- package/dist/skill/references/{hosted-automation-optimize-ab-winner.md → operations/hosted-automation-optimize-ab-winner.md} +2 -2
- package/dist/skill/references/{hosted-automation-optimize-index.md → operations/hosted-automation-optimize-index.md} +6 -6
- package/dist/skill/references/{hosted-automation-optimize-scale.md → operations/hosted-automation-optimize-scale.md} +2 -2
- package/dist/skill/references/{hosted-automation-optimize-weak-downbid.md → operations/hosted-automation-optimize-weak-downbid.md} +5 -5
- package/dist/skill/references/operations/hosted-automation-scenarios.md +23 -0
- package/dist/skill/references/{hosted-automation-self-control.md → operations/hosted-automation-self-control.md} +14 -14
- package/{assets/siluzan-ads/references → dist/skill/references/operations}/hosted-automation-user-catalog.md +12 -12
- package/dist/skill/report-templates/README.md +2 -1
- package/dist/skill/report-templates/REPORT-WORKFLOW.md +2 -2
- package/dist/skill/report-templates/google-ads-diagnosis.md +1 -1
- package/dist/skill/report-templates/google-inquiry-analysis.md +6 -6
- package/dist/skill/report-templates/google-period-report.md +1 -1
- package/dist/skill/report-templates/okki-weekly-google-client.md +4 -4
- package/dist/skill/scripts/install.ps1 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- package/eval/cases/accounts-entityid-vs-mediaccustomerid.scenario.json +2 -2
- package/eval/cases/accounts-mcc-bind-inquiry.scenario.json +3 -3
- package/eval/cases/accounts-single-balance-not-bulk.scenario.json +2 -2
- package/eval/cases/clue-meta-leads-json.scenario.json +2 -2
- package/eval/cases/clue-tiktok-leads-json.scenario.json +2 -2
- package/eval/cases/destructive-account-delink-needs-confirm.scenario.json +1 -1
- package/eval/cases/destructive-forewarning-delete-needs-confirm.scenario.json +1 -1
- package/eval/cases/destructive-invoice-apply-needs-confirm.scenario.json +1 -1
- package/eval/cases/finance-invoice-info-list.scenario.json +2 -2
- package/eval/cases/forewarning-list-google.scenario.json +2 -2
- package/eval/cases/google-ads-no-structural-without-confirm.scenario.json +3 -3
- package/eval/cases/google-analysis-keywords-route.scenario.json +2 -2
- package/eval/cases/hosted-sop-cpa-spike-downbid.scenario.json +5 -5
- package/eval/cases/hosted-sop-daily-budget-circuit-breaker.scenario.json +3 -3
- package/eval/cases/hosted-sop-empty-spend-pause-p1.scenario.json +5 -5
- package/eval/cases/meta-single-balance-not-bulk.scenario.json +2 -2
- package/eval/cases/open-account-bing-noninteractive.scenario.json +3 -3
- package/eval/cases/open-account-google-noninteractive.scenario.json +2 -2
- package/eval/cases/open-account-tiktok-license-file.scenario.json +2 -2
- package/eval/cases/optimize-list-by-account.scenario.json +2 -2
- package/eval/cases/rag-before-keyword-expand.scenario.json +3 -3
- package/eval/cases/rag-list-then-query.scenario.json +2 -2
- package/eval/cases/report-list-google.scenario.json +2 -2
- package/eval/cases/report-push-list-google.scenario.json +3 -3
- package/eval/cases/reporting-vs-account-analytics-routing.scenario.json +4 -4
- package/eval/cases/setup-login-or-env.scenario.json +2 -2
- package/eval/cases/setup-siluzan-data-permission-env.scenario.json +2 -2
- package/eval/cases/skill-optimize-vs-google-ads-distinction.scenario.json +4 -4
- package/eval/cases/tiktok-bc-bind-inquiry.scenario.json +2 -2
- package/eval/cases/tips-json-filtering.scenario.json +4 -4
- package/eval/cases/tips-large-json-pagination.scenario.json +3 -3
- package/eval/cases/uj-ad-bluetooth-keywords-exclude-cheap-free.scenario.json +1 -1
- package/eval/cases/uj-ad-campaign-validate-before-create-stub.scenario.json +2 -2
- package/eval/cases/uj-ad-keywords-camping-tent-outdoor-plan.scenario.json +1 -1
- package/eval/cases/uj-ad-outdoor-campgear-search-plan.scenario.json +3 -3
- package/eval/cases/uj-analytics-30d-pdf-campaign-device-geo.scenario.json +2 -2
- package/eval/cases/uj-analytics-compare-google-tiktok-last-month-roi.scenario.json +1 -1
- package/eval/cases/uj-analytics-google-weekly-trends-campaigns-keywords.scenario.json +2 -2
- package/eval/cases/uj-analytics-report-push-weekly-email.scenario.json +3 -3
- package/eval/cases/uj-finance-invoice-records-this-month.scenario.json +2 -2
- package/eval/cases/uj-life-newbie-siluzan-google-end-to-end.scenario.json +2 -2
- package/eval/cases/uj-ops-google-accounts-list-normal.scenario.json +2 -2
- package/eval/cases/uj-ops-google-yesterday-spend-conversions.scenario.json +2 -2
- package/eval/cases/uj-ops-open-google-b2c-usd-shenzhen.scenario.json +3 -3
- package/eval/cases/uj-ops-pause-worst-adgroup-confirm.scenario.json +2 -2
- package/eval/cases/uj-ops-tiktok-leads-last-week.scenario.json +2 -2
- package/eval/cases/uj-patrol-cpc-spike-adgroups-over-15.scenario.json +3 -3
- package/eval/cases/uj-patrol-forewarning-create-daily-cap-3000.scenario.json +3 -3
- package/eval/cases/uj-patrol-forewarning-trigger-records.scenario.json +2 -2
- package/eval/cases/uj-patrol-google-balances-low.scenario.json +2 -2
- package/eval/cases/uj-roi-full-google-account-diagnosis.scenario.json +1 -1
- package/eval/cases/uj-roi-keywords-high-cpa-low-cvr-triage.scenario.json +1 -1
- package/eval/cases/uj-roi-optimize-records-then-execute-cautiously.scenario.json +2 -2
- package/eval/cases/uj-roi-search-terms-add-negative-keywords.scenario.json +2 -2
- package/package.json +2 -2
- package/dist/skill/references/hosted-automation-scenarios.md +0 -23
- package/dist/skill/references/hosted-automation-user-catalog.md +0 -38
- /package/dist/skill/references/{write-audit-restore.md → accounts/write-audit-restore.md} +0 -0
- /package/dist/skill/references/{geo-continents.json → analytics/geo-continents.json} +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-account-audit.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-audience-strategy.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-campaign-optimization.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-compliance.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-conversion-architecture.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-creative-optimization.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-keyword-optimization.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-keyword-strategy.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/google-ads-landing-page-discovery-via-webfetch.md +0 -0
- /package/dist/skill/references/{google-ads-rules → google-ads/rules}/sensitive-industries.md +0 -0
- /package/dist/skill/references/{clue.md → operations/clue.md} +0 -0
- /package/dist/skill/references/{forewarning.md → operations/forewarning.md} +0 -0
- /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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
113792
|
-
|
|
113793
|
-
|
|
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
|
-
|
|
113897
|
-
|
|
113898
|
-
|
|
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
|
|
113933
|
+
const bnText = cfg.businessName.trim();
|
|
113934
|
+
const bn = calcGoogleCharLength(bnText);
|
|
113901
113935
|
if (bn > BUSINESS_NAME_MAX) {
|
|
113902
|
-
|
|
113903
|
-
|
|
113904
|
-
|
|
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
|
|
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 {
|
|
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 {
|
|
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 ?
|
|
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 |
|